From 4330e12c3e2d325915558613791c28549b748285 Mon Sep 17 00:00:00 2001 From: gaozheng Date: Thu, 25 Jun 2026 07:36:40 +0800 Subject: [PATCH] =?UTF-8?q?feat(vtk):=20=E9=80=90=E7=BA=BF=E4=B8=89?= =?UTF-8?q?=E7=BB=B4=E4=BD=93=E8=B0=83=E4=BA=AE=E8=B0=83=E6=B8=85=E6=99=B0?= =?UTF-8?q?(gallery=204=20=E7=BB=84=E5=AF=B9=E7=85=A7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit P3 默认(seismic+严格梯度门+低 ambient)整体偏暗、均匀层被门全透成空。 本次只调视觉(配色/不透明度/梯度门松弛/光照),不动建体/桥接/异步/LOD: - 新增调亮版 seismic、增强灰度两套配色 - GalleryVariant 加 gradGateRelax(梯度门松弛 0~1)与 ambient 字段 - makeVariantProperty 按松弛度抬低梯度地板+左移阈值,保留弱结构;ambient 由变体控 - 4 组对照重排:var1 暗版基线 / var2 提亮 seismic / var3 jet 高对比 / var4 增强灰度(默认) - gallery --out 让出图落在 store 目录(tmp/line001_proc),便于就地对照 - 端点仍按该体 2/98 分位自适应,非写死 实测(tmp/line001_proc):默认 var4 平均亮度 21.97→43.28、结构像素 3.9%→23.1%, 明显更亮更清晰;fps 约 51(交互级)。 --- tools/gpr_poc/main.cpp | 174 +++++++++++++++++++++++++++++------------ 1 file changed, 125 insertions(+), 49 deletions(-) diff --git a/tools/gpr_poc/main.cpp b/tools/gpr_poc/main.cpp index 61c8f43..c276dc6 100644 --- a/tools/gpr_poc/main.cpp +++ b/tools/gpr_poc/main.cpp @@ -1318,6 +1318,34 @@ geopro::core::ColorScale makeJetColorScale(double vmin, double vmax) { return cs; } +// 调亮版 seismic:与 makeSeismicColorScale 同红-白-蓝走向,但把蓝端提亮、整体抬白, +// 弱信号也落在更亮的色域(强负不再是深蓝、零附近纯白、强正亮红),整体更醒目不发暗。 +geopro::core::ColorScale makeBrightSeismicColorScale(double vmin, double vmax) { + geopro::core::ColorScale cs; + const double span = (vmax > vmin) ? (vmax - vmin) : 1.0; + auto at = [&](double t) { return vmin + span * t; }; + cs.addStop(at(0.00), geopro::core::Rgba{70, 130, 255, 255}); // 亮蓝(强负) + cs.addStop(at(0.30), geopro::core::Rgba{170, 210, 255, 255}); // 浅亮蓝 + cs.addStop(at(0.50), geopro::core::Rgba{255, 255, 255, 255}); // 纯白(零) + cs.addStop(at(0.70), geopro::core::Rgba{255, 200, 150, 255}); // 亮浅橙 + cs.addStop(at(1.00), geopro::core::Rgba{255, 80, 60, 255}); // 亮红(强正) + return cs; +} + +// 增强灰度:黑→白单调,但中段抬亮、两端拉满,弱反射也落在中亮灰,层界面/竖纹对比醒目。 +// GPR 内部水平层叠/基底反射用灰度往往最干净直读(不被多色相干扰)。 +geopro::core::ColorScale makeGrayEnhancedColorScale(double vmin, double vmax) { + geopro::core::ColorScale cs; + const double span = (vmax > vmin) ? (vmax - vmin) : 1.0; + auto at = [&](double t) { return vmin + span * t; }; + cs.addStop(at(0.00), geopro::core::Rgba{20, 25, 40, 255}); // 强负:近黑带冷调 + cs.addStop(at(0.30), geopro::core::Rgba{120, 125, 135, 255}); // 中负:中灰(抬亮) + cs.addStop(at(0.50), geopro::core::Rgba{180, 182, 188, 255}); // 零附近:亮灰 + cs.addStop(at(0.70), geopro::core::Rgba{225, 222, 210, 255}); // 中正:暖亮灰 + cs.addStop(at(1.00), geopro::core::Rgba{255, 252, 240, 255}); // 强正:近白 + return cs; +} + // 「实体感」不透明度包络(Task 12d gallery):与 structural 双端斜坡不同,这里让 // 中高值段普遍可见——背景(近零)仍压低但不归零,中高段从 floorOpacity 平滑升到 // maxOpacity,使体读起来像半透明实心块、内部层次(而非只剩两端薄壳)可见。 @@ -2916,7 +2944,13 @@ enum class OpacityProfile { kSolid, // V 形实体感:中高值段普遍可见,半透明实心块 kStructural, // 现有双端斜坡:仅正负两端不透明(对照基线) }; -enum class ColorChoice { kStructural, kSeismic, kJet }; +enum class ColorChoice { + kStructural, + kSeismic, + kJet, + kBrightSeismic, // 调亮版 seismic + kGrayEnhanced, // 增强灰度 +}; struct GalleryVariant { const char* name; // 文件名后缀:view-.png @@ -2934,42 +2968,55 @@ struct GalleryVariant { // ---- C4 视觉调优(梯度不透明度 + 光照),默认关 → 不改既有变体行为 ---- bool useGradientOpacity = false; // SetGradientOpacity:均匀层透明、界面/异常显形 bool useShade = false; // SetShade:层界面带立体明暗 + // ---- P4 调亮/调清晰 ---- + // 梯度门松弛度 0~1:0=严格(均匀层全透、偏暗) 1=宽松(均匀层保留底不透明、更亮更满)。 + // 宽松时降低梯度阈值并抬高「低梯度区」的不透明度地板,让横向层叠/基底反射等弱结构保留。 + double gradGateRelax = 0.0; + double ambient = 0.30; // 光照环境项(别太低,否则体面偏暗);useShade 时生效。 }; -// C4 视觉调优:4 组对照(纯标量 / +梯度不透明度 / +光照 / 全开)。 -// 同一局部段、同一 seismic 配色、同一斜穿俯视取景(El45/Az30,视线穿进体内部而非 -// 只看端面)——唯一变量是「梯度不透明度 / 光照」,供肉眼直读"内部结构是否透出"。 -// maxOpacity 字段:纯标量组用低峰值(0.45,避免均匀积分糊成实心);开了梯度门的组用 -// 高峰值(0.6)——均匀区被梯度门压透明后,层界面的净不透明度(标量×梯度)才够高、浮成实面。 -// 末项 var4(全开) = kViewDefaultVariant → 交互窗口默认走全开。 +// P4 调亮/调清晰:4 组对照(暗版基线 / 提亮 / 高对比 / 灰度增强)。 +// 同一局部段、同一斜穿取景(El45/Az30)。P3 默认(seismic+严格梯度门+低 ambient)整体 +// 偏暗、均匀层被门全透成空。本组在「消雾」与「够亮够满」间放宽门控、抬 ambient、换更亮 +// 配色,让横向层叠/竖纹/基底反射醒目。所有端点按该体 2/98 分位自适应(runGalleryVariant +// 内标定),非写死单一数据。末项 = kViewDefaultVariant → 交互窗口默认取最清晰醒目组。 +// +// 字段:floorOpacity/midOpacity/maxOpacity(V 形标量包络)、gradGateRelax(梯度门松弛 +// 0~1)、ambient(光照环境项)。提亮组抬高 floor/mid 让均匀层保留底不透明、抬 ambient +// 防体面发暗、放宽梯度门保留弱结构。 const GalleryVariant kGalleryVariants[] = { - // var1:纯标量不透明度(基线)——无梯度门、无光照。GPR 均匀层段沿射线积分糊成 - // 半透明实心块,只外表面/端面可读,内部水平分层难分辨(对照基准)。 + // var1:暗版基线(= P3 默认)——seismic + 严格梯度门(relax0) + 低 ambient0.3 + 暗背景。 + // 均匀层几乎全透、整体偏暗偏空,仅作对照。 {"var1", OpacityProfile::kSolid, ColorChoice::kSeismic, - 0.04, 0.30, 0.45, 8.0, 45.0, 30.0, 1.5, {0.05, 0.05, 0.09}, - "纯标量不透明度(基线):V形包络(floor0.04/mid0.30/max0.45)+seismic," - "无梯度门/无光照,均匀层积分糊成半透明块、仅端面可读", - /*useGradientOpacity=*/false, /*useShade=*/false}, - // var2:+梯度不透明度——低梯度(均匀层)透明、高梯度(层界面/异常)显形,射线穿透 - // 均匀雾、内部界面浮出。标量峰值提到 0.6(梯度门压透明后层面才够实)。 - {"var2", OpacityProfile::kSolid, ColorChoice::kSeismic, - 0.04, 0.30, 0.60, 8.0, 45.0, 30.0, 1.5, {0.05, 0.05, 0.09}, - "+梯度不透明度:低梯度(均匀层)透明、高梯度(界面/异常)显形,标量峰值0.6," - "射线穿透均匀雾、内部界面浮出", - /*useGradientOpacity=*/true, /*useShade=*/false}, - // var3:+光照——在梯度门基础上 ShadeOn,层界面带立体明暗(非纯平涂)。 - {"var3", OpacityProfile::kSolid, ColorChoice::kSeismic, - 0.04, 0.30, 0.60, 8.0, 45.0, 30.0, 1.5, {0.05, 0.05, 0.09}, - "+光照(梯度门+Shade):层界面带立体明暗(Ambient0.3/Diffuse0.7/Specular0.2)," - "界面起伏更有体积感", - /*useGradientOpacity=*/true, /*useShade=*/true}, - // var4:全开——梯度不透明度 + 光照 + seismic 双端斜坡配色 + 略亮冷灰背景。 - // 综合最佳,选作交互窗口默认(kViewDefaultVariant)。 - {"var4", OpacityProfile::kSolid, ColorChoice::kSeismic, 0.04, 0.30, 0.60, 8.0, 45.0, 30.0, 1.5, {0.07, 0.08, 0.11}, - "全开(综合最佳):梯度不透明度+光照+seismic+略亮冷灰背景,内部分层界面/异常" - "从均匀块透出 → 交互窗口默认", - /*useGradientOpacity=*/true, /*useShade=*/true}, + "暗版基线(P3默认):seismic+严格梯度门+低ambient+暗背景,均匀层近全透、偏暗偏空", + /*useGradientOpacity=*/true, /*useShade=*/true, + /*gradGateRelax=*/0.0, /*ambient=*/0.30}, + // var2:提亮——调亮版 seismic + 放宽梯度门(relax0.6) + 抬 floor/mid/max + 高 ambient + // + 略亮背景。均匀层保留底不透明、横向层叠透出、整体明显更亮更实。 + {"var2", OpacityProfile::kSolid, ColorChoice::kBrightSeismic, + 0.10, 0.45, 0.75, 8.0, 45.0, 30.0, 1.5, {0.12, 0.13, 0.17}, + "提亮:调亮版seismic+放宽梯度门(relax0.6)+抬不透明度(floor0.10/mid0.45/max0.75)" + "+高ambient0.5,均匀层保留、层叠透出、明显更亮", + /*useGradientOpacity=*/true, /*useShade=*/true, + /*gradGateRelax=*/0.6, /*ambient=*/0.50}, + // var3:高对比——jet 高饱和配色 + 放宽梯度门(relax0.5) + 抬不透明度 + ambient0.45 + + // 暗背景衬高饱和。弱信号也映到鲜明色相,层界面/异常对比最狠。 + {"var3", OpacityProfile::kSolid, ColorChoice::kJet, + 0.08, 0.42, 0.78, 8.0, 45.0, 30.0, 1.5, {0.04, 0.04, 0.07}, + "高对比:jet高饱和+放宽梯度门(relax0.5)+抬不透明度+ambient0.45+暗背景衬色," + "弱信号映鲜明色相、层界面对比最狠", + /*useGradientOpacity=*/true, /*useShade=*/true, + /*gradGateRelax=*/0.5, /*ambient=*/0.45}, + // var4:灰度增强(默认)——增强灰度 + 放宽梯度门(relax0.7) + 抬不透明度 + 高 ambient + // + 中性背景。GPR 内部水平层叠/竖纹/基底反射用增强灰度最干净直读,选作交互默认。 + {"var4", OpacityProfile::kSolid, ColorChoice::kGrayEnhanced, + 0.12, 0.48, 0.80, 8.0, 45.0, 30.0, 1.5, {0.14, 0.15, 0.18}, + "灰度增强(默认/综合最佳):增强灰度+放宽梯度门(relax0.7)+抬不透明度" + "(floor0.12/mid0.48/max0.80)+高ambient0.5+中性背景,层叠/竖纹/基底反射干净醒目" + " → 交互窗口默认", + /*useGradientOpacity=*/true, /*useShade=*/true, + /*gradGateRelax=*/0.7, /*ambient=*/0.50}, }; // 交互窗口(无 flag 的 view)的默认视觉变体 = var4(kGalleryVariants 末项)。 @@ -2981,6 +3028,10 @@ 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::kBrightSeismic: + return makeBrightSeismicColorScale(vmin, vmax); + case ColorChoice::kGrayEnhanced: + return makeGrayEnhancedColorScale(vmin, vmax); case ColorChoice::kStructural: default: return makeStructuralColorScale(vmin, vmax); } @@ -3005,22 +3056,34 @@ vtkSmartPointer makeVariantProperty( prop = makeTunedVolumeProperty(q, cs, vmin, vmax, maxOpacity); } - // 梯度不透明度:均匀层(低梯度)透明,层界面/异常边缘(高梯度)显形。阈值按实测分布: - // median 处仍 0(压住均匀积分雾),p90 升到 0.5,p99 到 0.9。无 gs(未测)则跳过。 + // 梯度不透明度:均匀层(低梯度)半透/透明,层界面/异常边缘(高梯度)显形。阈值按实测 + // 分布,并按 gradGateRelax 松弛: + // - relax=0(严格,暗版):median→0、p90→0.5、p99→0.9,均匀层全透 → 偏暗偏空。 + // - relax>0(调亮):低梯度地板抬到 floorG(均匀层保留底不透明、更亮更满),阈值 + // 整体左移(更早升起),让横向层叠/基底反射等弱结构保留可见。 + // 无 gs(未测)则跳过。 if (v.useGradientOpacity && gs != nullptr && gs->samples > 0) { + const double relax = std::clamp(v.gradGateRelax, 0.0, 1.0); + const double floorG = 0.30 * relax; // 低梯度区不透明度地板(均匀层保留度) + const double midG = 0.5 + 0.25 * relax; + const double hiG = 0.9; + // 阈值左移:松弛越大,门越早从低梯度升起(结构保留越多)。 + const double tLo = std::max(1.0, gs->median * (1.0 - 0.7 * relax)); + const double tMid = std::max(2.0, gs->p90 * (1.0 - 0.6 * relax)); + const double tHi = std::max(3.0, gs->p99 * (1.0 - 0.4 * relax)); vtkNew grad; - grad->AddPoint(0.0, 0.0); - grad->AddPoint(std::max(1.0, gs->median), 0.0); - grad->AddPoint(std::max(2.0, gs->p90), 0.5); - grad->AddPoint(std::max(3.0, gs->p99), 0.9); + grad->AddPoint(0.0, floorG); + grad->AddPoint(tLo, floorG); + grad->AddPoint(tMid, midG); + grad->AddPoint(tHi, hiG); prop->SetGradientOpacity(grad); } // 光照:ShadeOn + Ambient/Diffuse,保留立体明暗;Specular 压到 0.05(近乎关)避免 - // 旋转时视角相关的高光在体表游走形成「移动白斑」。保留 ambient/diffuse 立体感。 + // 旋转时视角相关的高光在体表游走形成「移动白斑」。Ambient 由变体控(别太低否则偏暗)。 if (v.useShade) { prop->ShadeOn(); - prop->SetAmbient(0.3); + prop->SetAmbient(v.ambient); prop->SetDiffuse(0.7); prop->SetSpecular(0.05); prop->SetSpecularPower(10.0); @@ -3298,8 +3361,9 @@ std::size_t viewSetupDefaultFrame(ViewState* st, vtkRenderer* ren) { } // 渲一组画廊变体并存 PNG,报告 结构像素 / 平均亮度 / fps。返回 0=OK。 +// shotDirOverride 非空 → PNG 存到该目录(P4 让 gallery 出图落在 store 同目录,便于对照)。 int runGalleryVariant(const std::string& dir, const GalleryVariant& v, - int frames) { + int frames, const std::string& shotDirOverride = "") { const int winW = 1280, winH = 800; geopro::data::ChunkedVolumeStore store(dir); const geopro::data::StoreMeta& m = store.meta(); @@ -3369,7 +3433,9 @@ int runGalleryVariant(const std::string& dir, const GalleryVariant& v, rw->Render(); const fs::path shotDir = - fs::path("docs") / "superpowers" / "plans" / "poc-lod-shots"; + shotDirOverride.empty() + ? fs::path("docs") / "superpowers" / "plans" / "poc-lod-shots" + : fs::path(shotDirOverride); fs::create_directories(shotDir); const std::string pngPath = (shotDir / (std::string("view-") + v.name + ".png")).string(); @@ -3430,9 +3496,12 @@ int runGalleryVariant(const std::string& dir, const GalleryVariant& v, return ok ? 0 : 1; } -// view --gallery:依次渲全部 4 组变体。 -int cmdViewGallery(const std::string& dir, int frames) { +// view --gallery:依次渲全部 4 组变体。shotDir 空 → 默认 docs/.../poc-lod-shots; +// 否则存到 shotDir(P4:默认传 store 目录,4 张图落在 tmp/line001_proc)。 +int cmdViewGallery(const std::string& dir, int frames, + const std::string& shotDir = "") { std::cout << "[view --gallery] storeDir=" << dir << " frames=" << frames + << " shotDir=" << (shotDir.empty() ? "(默认)" : shotDir) << "\n[view --gallery] 离屏闸门复检...\n"; if (cmdOffscreenSmoke() != 0) { std::cout << "[view --gallery] 闸门失败,中止。\n"; @@ -3440,10 +3509,12 @@ int cmdViewGallery(const std::string& dir, int frames) { } int rc = 0; for (const auto& v : kGalleryVariants) { - if (runGalleryVariant(dir, v, frames) != 0) rc = 1; + if (runGalleryVariant(dir, v, frames, shotDir) != 0) rc = 1; } - std::cout << "\n[view --gallery] 完成,4 张图存于 " - "docs/superpowers/plans/poc-lod-shots/view-var{1..4}.png\n"; + const std::string outDesc = + shotDir.empty() ? "docs/superpowers/plans/poc-lod-shots" : shotDir; + std::cout << "\n[view --gallery] 完成,4 张图存于 " << outDesc + << "/view-var{1..4}.png\n"; return rc; } @@ -3488,9 +3559,13 @@ int cmdView(int argc, char** argv) { const bool nearPreview = hasFlag("near") || (a.kv.count("variant") && a.get("variant", "") == "near"); + // 画廊出图目录:--out 显式指定;否则默认存到 store 目录(P4:gallery 落在 + // tmp/line001_proc,便于控制方就地对照)。 + const std::string galleryShotDir = a.get("out", dir); + // 画廊模式(Task 12d):渲 4 组视觉调参图供挑选。优先于其余路径。 if (hasFlag("gallery")) { - return cmdViewGallery(dir, frames); + return cmdViewGallery(dir, frames, galleryShotDir); } // 单变体:view --preview --variant N(N=1..4),只渲第 N 组(near 不走此路)。 if (preview && a.kv.count("variant") && !nearPreview) { @@ -3503,7 +3578,8 @@ int cmdView(int argc, char** argv) { } std::cout << "[view] storeDir=" << dir << " 单变体 variant=" << vi << "\n"; if (cmdOffscreenSmoke() != 0) return 1; - return runGalleryVariant(dir, kGalleryVariants[vi - 1], frames); + return runGalleryVariant(dir, kGalleryVariants[vi - 1], frames, + galleryShotDir); } std::cout << "[view] storeDir=" << dir << " exagg=" << exagg