diff --git a/docs/superpowers/plans/poc-lod-shots/view-default.png b/docs/superpowers/plans/poc-lod-shots/view-default.png index 3b97ae1..06d3ca6 100644 Binary files a/docs/superpowers/plans/poc-lod-shots/view-default.png and b/docs/superpowers/plans/poc-lod-shots/view-default.png differ diff --git a/tools/gpr_poc/main.cpp b/tools/gpr_poc/main.cpp index 7d63c0c..59aec7d 100644 --- a/tools/gpr_poc/main.cpp +++ b/tools/gpr_poc/main.cpp @@ -2292,6 +2292,90 @@ int cmdFpsBudget(int argc, char** argv) { return textureErr ? 1 : 0; } +// ============================================================================ +// 视觉调参画廊(Task 12d gallery):view --preview --variant N +// ============================================================================ +// +// 同一局部段(沿线中段 kViewDefaultLocalBricks 列全分辨率) + 同一相机框法 +// (ResetCamera→Elevation/Azimuth→Zoom),只换「不透明度包络 / 配色 / 取景角度 / +// 背景」四组视觉参数,各存一张 PNG 供控制方挑选。fps 对视觉调参近乎中性,每组实测验证。 +// +// 注:交互窗口(无 flag 的 view)默认即采用 var4(kViewDefaultVariant)——配色/不透明度 +// 包络/取景/exagg/背景全部走同一份 var4 参数,故「交互默认画面 == view-var4」。 +enum class OpacityProfile { + kSolid, // V 形实体感:中高值段普遍可见,半透明实心块 + kStructural, // 现有双端斜坡:仅正负两端不透明(对照基线) +}; +enum class ColorChoice { kStructural, kSeismic, kJet }; + +struct GalleryVariant { + const char* name; // 文件名后缀:view-.png + OpacityProfile profile; + ColorChoice color; + double floorOpacity; // 近零背景不透明度(kSolid 用) + double midOpacity; // 中值段不透明度(kSolid 用) + double maxOpacity; // 两端峰值不透明度 + double exagg; // 垂向夸张 + double elevation; // ResetCamera 后 Elevation + double azimuth; // 再 Azimuth + double zoom; // 再 Zoom 填满画面 + double bg[3]; // 背景 RGB + const char* desc; // 报告用中文说明 +}; + +// 4 组视觉参数。值经离屏实跑挑出(详见报告)。 +const GalleryVariant kGalleryVariants[] = { + // var1:高不透明度实体感——seismic 亮配色 + V 形包络(中段 0.40/两端 0.85), + // floor 压到 0.04:近零层间隙近透明,亮层面浮出 → 内部层状结构可读。 + {"var1", OpacityProfile::kSolid, ColorChoice::kSeismic, + 0.04, 0.40, 0.85, 8.0, 22.0, 28.0, 1.9, {0.05, 0.05, 0.09}, + "高不透明度实体感:V形包络(floor0.04/mid0.40/max0.85)+seismic 亮配色," + "半透明实心、内部层次可见"}, + // var2:高对比配色——jet 全程高饱和 + 中等不透明度 V 形包络。 + {"var2", OpacityProfile::kSolid, ColorChoice::kJet, + 0.04, 0.32, 0.70, 8.0, 22.0, 28.0, 1.9, {0.06, 0.06, 0.10}, + "高对比配色:jet 蓝青绿黄红全程高饱和 + 中等 V 形包络(mid0.32/max0.70)"}, + // var3:居中正对纵截面——低 Elevation/Azimuth 摆平、正对 X-Z 长侧面(层状反射沿 + // X 延展最清晰)、Zoom2.0 填满 ~70%;floor 压更低让层间隙透明、层面立体。 + {"var3", OpacityProfile::kSolid, ColorChoice::kSeismic, + 0.03, 0.38, 0.82, 9.0, 10.0, 12.0, 2.0, {0.05, 0.05, 0.09}, + "居中正对纵截面:低 El10/Az12 摆平正对 X-Z 长侧面、Zoom2.0 填满视野," + "floor0.03 凸显层面,exagg9 放大薄轴"}, + // var4:最像真实 GPR 三维图——seismic + 略提背景亮 + 微立体角 + 实体包络。 + // 综合最佳,选作交互窗口默认(kViewDefaultVariant)。 + {"var4", OpacityProfile::kSolid, ColorChoice::kSeismic, + 0.035, 0.38, 0.84, 8.0, 18.0, 22.0, 2.0, {0.07, 0.08, 0.11}, + "综合最佳:seismic + 实体包络(floor0.035/mid0.38/max0.84) + 微立体取景" + "(El18/Az22/Zoom2.0) + 略亮冷灰背景"}, +}; + +// 交互窗口(无 flag 的 view)的默认视觉变体 = var4(kGalleryVariants 末项)。 +// 交互默认与 view-var4 走同一份参数 → 二者画面一致(DRY,不复制粘贴漂移)。 +const GalleryVariant& kViewDefaultVariant = + kGalleryVariants[sizeof(kGalleryVariants) / sizeof(kGalleryVariants[0]) - 1]; + +geopro::core::ColorScale pickColor(ColorChoice c, double vmin, double vmax) { + switch (c) { + case ColorChoice::kSeismic: return makeSeismicColorScale(vmin, vmax); + case ColorChoice::kJet: return makeJetColorScale(vmin, vmax); + case ColorChoice::kStructural: + default: return makeStructuralColorScale(vmin, vmax); + } +} + +// 按变体的不透明度包络建体属性(gallery / 交互默认共用,DRY)。kSolid 走 V 形实体 +// 包络(floor/mid/max),kStructural 走双端斜坡(仅 maxOpacity)。 +vtkSmartPointer makeVariantProperty( + const GalleryVariant& v, const geopro::core::Quant& q, + const geopro::core::ColorScale& cs, double vmin, double vmax, + double maxOpacity) { + if (v.profile == OpacityProfile::kSolid) { + return makeSolidVolumeProperty(q, cs, vmin, vmax, v.floorOpacity, + v.midOpacity, maxOpacity); + } + return makeTunedVolumeProperty(q, cs, vmin, vmax, maxOpacity); +} + // ============================================================================ // ③ view:真窗口可交互(给用户肉眼测 + 最低配机跑)(Task 12d) // ============================================================================ @@ -2422,12 +2506,11 @@ void viewOnInteract(vtkObject*, unsigned long, void* clientData, void*) { // 越填不满画面;256 道是 ① cmdTune 出 lod-tuned-local.png(有清晰层状结构)的取景, // 沿用之以保证首帧同等可读。 constexpr int kViewDefaultLocalBricks = 4; -// ResetCamera 后再 Zoom 拉近填满画面(同 ① cmdTune 的 1.7×:默认框得偏松,留白多)。 -constexpr double kViewDefaultZoom = 1.7; // 建立 view 的「默认取景」:把 level0 一段局部体(沿线中段)整卷单块喂 mapper,再 // ResetCamera 到该局部段(actor 已 SetScale(1,exagg,exagg)),置相机为能看出层状 // 结构的角度。真窗口 / --smoke / --preview 三条路径共用此函数 → 渲的是同一画面。 +// 取景角度(Elevation/Azimuth/Zoom)取自 kViewDefaultVariant(var4),与 view-var4 一致。 // // 返回喂给 mapper 的块数(=1)。同步更新 st->lastLevel=0(默认即全分辨率局部段)。 std::size_t viewSetupDefaultFrame(ViewState* st, vtkRenderer* ren) { @@ -2452,75 +2535,13 @@ std::size_t viewSetupDefaultFrame(ViewState* st, vtkRenderer* ren) { // 相机角度沿用能看出结构的 Elevation/Azimuth,再 Zoom 拉近填满画面。 ren->ResetCamera(); st->cam = ren->GetActiveCamera(); - st->cam->Elevation(28.0); // 同 ① cmdTune 的取景角度 - st->cam->Azimuth(30.0); - st->cam->Zoom(kViewDefaultZoom); // 拉近填满画面,避免局部段缩在角落 + st->cam->Elevation(kViewDefaultVariant.elevation); // var4 取景:El18 + st->cam->Azimuth(kViewDefaultVariant.azimuth); // var4 取景:Az22 + st->cam->Zoom(kViewDefaultVariant.zoom); // var4 取景:Zoom2.0 填满画面 ren->ResetCameraClippingRange(); return one.size(); } -// ============================================================================ -// 视觉调参画廊(Task 12d gallery):view --preview --variant N -// ============================================================================ -// -// 同一局部段(沿线中段 kViewDefaultLocalBricks 列全分辨率) + 同一相机框法 -// (ResetCamera→Elevation/Azimuth→Zoom),只换「不透明度包络 / 配色 / 取景角度 / -// 背景」四组视觉参数,各存一张 PNG 供控制方挑选。fps 对视觉调参近乎中性,每组实测验证。 -enum class OpacityProfile { - kSolid, // V 形实体感:中高值段普遍可见,半透明实心块 - kStructural, // 现有双端斜坡:仅正负两端不透明(对照基线) -}; -enum class ColorChoice { kStructural, kSeismic, kJet }; - -struct GalleryVariant { - const char* name; // 文件名后缀:view-.png - OpacityProfile profile; - ColorChoice color; - double floorOpacity; // 近零背景不透明度(kSolid 用) - double midOpacity; // 中值段不透明度(kSolid 用) - double maxOpacity; // 两端峰值不透明度 - double exagg; // 垂向夸张 - double elevation; // ResetCamera 后 Elevation - double azimuth; // 再 Azimuth - double zoom; // 再 Zoom 填满画面 - double bg[3]; // 背景 RGB - const char* desc; // 报告用中文说明 -}; - -// 4 组视觉参数。值经离屏实跑挑出(详见报告)。 -const GalleryVariant kGalleryVariants[] = { - // var1:高不透明度实体感——seismic 亮配色 + V 形包络(中段 0.40/两端 0.85), - // floor 压到 0.04:近零层间隙近透明,亮层面浮出 → 内部层状结构可读。 - {"var1", OpacityProfile::kSolid, ColorChoice::kSeismic, - 0.04, 0.40, 0.85, 8.0, 22.0, 28.0, 1.9, {0.05, 0.05, 0.09}, - "高不透明度实体感:V形包络(floor0.04/mid0.40/max0.85)+seismic 亮配色," - "半透明实心、内部层次可见"}, - // var2:高对比配色——jet 全程高饱和 + 中等不透明度 V 形包络。 - {"var2", OpacityProfile::kSolid, ColorChoice::kJet, - 0.04, 0.32, 0.70, 8.0, 22.0, 28.0, 1.9, {0.06, 0.06, 0.10}, - "高对比配色:jet 蓝青绿黄红全程高饱和 + 中等 V 形包络(mid0.32/max0.70)"}, - // var3:居中正对纵截面——低 Elevation/Azimuth 摆平、正对 X-Z 长侧面(层状反射沿 - // X 延展最清晰)、Zoom2.0 填满 ~70%;floor 压更低让层间隙透明、层面立体。 - {"var3", OpacityProfile::kSolid, ColorChoice::kSeismic, - 0.03, 0.38, 0.82, 9.0, 10.0, 12.0, 2.0, {0.05, 0.05, 0.09}, - "居中正对纵截面:低 El10/Az12 摆平正对 X-Z 长侧面、Zoom2.0 填满视野," - "floor0.03 凸显层面,exagg9 放大薄轴"}, - // var4:最像真实 GPR 三维图——seismic + 略提背景亮 + 微立体角 + 实体包络。 - {"var4", OpacityProfile::kSolid, ColorChoice::kSeismic, - 0.035, 0.38, 0.84, 8.0, 18.0, 22.0, 2.0, {0.07, 0.08, 0.11}, - "综合最佳:seismic + 实体包络(floor0.035/mid0.38/max0.84) + 微立体取景" - "(El18/Az22/Zoom2.0) + 略亮冷灰背景"}, -}; - -geopro::core::ColorScale pickColor(ColorChoice c, double vmin, double vmax) { - switch (c) { - case ColorChoice::kSeismic: return makeSeismicColorScale(vmin, vmax); - case ColorChoice::kJet: return makeJetColorScale(vmin, vmax); - case ColorChoice::kStructural: - default: return makeStructuralColorScale(vmin, vmax); - } -} - // 渲一组画廊变体并存 PNG,报告 结构像素 / 平均亮度 / fps。返回 0=OK。 int runGalleryVariant(const std::string& dir, const GalleryVariant& v, int frames) { @@ -2530,13 +2551,8 @@ int runGalleryVariant(const std::string& dir, const GalleryVariant& v, const double vmin = m.vminPhys, vmax = m.vmaxPhys; const geopro::core::ColorScale cs = pickColor(v.color, vmin, vmax); - vtkSmartPointer prop; - if (v.profile == OpacityProfile::kSolid) { - prop = makeSolidVolumeProperty(m.quant, cs, vmin, vmax, v.floorOpacity, - v.midOpacity, v.maxOpacity); - } else { - prop = makeTunedVolumeProperty(m.quant, cs, vmin, vmax, v.maxOpacity); - } + vtkSmartPointer prop = + makeVariantProperty(v, m.quant, cs, vmin, vmax, v.maxOpacity); auto rw = makeOffscreenWindow(winW, winH); vtkNew ren; @@ -2661,8 +2677,15 @@ int cmdView(int argc, char** argv) { return 2; } const std::string dir = a.positional[0]; - const double exagg = std::stod(a.get("exagg", "8")); - const double opacity = std::stod(a.get("opacity", "0.5")); + // 交互/preview/smoke 默认视觉参数 = kViewDefaultVariant(var4):配色/不透明度包络/ + // exagg/背景全部走 var4,故默认画面 == view-var4(DRY,与画廊同源)。命令行 --exagg / + // --opacity 若用户显式传则覆盖 var4 对应值,否则用 var4 的 exagg / maxOpacity。 + const GalleryVariant& dv = kViewDefaultVariant; + const double exagg = + a.kv.count("exagg") ? std::stod(a.get("exagg", "8")) : dv.exagg; + const double opacity = a.kv.count("opacity") + ? std::stod(a.get("opacity", "0.5")) + : dv.maxOpacity; const std::size_t budget = static_cast(std::stoul(a.get("budget", "64"))); const int frames = std::stoi(a.get("frames", "90")); @@ -2709,9 +2732,10 @@ int cmdView(int argc, char** argv) { const auto& m = src.meta(); src.setAspect(static_cast(winW) / winH); const double vmin = m.vminPhys, vmax = m.vmaxPhys; - const geopro::core::ColorScale cs = makeStructuralColorScale(vmin, vmax); + // 配色/不透明度包络取自 var4:seismic + V 形实体包络(floor/mid + opacity 作峰值)。 + const geopro::core::ColorScale cs = pickColor(dv.color, vmin, vmax); vtkSmartPointer prop = - makeTunedVolumeProperty(m.quant, cs, vmin, vmax, opacity); + makeVariantProperty(dv, m.quant, cs, vmin, vmax, opacity); // 渲染窗口:preview/smoke 走离屏,否则真窗口。 vtkSmartPointer rw; @@ -2724,7 +2748,7 @@ int cmdView(int argc, char** argv) { } vtkNew ren; - ren->SetBackground(0.04, 0.04, 0.08); + ren->SetBackground(dv.bg[0], dv.bg[1], dv.bg[2]); // var4 略亮冷灰背景 rw->AddRenderer(ren); vtkNew mapper; @@ -2733,7 +2757,7 @@ int cmdView(int argc, char** argv) { auto volume = vtkSmartPointer::New(); volume->SetMapper(mapper); volume->SetProperty(prop); - volume->SetScale(1.0, exagg, exagg); // 垂向夸张(同 ①) + volume->SetScale(1.0, exagg, exagg); // 垂向夸张(默认 var4 exagg) ren->AddVolume(volume); // 屏幕左上角实时 fps 文本。