feat(poc): view 视觉调参画廊 4 组离屏 preview
view 加 --gallery / --preview --variant N:同一沿线中段局部段+同一相机框法,渲 4 组不同视觉参数(不透明度包络/配色/取景/背景)各存 PNG 供挑选。 新增 makeSeismicColorScale(红白蓝高对比)、makeJetColorScale(全程高饱和)、makeSolidVolumeProperty(V形实体感包络:近零背景压低但可见、中高值段普遍 0.3~0.85、半透明实心内部层次可读)、meanBrightness(画面均亮度度量)。 基线默认结构像素仅 0.07%(几乎全黑、缩角落);4 组拉到 17~30%,亮度显著提升,fps 76~86(≫30)。推荐 var4 作默认。
This commit is contained in:
parent
b1a8d1365d
commit
9af363080a
Binary file not shown.
|
After Width: | Height: | Size: 236 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 200 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 208 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 250 KiB |
|
|
@ -680,6 +680,79 @@ geopro::core::ColorScale makeStructuralColorScale(double vmin, double vmax) {
|
||||||
return cs;
|
return cs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 地震高对比色阶(seismic 红-白-蓝):两端饱和亮色(强正=亮红、强负=亮蓝),
|
||||||
|
// 零附近白。比 structural 更亮、对比更狠,正负反射一眼分开。
|
||||||
|
geopro::core::ColorScale makeSeismicColorScale(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{30, 60, 255, 255}); // 亮蓝(强负)
|
||||||
|
cs.addStop(at(0.30), geopro::core::Rgba{120, 180, 255, 255}); // 浅蓝
|
||||||
|
cs.addStop(at(0.50), geopro::core::Rgba{255, 255, 255, 255}); // 白(零)
|
||||||
|
cs.addStop(at(0.70), geopro::core::Rgba{255, 170, 120, 255}); // 浅橙
|
||||||
|
cs.addStop(at(1.00), geopro::core::Rgba{255, 40, 30, 255}); // 亮红(强正)
|
||||||
|
return cs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// jet 类高饱和色阶(蓝-青-绿-黄-红):全程高亮高饱和,最大化色彩动态范围,
|
||||||
|
// 弱信号也能映到鲜明色相,适合「一眼铺满层次」的取向。
|
||||||
|
geopro::core::ColorScale makeJetColorScale(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{0, 0, 200, 255}); // 蓝
|
||||||
|
cs.addStop(at(0.25), geopro::core::Rgba{0, 200, 255, 255}); // 青
|
||||||
|
cs.addStop(at(0.50), geopro::core::Rgba{0, 230, 60, 255}); // 绿
|
||||||
|
cs.addStop(at(0.75), geopro::core::Rgba{255, 230, 0, 255}); // 黄
|
||||||
|
cs.addStop(at(1.00), geopro::core::Rgba{255, 30, 0, 255}); // 红
|
||||||
|
return cs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 「实体感」不透明度包络(Task 12d gallery):与 structural 双端斜坡不同,这里让
|
||||||
|
// 中高值段普遍可见——背景(近零)仍压低但不归零,中高段从 floorOpacity 平滑升到
|
||||||
|
// maxOpacity,使体读起来像半透明实心块、内部层次(而非只剩两端薄壳)可见。
|
||||||
|
// floorOpacity:近零背景的最低不透明度(0.05~0.12,压住但不消失)
|
||||||
|
// maxOpacity :强反射端的不透明度峰值(0.85 时近实心)
|
||||||
|
// midOpacity :中值段(半幅处)的不透明度(0.3~0.5,决定「半透明实心」观感)
|
||||||
|
vtkSmartPointer<vtkVolumeProperty> makeSolidVolumeProperty(
|
||||||
|
const geopro::core::Quant& q, const geopro::core::ColorScale& cs,
|
||||||
|
double vminPhys, double vmaxPhys, double floorOpacity, double midOpacity,
|
||||||
|
double maxOpacity) {
|
||||||
|
constexpr int kTransferSamples = 64;
|
||||||
|
if (vminPhys >= vmaxPhys) vmaxPhys = vminPhys + 1.0;
|
||||||
|
const double qminD = static_cast<double>(q.toQ(vminPhys));
|
||||||
|
const double qmaxD = static_cast<double>(q.toQ(vmaxPhys));
|
||||||
|
|
||||||
|
vtkNew<vtkColorTransferFunction> color;
|
||||||
|
for (int t = 0; t < kTransferSamples; ++t) {
|
||||||
|
const double qd = qminD + (qmaxD - qminD) * t / (kTransferSamples - 1);
|
||||||
|
const auto qvLevel = static_cast<std::int16_t>(std::lround(qd));
|
||||||
|
const double phys = q.toPhys(qvLevel);
|
||||||
|
const auto c = cs.colorAt(phys);
|
||||||
|
color->AddRGBPoint(qd, c.r / 255.0, c.g / 255.0, c.b / 255.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 不透明度:V 形(中段=零附近背景=floor,正负两端=max),但全程 ≥floor 且中值
|
||||||
|
// 段≈mid → 整体半透明实心、内部层次可见,而非两端薄壳。
|
||||||
|
vtkNew<vtkPiecewiseFunction> opacity;
|
||||||
|
opacity->AddPoint(
|
||||||
|
static_cast<double>(geopro::core::ScalarVolumeI16::kBlank), 0.0);
|
||||||
|
const double qmid = 0.5 * (qminD + qmaxD);
|
||||||
|
const double half = 0.5 * (qmaxD - qminD);
|
||||||
|
opacity->AddPoint(qminD, maxOpacity); // 强负反射:近实心
|
||||||
|
opacity->AddPoint(qmid - 0.55 * half, midOpacity); // 中负段:半透明实心
|
||||||
|
opacity->AddPoint(qmid, floorOpacity); // 近零背景:压低但可见
|
||||||
|
opacity->AddPoint(qmid + 0.55 * half, midOpacity); // 中正段:半透明实心
|
||||||
|
opacity->AddPoint(qmaxD, maxOpacity); // 强正反射:近实心
|
||||||
|
|
||||||
|
auto prop = vtkSmartPointer<vtkVolumeProperty>::New();
|
||||||
|
prop->SetColor(color);
|
||||||
|
prop->SetScalarOpacity(opacity);
|
||||||
|
prop->SetInterpolationTypeToLinear();
|
||||||
|
prop->ShadeOff();
|
||||||
|
return prop;
|
||||||
|
}
|
||||||
|
|
||||||
// 参数化量化域传函:与 makeI16VolumeProperty 同逻辑,但 kMaxOpacity 可由 --opacity 控。
|
// 参数化量化域传函:与 makeI16VolumeProperty 同逻辑,但 kMaxOpacity 可由 --opacity 控。
|
||||||
// 不透明度调高时光线提前终止,fps 近乎中性甚至更快(探针认知,报告打印前后对照证实)。
|
// 不透明度调高时光线提前终止,fps 近乎中性甚至更快(探针认知,报告打印前后对照证实)。
|
||||||
vtkSmartPointer<vtkVolumeProperty> makeTunedVolumeProperty(
|
vtkSmartPointer<vtkVolumeProperty> makeTunedVolumeProperty(
|
||||||
|
|
@ -1560,6 +1633,23 @@ void savePng(vtkRenderWindow* rw, const std::string& path) {
|
||||||
writer->Write();
|
writer->Write();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 画面平均亮度(0~255):取前缓冲 RGB 求 luma 均值。Task 12d gallery 报告用,
|
||||||
|
// 量化「整体偏暗 vs 变亮」——背景占多数,故这是含背景的全屏均亮(横向对比有效)。
|
||||||
|
double meanBrightness(vtkRenderWindow* rw, int w, int h) {
|
||||||
|
auto px = vtkSmartPointer<vtkUnsignedCharArray>::New();
|
||||||
|
rw->GetRGBACharPixelData(0, 0, w - 1, h - 1, /*front=*/1, px);
|
||||||
|
const vtkIdType np = px->GetNumberOfTuples();
|
||||||
|
if (np == 0) return 0.0;
|
||||||
|
double sum = 0.0;
|
||||||
|
for (vtkIdType i = 0; i < np; ++i) {
|
||||||
|
const double r = px->GetComponent(i, 0);
|
||||||
|
const double g = px->GetComponent(i, 1);
|
||||||
|
const double b = px->GetComponent(i, 2);
|
||||||
|
sum += 0.299 * r + 0.587 * g + 0.114 * b;
|
||||||
|
}
|
||||||
|
return sum / static_cast<double>(np);
|
||||||
|
}
|
||||||
|
|
||||||
int cmdRenderLOD(int argc, char** argv) {
|
int cmdRenderLOD(int argc, char** argv) {
|
||||||
const Args a = parseArgs(argc, argv, 2);
|
const Args a = parseArgs(argc, argv, 2);
|
||||||
if (a.positional.empty()) {
|
if (a.positional.empty()) {
|
||||||
|
|
@ -2369,11 +2459,205 @@ std::size_t viewSetupDefaultFrame(ViewState* st, vtkRenderer* ren) {
|
||||||
return one.size();
|
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-<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。
|
||||||
|
int runGalleryVariant(const std::string& dir, const GalleryVariant& v,
|
||||||
|
int frames) {
|
||||||
|
const int winW = 1280, winH = 800;
|
||||||
|
geopro::data::ChunkedVolumeStore store(dir);
|
||||||
|
const geopro::data::StoreMeta& m = store.meta();
|
||||||
|
const double vmin = m.vminPhys, vmax = m.vmaxPhys;
|
||||||
|
const geopro::core::ColorScale cs = pickColor(v.color, vmin, vmax);
|
||||||
|
|
||||||
|
vtkSmartPointer<vtkVolumeProperty> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto rw = makeOffscreenWindow(winW, winH);
|
||||||
|
vtkNew<vtkRenderer> ren;
|
||||||
|
ren->SetBackground(v.bg[0], v.bg[1], v.bg[2]);
|
||||||
|
rw->AddRenderer(ren);
|
||||||
|
|
||||||
|
vtkNew<vtkMultiBlockVolumeMapper> mapper;
|
||||||
|
mapper->SetRequestedRenderMode(vtkSmartVolumeMapper::GPURenderMode);
|
||||||
|
auto volume = vtkSmartPointer<vtkVolume>::New();
|
||||||
|
volume->SetMapper(mapper);
|
||||||
|
volume->SetProperty(prop);
|
||||||
|
volume->SetScale(1.0, v.exagg, v.exagg);
|
||||||
|
ren->AddVolume(volume);
|
||||||
|
|
||||||
|
// 局部段(沿线中段,同 viewSetupDefaultFrame 的取段法)。
|
||||||
|
const int totBx = store.bricksX(0);
|
||||||
|
const int localBx = std::min(kViewDefaultLocalBricks, totBx);
|
||||||
|
const int bx0 = std::max(0, totBx / 2 - localBx / 2);
|
||||||
|
vtkSmartPointer<vtkImageData> locImg =
|
||||||
|
buildLocalLevel0Image(store, m, bx0, localBx);
|
||||||
|
std::vector<vtkSmartPointer<vtkImageData>> one{locImg};
|
||||||
|
auto mb = makeMultiBlock(one);
|
||||||
|
mapper->SetInputDataObject(mb);
|
||||||
|
mapper->Update();
|
||||||
|
|
||||||
|
ren->ResetCamera();
|
||||||
|
vtkCamera* cam = ren->GetActiveCamera();
|
||||||
|
cam->Elevation(v.elevation);
|
||||||
|
cam->Azimuth(v.azimuth);
|
||||||
|
cam->Zoom(v.zoom);
|
||||||
|
ren->ResetCameraClippingRange();
|
||||||
|
|
||||||
|
auto capWin = vtkSmartPointer<CapturingOutputWindow>::New();
|
||||||
|
vtkOutputWindow::SetInstance(capWin);
|
||||||
|
rw->Render();
|
||||||
|
|
||||||
|
const fs::path shotDir =
|
||||||
|
fs::path("docs") / "superpowers" / "plans" / "poc-lod-shots";
|
||||||
|
fs::create_directories(shotDir);
|
||||||
|
const std::string pngPath =
|
||||||
|
(shotDir / (std::string("view-") + v.name + ".png")).string();
|
||||||
|
savePng(rw.Get(), pngPath);
|
||||||
|
|
||||||
|
// 结构像素:任一通道 >50(排除暗背景),度量「确有体结构」。
|
||||||
|
auto countStructPixels = [&]() -> vtkIdType {
|
||||||
|
auto px = vtkSmartPointer<vtkUnsignedCharArray>::New();
|
||||||
|
rw->GetRGBACharPixelData(0, 0, winW - 1, winH - 1, /*front=*/1, px);
|
||||||
|
vtkIdType n = 0;
|
||||||
|
const vtkIdType np = px->GetNumberOfTuples();
|
||||||
|
for (vtkIdType i = 0; i < np; ++i) {
|
||||||
|
if (px->GetComponent(i, 0) > 50 || px->GetComponent(i, 1) > 50 ||
|
||||||
|
px->GetComponent(i, 2) > 50) {
|
||||||
|
++n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
};
|
||||||
|
const vtkIdType structPx = countStructPixels();
|
||||||
|
const double bright = meanBrightness(rw.Get(), winW, winH);
|
||||||
|
|
||||||
|
rw->Render(); // 预热再测 fps
|
||||||
|
Stopwatch sw;
|
||||||
|
for (int f = 0; f < frames; ++f) {
|
||||||
|
cam->Azimuth(360.0 / frames);
|
||||||
|
rw->Render();
|
||||||
|
}
|
||||||
|
const double ms = sw.elapsedMs();
|
||||||
|
const double fps = ms > 0 ? frames * 1000.0 / ms : 0.0;
|
||||||
|
const bool texErr = capWin->textureError();
|
||||||
|
vtkOutputWindow::SetInstance(nullptr);
|
||||||
|
const bool ok = !texErr && structPx > 0;
|
||||||
|
|
||||||
|
std::cout << "\n--- gallery " << v.name << " ---\n";
|
||||||
|
std::cout << "参数 : " << v.desc << "\n";
|
||||||
|
std::cout << "存图 : " << pngPath << "\n";
|
||||||
|
std::cout << "结构像素(>50) : " << structPx << " / " << (winW * winH)
|
||||||
|
<< " (" << (100.0 * structPx / (winW * winH)) << "%)\n";
|
||||||
|
std::cout << "平均亮度(0-255) : " << bright << "\n";
|
||||||
|
std::cout << "真实 fps : " << (ok ? std::to_string(fps) : "INVALID")
|
||||||
|
<< " (" << frames << " 帧旋相机)\n";
|
||||||
|
std::cout << "结果 : " << (ok ? "OK" : "FAIL") << "\n";
|
||||||
|
|
||||||
|
writeMetricLine(
|
||||||
|
"view-gallery," + std::string(v.name) + ",dir=" + dir +
|
||||||
|
",profile=" + (v.profile == OpacityProfile::kSolid ? "solid" : "struct") +
|
||||||
|
",floor=" + std::to_string(v.floorOpacity) +
|
||||||
|
",mid=" + std::to_string(v.midOpacity) +
|
||||||
|
",max=" + std::to_string(v.maxOpacity) +
|
||||||
|
",exagg=" + std::to_string(v.exagg) +
|
||||||
|
",el=" + std::to_string(v.elevation) +
|
||||||
|
",az=" + std::to_string(v.azimuth) + ",zoom=" + std::to_string(v.zoom) +
|
||||||
|
",structPx=" + std::to_string(structPx) +
|
||||||
|
",bright=" + std::to_string(bright) +
|
||||||
|
",fps=" + (ok ? std::to_string(fps) : "INVALID") +
|
||||||
|
",png=" + pngPath);
|
||||||
|
return ok ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// view --gallery:依次渲全部 4 组变体。
|
||||||
|
int cmdViewGallery(const std::string& dir, int frames) {
|
||||||
|
std::cout << "[view --gallery] storeDir=" << dir << " frames=" << frames
|
||||||
|
<< "\n[view --gallery] 离屏闸门复检...\n";
|
||||||
|
if (cmdOffscreenSmoke() != 0) {
|
||||||
|
std::cout << "[view --gallery] 闸门失败,中止。\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
int rc = 0;
|
||||||
|
for (const auto& v : kGalleryVariants) {
|
||||||
|
if (runGalleryVariant(dir, v, frames) != 0) rc = 1;
|
||||||
|
}
|
||||||
|
std::cout << "\n[view --gallery] 完成,4 张图存于 "
|
||||||
|
"docs/superpowers/plans/poc-lod-shots/view-var{1..4}.png\n";
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
int cmdView(int argc, char** argv) {
|
int cmdView(int argc, char** argv) {
|
||||||
const Args a = parseArgs(argc, argv, 2);
|
const Args a = parseArgs(argc, argv, 2);
|
||||||
if (a.positional.empty()) {
|
if (a.positional.empty()) {
|
||||||
std::cerr << "用法: gpr_poc view <storeDir> [--exagg 8] [--opacity 0.5] "
|
std::cerr << "用法: gpr_poc view <storeDir> [--exagg 8] [--opacity 0.5] "
|
||||||
"[--budget 64] [--smoke] [--preview] [--frames 90]\n";
|
"[--budget 64] [--smoke] [--preview] [--variant N] "
|
||||||
|
"[--gallery] [--frames 90]\n";
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
const std::string dir = a.positional[0];
|
const std::string dir = a.positional[0];
|
||||||
|
|
@ -2389,6 +2673,25 @@ int cmdView(int argc, char** argv) {
|
||||||
};
|
};
|
||||||
const bool smoke = hasFlag("smoke");
|
const bool smoke = hasFlag("smoke");
|
||||||
const bool preview = hasFlag("preview");
|
const bool preview = hasFlag("preview");
|
||||||
|
|
||||||
|
// 画廊模式(Task 12d):渲 4 组视觉调参图供挑选。优先于其余路径。
|
||||||
|
if (hasFlag("gallery")) {
|
||||||
|
return cmdViewGallery(dir, frames);
|
||||||
|
}
|
||||||
|
// 单变体:view --preview --variant N(N=1..4),只渲第 N 组。
|
||||||
|
if (preview && a.kv.count("variant")) {
|
||||||
|
const int vi = std::stoi(a.get("variant", "1"));
|
||||||
|
const int n = static_cast<int>(sizeof(kGalleryVariants) /
|
||||||
|
sizeof(kGalleryVariants[0]));
|
||||||
|
if (vi < 1 || vi > n) {
|
||||||
|
std::cerr << "[view] --variant 需在 1.." << n << " 之间\n";
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
std::cout << "[view] storeDir=" << dir << " 单变体 variant=" << vi << "\n";
|
||||||
|
if (cmdOffscreenSmoke() != 0) return 1;
|
||||||
|
return runGalleryVariant(dir, kGalleryVariants[vi - 1], frames);
|
||||||
|
}
|
||||||
|
|
||||||
std::cout << "[view] storeDir=" << dir << " exagg=" << exagg
|
std::cout << "[view] storeDir=" << dir << " exagg=" << exagg
|
||||||
<< " opacity=" << opacity << " budget=" << budget
|
<< " opacity=" << opacity << " budget=" << budget
|
||||||
<< (preview ? " [PREVIEW 离屏存图+测fps]"
|
<< (preview ? " [PREVIEW 离屏存图+测fps]"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue