geopro/docs/superpowers/plans/2026-06-07-m1-phase4-render.md

8.2 KiB
Raw Blame History

M1 Phase 4三维渲染扩展render 层 + 2D/3D + voxel + 散点/异常 + DEM实现计划

For agentic workers: REQUIRED SUB-SKILL: superpowers:subagent-driven-development。Steps 用 - [ ]

⚠️ 状态(2026-06-07,务必先读 2026-06-07-m1-view-redesign.md + STATUS.md):

  • Task 1(render 层 + 相机预设): 已完成。但中央"单场景平躺剖面 + 2D/3D 仅换相机"的做法已被 view-redesign 取代 —— 现在中央是 二维地图(测线线)/ 三维视图(竖直帘面)两种内容(详见 view-redesign 的"实施结果")。
  • Task 2(dd_voxel 体绘制+切片):🔶 VoxelActor 代码完成,但已从 UI 移除/搁置(散点 projX/Y 真实 CRS 未确认,无法与 lat/lon 帘面配准)。CRS 确认后再回归
  • Task 3(散点#17 + 异常叠加):🔶 散点#17 已完成(2026-06-08,ScatterActor 离屏 PNG 核对吻合 Python 真值 ref_17,接入数据详情「反演剖面/原数据」切换,+2 单测,全 31 测试绿;app 待人工登录复核)。异常叠加 未做(下一项)
  • Task 4(DEM/影像地形): 未做(P4 下次,需先确认 CRS + 加 gdal)。 渲染必须用 tests/spike/render_verify.cpp 离屏 PNG 核对(本会话教训)。

Goal: 把工作台中央视图从"内联单一网格渲染"升级为正式 render 层(Scene + actor 工厂 + 相机预设),并补齐 M1 三维内容:2D/3D 切换、dd_voxel 体绘制与切片、散点(#17)、异常叠加、DEM 地形。

Architecture: 新建 src/render/(依赖 VTK + core,不依赖 Qt 业务;由 app 的 QVTK widget 承载)。Scene 独占 vtkRenderer/vtkRenderWindow,actor 工厂按 core 模型产 actor;相机预设 Top2D(正交俯视)/Free3D(透视轨道);UI 加 2D/3D 切换。逐步把 main.cpp::renderGrid 迁入 render/actors/GridContourActor

Tech Stack: VTK 9.6 / core 模型 / Qt(仅 app 层 UI)。统一 Release,构建经 cmd /c "...\external\dev.bat <cmd>"(PowerShell 调)。app 构建前先 taskkill /IM geopro_desktop.exe /F

已就绪输入: geopro_dataLocalSampleRepository(loadGrid/loadScatter/loadColorScale/loadAnomalies);geopro_coreIdwInterpolator(→ScalarVolume)、ColorScaleGrid/ScatterField/Anomaly;Python 已验证 voxel(tools/validate_voxel.py:两交叉剖面 IDW 成体、15.9% 有约束)与 #17/#18(tools/validate_samples.py)。


Task 1render 层基础 + 2D/3D 相机切换

Files: src/render/CMakeLists.txtsrc/render/Scene.{hpp,cpp}src/render/ColorLutBuilder.{hpp,cpp}src/render/actors/GridContourActor.{hpp,cpp}src/render/CameraPreset.{hpp,cpp}src/CMakeLists.txtsrc/app/main.cpp(改用 render 层)、src/app/CMakeLists.txt(链 geopro_render)、tests/render/test_color_lut.cpp

  • Step 1: src/render/CMakeLists.txt:find_package(VTK REQUIRED COMPONENTS CommonCore CommonDataModel FiltersGeometry FiltersModeling RenderingOpenGL2 InteractionStyle);add_library(geopro_render STATIC ...)geopro_core ${VTK_LIBRARIES};vtk_module_autoinit(TARGETS geopro_render MODULES ${VTK_LIBRARIES});AUTOMOC OFFsrc/CMakeLists.txtadd_subdirectory(render)(net 之后、app 之前)。
  • Step 2: ColorLutBuilder:vtkSmartPointer<vtkLookupTable> build(const core::ColorScale&, double vmin, double vmax, int n=256) —— 用 cs.colorAt(val) 填 N 级 LUT。可单测:给已知 colorBar,断言某档颜色。(render 测试需 VTK,tests 已链 VTK 模块——按需在 tests/CMakeLists 补 VTK 组件。)单测 tests/render/test_color_lut.cpp:构造 ColorScale 两档,build LUT,lut->GetColor(val,rgb) 断言取下界色。
  • Step 3: GridContourActor:struct GridActors{vtkSmartPointer<vtkActor> bands, edges;};GridActors build(const core::Grid&, const core::ColorScale&)——把 main.cpp::renderGrid 的管线(vtkImageData→DataSetSurfaceFilter→BandedPolyDataContourFilter(GenerateContourEdges)→mapper+LUT)迁过来,返回 actors(不直接操作 renderer)。
  • Step 4: Scene:持有 vtkSmartPointer<vtkRenderer>;void clear()(RemoveAllViewProps)、void add(vtkActor*)vtkRenderer* renderer();CameraPreset:void applyTop2D(vtkRenderer*)(ParallelProjectionOn + 俯视 XY:相机沿 +Z 看 -Z,up=+Y,ResetCamera)、void applyFree3D(vtkRenderer*)(ParallelProjectionOff + 斜视,ResetCamera)。
  • Step 5: main.cpp 改:buildWorkbench 里渲染数据集时用 Scene::clear() + GridContourActor::build + Scene::add;中央工具条加两个按钮/QActionGroup「二维」「三维」,点击调 CameraPreset::applyTop2D/applyFree3D(scene.renderer()) + renderWindow->Render()。默认二维。
  • Step 6: 构建(先 taskkill)+ 冒烟运行;ctest -R ColorLut 过。人工验证:登录进工作台 → 点数据集出剖面 → 点"三维"可旋转、"二维"回正交俯视。
  • Step 7: 提交 feat(render): render 层(Scene/ColorLut/GridContourActor/相机预设) + 2D/3D 切换

Task 2dd_voxel 体绘制 + 交互切片(核心难点)

Files: src/render/actors/VoxelActor.{hpp,cpp}src/app/main.cpp(3D 模式可加载体素)、tests/render/test_voxel_build.cpp

  • VoxelActor::build(const core::ScalarVolume&, const core::ColorScale&)vtkSmartPointer<vtkVolume>:ScalarVolumevtkImageData(dims/spacing/origin,标量;NaN 留空→不透明度 0)→ vtkGPUVolumeRayCastMapper + vtkColorTransferFunction(由 ColorScale)+ vtkPiecewiseFunction(NaN/低值透明)。render 的 VTK 组件加 RenderingVolumeOpenGL2
  • 体素数据来源:main.cpprepo.loadScatter("grid1")(剖面1)+ 若有剖面2 一并;调 IdwInterpolator(GridSpec 由散点包络 + maxDist)→ ScalarVolume → VoxelActor。(M1 数据为两交叉剖面,体素为"十字片",已知;可信满体需更多线,记 §14。)
  • 切片:vtkImagePlaneWidgetvtkResliceCursorWidget,挂 interactor,3D 模式启用、2D 模式禁用;拖动出 dd_slice 截面。
  • 单测 test_voxel_build:小 PointSet → IdwInterpolator → VoxelActor::build 不崩、vtkImageData dims 正确。
  • 构建+冒烟;人工验证:3D 模式显示体素 + 可拖切片。提交 feat(render): dd_voxel 体绘制 + 交互切片

Task 3散点(#17) + 异常叠加

Files: src/render/actors/ScatterActor.{hpp,cpp}src/render/actors/AnomalyActor.{hpp,cpp}main.cpp

  • render::buildScatter(const core::ScatterField&, const core::ColorScale&, float pointSize)vtkActor:vtkPolyData(verts,点 x/-y/0,与 #18 同坐标系)+ 点标量 v + LUT(色阶范围优先 colorBar 真实分段值);方块点 SetPointSize。色阶用散点自带文件(loadScatterColorScale)。离屏 verify_scatter.png 核对吻合 ref_17。
  • AnomalyActor::build(const std::vector<core::Anomaly>&)vtkActor(或多 actor):按 markType——点(vtkGlyph3D)/线(polydata,dashed,lineColor/width)/面(vtkPolygon填充+边);坐标用 localPts。叠加在剖面上。
  • main.cpp:数据集详情可切换"网格/散点"显示;异常按需叠加。人工验证:散点图 ~#17;异常虚线叠在剖面上。提交 feat(render): 散点(#17) + 异常叠加

Task 4DEM 地形 + 影像(④)

Files: src/render/actors/TerrainActor.{hpp,cpp}vcpkg.json(加 gdal)、main.cpp

  • vcpkg.json"gdal"(重依赖,首次编译久)。TerrainActor::build(demPath, imagePath):GDAL 读 dem.tif(高程栅格)+ image.tif+image.tfw;影像 EPSG:3857、DEM/剖面坐标不同 → 用 core::CrsTransform 重投影到世界系(设计 §5/§M-1);vtkImageDatavtkWarpScalar(高程抬升)+ 影像作纹理贴面。
  • main.cpp:3D 模式可叠加地形面。人工验证:地形起伏 + 影像贴图(图 #05 样)。提交 feat(render): DEM 地形 + 影像叠加(GDAL+PROJ 重投影)

Self-Review 备注

  • 覆盖设计 §4(单一场景/相机预设/数据→actor 管线)、§K-5(① 散点/异常 + ② voxel/切片 + ④ DEM)、§10(IDW→ScalarVolume→VTK 在 render 转换)。
  • 铁律:render 依赖 VTK+core,不碰 Qt 业务;core 仍纯净。
  • voxel 可信度依赖输入(两交叉剖面→十字片,§14);DEM 影像异源 CRS 必重投影(§5)。
  • 颜色精确映射(colorBar 非均匀)在 ColorLutBuilder/VoxelActor 用 ColorScale.colorAt 落实(修正 spike S3 的偏蓝)。