8.7 KiB
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 体绘制+切片):🔶 体绘制+回归 ✅ 已完成(2026-06-08,CRS 已实证定 EPSG:4547)。
buildVoxelFromScatters(散点→4547→4326→GeoLocalFrame 配准 + IDW)→buildVoxel;接入 app 3D「体素」开关。离屏 PNG 两臂支撑吻合 ref voxel_hslice、profile1 片贴合帘面;+1 单测。dd_slice 交互切片 ⬜ 未做(image 已暴露供 reslice widget)。- Task 3(散点#17 + 异常叠加):✅ 已完成(2026-06-08)。散点#17=
ScatterActor(吻合 ref_17);异常叠加=AnomalyActor(点/线/面,叠在数据详情#18/#17,「显示异常」开关,吻合 ref_18)。数据详情切换按原型命名「原数据/网格数据」。+6 单测,全 35 测试绿;app 待人工登录复核。布局对齐原型其余项见 STATUS §6.10。- 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_data 的 LocalSampleRepository(loadGrid/loadScatter/loadColorScale/loadAnomalies);geopro_core 的 IdwInterpolator(→ScalarVolume)、ColorScale、Grid/ScatterField/Anomaly;Python 已验证 voxel(tools/validate_voxel.py:两交叉剖面 IDW 成体、15.9% 有约束)与 #17/#18(tools/validate_samples.py)。
Task 1:render 层基础 + 2D/3D 相机切换
Files: src/render/CMakeLists.txt、src/render/Scene.{hpp,cpp}、src/render/ColorLutBuilder.{hpp,cpp}、src/render/actors/GridContourActor.{hpp,cpp}、src/render/CameraPreset.{hpp,cpp}、src/CMakeLists.txt、src/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 OFF。src/CMakeLists.txt加add_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 2:dd_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>:ScalarVolume→vtkImageData(dims/spacing/origin,标量;NaN 留空→不透明度 0)→vtkGPUVolumeRayCastMapper+vtkColorTransferFunction(由 ColorScale)+vtkPiecewiseFunction(NaN/低值透明)。render 的 VTK 组件加RenderingVolumeOpenGL2。- 体素数据来源:
main.cpp用repo.loadScatter("grid1")(剖面1)+ 若有剖面2 一并;调IdwInterpolator(GridSpec 由散点包络 + maxDist)→ ScalarVolume → VoxelActor。(M1 数据为两交叉剖面,体素为"十字片",已知;可信满体需更多线,记 §14。) - 切片:
vtkImagePlaneWidget或vtkResliceCursorWidget,挂 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。render::buildAnomalies(const std::vector<core::Anomaly>&)→std::vector<vtkActor>(每异常一 actor,各自 lineColor/width/dashed):按 markType——点(vtkVertex+点大小)/线(开放 polyline,dashed stipple)/面(闭合 polyline 轮廓);坐标 (x,-y,0) 与 #18 同空间。叠加在数据详情剖面上(同纵向夸张)。dashed 在 OpenGL2 偏弱(几何/色/位正确)。- main.cpp:数据集详情可切换"网格/散点"显示;异常按需叠加。人工验证:散点图 ~#17;异常虚线叠在剖面上。提交
feat(render): 散点(#17) + 异常叠加。
Task 4:DEM 地形 + 影像(④)
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);vtkImageData→vtkWarpScalar(高程抬升)+ 影像作纹理贴面。- 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 的偏蓝)。