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

65 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# M1 Phase 4三维渲染扩展render 层 + 2D/3D + voxel + 散点/异常 + DEM实现计划
> **For agentic workers:** REQUIRED SUB-SKILL: superpowers:subagent-driven-development。Steps 用 `- [ ]`。
**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 1render 层基础 + 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 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>`:`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`
- [ ] `ScatterActor::build(const core::ScatterField&, const core::ColorScale&)`→`vtkActor`:`vtkPolyData`(verts,点用 x/y/(z))+ 标量 v + LUT;点大小适中。(#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);`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 的偏蓝)。