feat/vtk-3d-view #7

Merged
gaozheng merged 301 commits from feat/vtk-3d-view into main 2026-06-27 18:43:52 +08:00
2 changed files with 104 additions and 80 deletions
Showing only changes of commit f4922dd6e2 - Show all commits

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

View File

@ -2292,6 +2292,90 @@ int cmdFpsBudget(int argc, char** argv) {
return textureErr ? 1 : 0; return textureErr ? 1 : 0;
} }
// ============================================================================
// 视觉调参画廊Task 12d galleryview --preview --variant N
// ============================================================================
//
// 同一局部段(沿线中段 kViewDefaultLocalBricks 列全分辨率) + 同一相机框法
// (ResetCamera→Elevation/Azimuth→Zoom),只换「不透明度包络 / 配色 / 取景角度 /
// 背景」四组视觉参数,各存一张 PNG 供控制方挑选。fps 对视觉调参近乎中性,每组实测验证。
//
// 注:交互窗口(无 flag 的 view)默认即采用 var4kViewDefaultVariant——配色/不透明度
// 包络/取景/exagg/背景全部走同一份 var4 参数,故「交互默认画面 == view-var4」。
enum class OpacityProfile {
kSolid, // V 形实体感:中高值段普遍可见,半透明实心块
kStructural, // 现有双端斜坡:仅正负两端不透明(对照基线)
};
enum class ColorChoice { kStructural, kSeismic, kJet };
struct GalleryVariant {
const char* name; // 文件名后缀view-<name>.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)的默认视觉变体 = var4kGalleryVariants 末项)。
// 交互默认与 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<vtkVolumeProperty> 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 // ③ view真窗口可交互给用户肉眼测 + 最低配机跑Task 12d
// ============================================================================ // ============================================================================
@ -2422,12 +2506,11 @@ void viewOnInteract(vtkObject*, unsigned long, void* clientData, void*) {
// 越填不满画面256 道是 ① cmdTune 出 lod-tuned-local.png有清晰层状结构的取景 // 越填不满画面256 道是 ① cmdTune 出 lod-tuned-local.png有清晰层状结构的取景
// 沿用之以保证首帧同等可读。 // 沿用之以保证首帧同等可读。
constexpr int kViewDefaultLocalBricks = 4; constexpr int kViewDefaultLocalBricks = 4;
// ResetCamera 后再 Zoom 拉近填满画面(同 ① cmdTune 的 1.7×:默认框得偏松,留白多)。
constexpr double kViewDefaultZoom = 1.7;
// 建立 view 的「默认取景」:把 level0 一段局部体(沿线中段)整卷单块喂 mapper // 建立 view 的「默认取景」:把 level0 一段局部体(沿线中段)整卷单块喂 mapper
// ResetCamera 到该局部段(actor 已 SetScale(1,exagg,exagg)),置相机为能看出层状 // ResetCamera 到该局部段(actor 已 SetScale(1,exagg,exagg)),置相机为能看出层状
// 结构的角度。真窗口 / --smoke / --preview 三条路径共用此函数 → 渲的是同一画面。 // 结构的角度。真窗口 / --smoke / --preview 三条路径共用此函数 → 渲的是同一画面。
// 取景角度(Elevation/Azimuth/Zoom)取自 kViewDefaultVariant(var4),与 view-var4 一致。
// //
// 返回喂给 mapper 的块数(=1)。同步更新 st->lastLevel=0默认即全分辨率局部段 // 返回喂给 mapper 的块数(=1)。同步更新 st->lastLevel=0默认即全分辨率局部段
std::size_t viewSetupDefaultFrame(ViewState* st, vtkRenderer* ren) { std::size_t viewSetupDefaultFrame(ViewState* st, vtkRenderer* ren) {
@ -2452,75 +2535,13 @@ std::size_t viewSetupDefaultFrame(ViewState* st, vtkRenderer* ren) {
// 相机角度沿用能看出结构的 Elevation/Azimuth再 Zoom 拉近填满画面。 // 相机角度沿用能看出结构的 Elevation/Azimuth再 Zoom 拉近填满画面。
ren->ResetCamera(); ren->ResetCamera();
st->cam = ren->GetActiveCamera(); st->cam = ren->GetActiveCamera();
st->cam->Elevation(28.0); // 同 ① cmdTune 的取景角度 st->cam->Elevation(kViewDefaultVariant.elevation); // var4 取景El18
st->cam->Azimuth(30.0); st->cam->Azimuth(kViewDefaultVariant.azimuth); // var4 取景Az22
st->cam->Zoom(kViewDefaultZoom); // 拉近填满画面,避免局部段缩在角落 st->cam->Zoom(kViewDefaultVariant.zoom); // var4 取景Zoom2.0 填满画面
ren->ResetCameraClippingRange(); ren->ResetCameraClippingRange();
return one.size(); return one.size();
} }
// ============================================================================
// 视觉调参画廊Task 12d galleryview --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-<name>.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。 // 渲一组画廊变体并存 PNG报告 结构像素 / 平均亮度 / fps。返回 0=OK。
int runGalleryVariant(const std::string& dir, const GalleryVariant& v, int runGalleryVariant(const std::string& dir, const GalleryVariant& v,
int frames) { int frames) {
@ -2530,13 +2551,8 @@ int runGalleryVariant(const std::string& dir, const GalleryVariant& v,
const double vmin = m.vminPhys, vmax = m.vmaxPhys; const double vmin = m.vminPhys, vmax = m.vmaxPhys;
const geopro::core::ColorScale cs = pickColor(v.color, vmin, vmax); const geopro::core::ColorScale cs = pickColor(v.color, vmin, vmax);
vtkSmartPointer<vtkVolumeProperty> prop; vtkSmartPointer<vtkVolumeProperty> prop =
if (v.profile == OpacityProfile::kSolid) { makeVariantProperty(v, m.quant, cs, vmin, vmax, v.maxOpacity);
prop = makeSolidVolumeProperty(m.quant, cs, vmin, vmax, v.floorOpacity,
v.midOpacity, v.maxOpacity);
} else {
prop = makeTunedVolumeProperty(m.quant, cs, vmin, vmax, v.maxOpacity);
}
auto rw = makeOffscreenWindow(winW, winH); auto rw = makeOffscreenWindow(winW, winH);
vtkNew<vtkRenderer> ren; vtkNew<vtkRenderer> ren;
@ -2661,8 +2677,15 @@ int cmdView(int argc, char** argv) {
return 2; return 2;
} }
const std::string dir = a.positional[0]; const std::string dir = a.positional[0];
const double exagg = std::stod(a.get("exagg", "8")); // 交互/preview/smoke 默认视觉参数 = kViewDefaultVariant(var4):配色/不透明度包络/
const double opacity = std::stod(a.get("opacity", "0.5")); // exagg/背景全部走 var4故默认画面 == view-var4DRY与画廊同源。命令行 --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 = const std::size_t budget =
static_cast<std::size_t>(std::stoul(a.get("budget", "64"))); static_cast<std::size_t>(std::stoul(a.get("budget", "64")));
const int frames = std::stoi(a.get("frames", "90")); const int frames = std::stoi(a.get("frames", "90"));
@ -2709,9 +2732,10 @@ int cmdView(int argc, char** argv) {
const auto& m = src.meta(); const auto& m = src.meta();
src.setAspect(static_cast<double>(winW) / winH); src.setAspect(static_cast<double>(winW) / winH);
const double vmin = m.vminPhys, vmax = m.vmaxPhys; const double vmin = m.vminPhys, vmax = m.vmaxPhys;
const geopro::core::ColorScale cs = makeStructuralColorScale(vmin, vmax); // 配色/不透明度包络取自 var4seismic + V 形实体包络(floor/mid + opacity 作峰值)。
const geopro::core::ColorScale cs = pickColor(dv.color, vmin, vmax);
vtkSmartPointer<vtkVolumeProperty> prop = vtkSmartPointer<vtkVolumeProperty> prop =
makeTunedVolumeProperty(m.quant, cs, vmin, vmax, opacity); makeVariantProperty(dv, m.quant, cs, vmin, vmax, opacity);
// 渲染窗口preview/smoke 走离屏,否则真窗口。 // 渲染窗口preview/smoke 走离屏,否则真窗口。
vtkSmartPointer<vtkRenderWindow> rw; vtkSmartPointer<vtkRenderWindow> rw;
@ -2724,7 +2748,7 @@ int cmdView(int argc, char** argv) {
} }
vtkNew<vtkRenderer> ren; vtkNew<vtkRenderer> ren;
ren->SetBackground(0.04, 0.04, 0.08); ren->SetBackground(dv.bg[0], dv.bg[1], dv.bg[2]); // var4 略亮冷灰背景
rw->AddRenderer(ren); rw->AddRenderer(ren);
vtkNew<vtkMultiBlockVolumeMapper> mapper; vtkNew<vtkMultiBlockVolumeMapper> mapper;
@ -2733,7 +2757,7 @@ int cmdView(int argc, char** argv) {
auto volume = vtkSmartPointer<vtkVolume>::New(); auto volume = vtkSmartPointer<vtkVolume>::New();
volume->SetMapper(mapper); volume->SetMapper(mapper);
volume->SetProperty(prop); volume->SetProperty(prop);
volume->SetScale(1.0, exagg, exagg); // 垂向夸张(同 ① volume->SetScale(1.0, exagg, exagg); // 垂向夸张(默认 var4 exagg
ren->AddVolume(volume); ren->AddVolume(volume);
// 屏幕左上角实时 fps 文本。 // 屏幕左上角实时 fps 文本。