fix(gpr_poc): view 交互窗口统一走单纹理快路,去 MultiBlock 分块
交互 view 任何相机位置都只渲一张 vtkImageData + 单 vtkSmartVolumeMapper, 与 --preview 走完全同一条产单图 + 同一 mapper 的路径: - LOD 选层:远观/中景升到最细的整卷各轴<=16384 层(L2/L3)整卷一张纹理; 拉近取 level0 视野覆盖的 X 子区域重组一张纹理。两条都用 buildLevelImage / buildLocalLevel0Image 产单图。 - 去掉交互路径的 OutOfCoreSource/BrickPager/MultiBlock + budget 分块渲染 (renderC 等 POC 子命令仍保留)。 - LOD 随缩放真切换,只在 EndInteraction 重组一次。 - 新增 view --preview --near 拉近预览(view-near.png),与真窗口同路径。 - parseArgs 支持无值布尔旗标(--preview --near 不再误吞)。 修复:开窗+拉近无缺块(整张纹理),fps 从个位数升到几十~上百。
This commit is contained in:
parent
f4922dd6e2
commit
fb175d6d3d
Binary file not shown.
|
Before Width: | Height: | Size: 253 KiB After Width: | Height: | Size: 149 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 267 KiB |
|
|
@ -100,9 +100,13 @@ Args parseArgs(int argc, char** argv, int start) {
|
||||||
Args a;
|
Args a;
|
||||||
for (int i = start; i < argc; ++i) {
|
for (int i = start; i < argc; ++i) {
|
||||||
std::string tok = argv[i];
|
std::string tok = argv[i];
|
||||||
if (tok.rfind("--", 0) == 0 && i + 1 < argc) {
|
if (tok.rfind("--", 0) == 0) {
|
||||||
a.kv[tok.substr(2)] = argv[i + 1];
|
// 下一个 token 也是 --xxx(或本 token 是末尾)→ 本 token 是无值布尔旗标,
|
||||||
++i;
|
// 存空串(避免把后面的旗标误当成本旗标的值,如 --preview --near)。
|
||||||
|
const bool hasValue =
|
||||||
|
(i + 1 < argc) && std::string(argv[i + 1]).rfind("--", 0) != 0;
|
||||||
|
a.kv[tok.substr(2)] = hasValue ? argv[i + 1] : "";
|
||||||
|
if (hasValue) ++i;
|
||||||
} else {
|
} else {
|
||||||
a.positional.push_back(tok);
|
a.positional.push_back(tok);
|
||||||
}
|
}
|
||||||
|
|
@ -2390,11 +2394,24 @@ vtkSmartPointer<vtkVolumeProperty> makeVariantProperty(
|
||||||
// 整卷单张 3D 纹理的轴上限(同 renderLOD/renderB 实测 GL_MAX_3D_TEXTURE_SIZE)。
|
// 整卷单张 3D 纹理的轴上限(同 renderLOD/renderB 实测 GL_MAX_3D_TEXTURE_SIZE)。
|
||||||
constexpr int kViewMax3DTex = 16384;
|
constexpr int kViewMax3DTex = 16384;
|
||||||
|
|
||||||
// view 的每帧回调共享状态(挂到 interactor 的 EndInteraction/Timer/Render 上)。
|
// 单纹理统一渲染(Task 12d-singletex):
|
||||||
|
//
|
||||||
|
// 交互 view 不再用 vtkMultiBlockVolumeMapper + budget 分块(缺块、个位数 fps)。
|
||||||
|
// 任何相机位置都只渲【一张 vtkImageData + 单个 vtkSmartVolumeMapper】,与 --preview
|
||||||
|
// 走完全同一条产单图 + 同一 mapper 的路径,保证一致、高 fps(与预览 184fps 同档)。
|
||||||
|
//
|
||||||
|
// LOD 选层规则(拉近变细、拉远变粗):
|
||||||
|
// - 远观/中景(相机选中粗层)→ 升到最细的「整卷各轴 ≤16384」层(本数据 L2:11119、
|
||||||
|
// L3:5560),整卷重组成一张纹理,任何缩放都显示完整体,绝不缺块。
|
||||||
|
// - 拉近(相机选中 level0/1,X 超 16384 无法整卷成单纹理)→ 取当前视野在 level0 的
|
||||||
|
// X 子区域(沿线裁一段,使子体各轴 ≤16384)重组一张纹理。
|
||||||
|
// 两条都用现成 buildLevelImage / buildLocalLevel0Image 产单图 → 单 SmartVolumeMapper。
|
||||||
|
|
||||||
|
// view 的每帧回调共享状态(挂到 interactor 的 EndInteraction 上)。
|
||||||
struct ViewState {
|
struct ViewState {
|
||||||
geopro::render::OutOfCoreSource* src = nullptr;
|
geopro::data::ChunkedVolumeStore* store = nullptr;
|
||||||
geopro::data::ChunkedVolumeStore* store = nullptr; // 整卷粗层渲染走它
|
vtkSmartVolumeMapper* mapper = nullptr; // 单纹理:单 SmartVolumeMapper
|
||||||
vtkMultiBlockVolumeMapper* mapper = nullptr;
|
vtkRenderer* ren = nullptr;
|
||||||
vtkCamera* cam = nullptr;
|
vtkCamera* cam = nullptr;
|
||||||
vtkTextActor* fpsText = nullptr;
|
vtkTextActor* fpsText = nullptr;
|
||||||
vtkRenderWindow* rw = nullptr;
|
vtkRenderWindow* rw = nullptr;
|
||||||
|
|
@ -2403,6 +2420,10 @@ struct ViewState {
|
||||||
// 整卷粗层 image 缓存(按 level 缓存,避免每帧重组整卷)。
|
// 整卷粗层 image 缓存(按 level 缓存,避免每帧重组整卷)。
|
||||||
int cachedWholeLevel = -1;
|
int cachedWholeLevel = -1;
|
||||||
vtkSmartPointer<vtkImageData> cachedWholeImg;
|
vtkSmartPointer<vtkImageData> cachedWholeImg;
|
||||||
|
// 局部子区域 image 缓存(按 level0 brick 段缓存,仅在段变化时重组)。
|
||||||
|
int cachedLocalBx0 = -1;
|
||||||
|
int cachedLocalCount = -1;
|
||||||
|
vtkSmartPointer<vtkImageData> cachedLocalImg;
|
||||||
// 回调防重入:回调内部会 Render(),若 Render 又触发观察者回调会无限递归。
|
// 回调防重入:回调内部会 Render(),若 Render 又触发观察者回调会无限递归。
|
||||||
bool inCb = false;
|
bool inCb = false;
|
||||||
};
|
};
|
||||||
|
|
@ -2417,7 +2438,7 @@ bool levelFitsSingleTexture(const geopro::data::ChunkedVolumeStore& store,
|
||||||
|
|
||||||
// 给定相机选中的 level,返回真正用于整卷渲染的 level:从 picked 起向粗逐层找,
|
// 给定相机选中的 level,返回真正用于整卷渲染的 level:从 picked 起向粗逐层找,
|
||||||
// 取第一个整卷各轴 ≤16384 的层(如 level0/1 长线 X 超 16384,则升到 level2)。
|
// 取第一个整卷各轴 ≤16384 的层(如 level0/1 长线 X 超 16384,则升到 level2)。
|
||||||
// 找不到(极端情况)返回 -1,调用方退回分块路径。
|
// 找不到(极端情况)返回 -1,调用方退回局部子区域路径。
|
||||||
int wholeVolumeLevelFor(const geopro::data::ChunkedVolumeStore& store,
|
int wholeVolumeLevelFor(const geopro::data::ChunkedVolumeStore& store,
|
||||||
int picked) {
|
int picked) {
|
||||||
const int maxLevel = store.levels() - 1;
|
const int maxLevel = store.levels() - 1;
|
||||||
|
|
@ -2429,43 +2450,103 @@ int wholeVolumeLevelFor(const geopro::data::ChunkedVolumeStore& store,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 用 source 当前工作集刷新 mapper 输入。返回喂给 mapper 的块数。
|
// 由相机到体中心距离粗分档选 LOD level(移植 OutOfCoreSource::pickLevel,使交互
|
||||||
//
|
// view 不再依赖 OutOfCoreSource)。0=最细。cam==nullptr 或单层 → 0。
|
||||||
// 策略(同 12c renderLOD 已验,修概览空窗):
|
int viewPickLevel(const geopro::data::ChunkedVolumeStore& store, vtkCamera* cam) {
|
||||||
// - 概览/中远视角(相机选中粗层)→ 升到最细的“整卷各轴 ≤16384”层,整卷重组成
|
const geopro::data::StoreMeta& m = store.meta();
|
||||||
// 单张 image 单块喂 mapper(忽略 budget,粗层本就小),任何缩放都显示完整体,
|
const int maxLevel = store.levels() - 1;
|
||||||
// 不再是 budget 砍后 9% 稀疏。本数据 level0/1 的 X(44476/22238)>16384 → 升 level2。
|
if (cam == nullptr || maxLevel <= 0) return 0;
|
||||||
// - 只有拉近到要全分辨率(相机选中 level0,X=44476 无法成单纹理)→ 退回分块 +
|
const double dx = m.nx * m.spacing[0];
|
||||||
// budget 路径(核外 LRU 驻留,只渲视野内子区域)。
|
const double dy = m.ny * m.spacing[1];
|
||||||
std::size_t viewRefreshBlocks(ViewState* st) {
|
const double dz = m.nz * m.spacing[2];
|
||||||
// 先让 source 按相机选好 LOD(同时刷新 lastLevel/视野统计供 fps 文本用)。
|
const double diag = std::sqrt(dx * dx + dy * dy + dz * dz);
|
||||||
st->src->update(st->cam);
|
if (diag <= 0.0) return 0;
|
||||||
const int picked = st->src->lastLevel();
|
double pos[3];
|
||||||
|
cam->GetPosition(pos);
|
||||||
|
const double cx = m.origin[0] + 0.5 * dx;
|
||||||
|
const double cy = m.origin[1] + 0.5 * dy;
|
||||||
|
const double cz = m.origin[2] + 0.5 * dz;
|
||||||
|
const double ddx = pos[0] - cx, ddy = pos[1] - cy, ddz = pos[2] - cz;
|
||||||
|
const double dist = std::sqrt(ddx * ddx + ddy * ddy + ddz * ddz);
|
||||||
|
const double ratio = dist / diag;
|
||||||
|
int level = 0;
|
||||||
|
if (ratio >= 1.0) level = 1;
|
||||||
|
if (ratio >= 2.0) level = 2;
|
||||||
|
if (ratio >= 4.0) level = 3;
|
||||||
|
return std::min(level, maxLevel);
|
||||||
|
}
|
||||||
|
|
||||||
// 仅当相机选中 level0(最近、要全分辨率)才分块;其余(概览/中远)整卷渲染。
|
// 拉近时,level0 整卷 X(44476)>16384 无法成单纹理 → 只取相机视野覆盖的 X 段。
|
||||||
if (st->store != nullptr && picked > 0) {
|
// 用相机视锥在 X 轴上的世界投影范围交体范围,换算成 level0 的 brick 列区间,并夹到
|
||||||
const int wlv = wholeVolumeLevelFor(*st->store, picked);
|
// 「段宽 ≤16384 体素」(=256 brick 列,远大于任何视野所需)。返回 [bx0, count)。
|
||||||
if (wlv >= 0) {
|
void viewLocalBrickRange(const geopro::data::ChunkedVolumeStore& store,
|
||||||
// 整卷粗层:按 level 缓存整卷 image,仅在 level 变化时重组。
|
vtkCamera* cam, int& bx0, int& count) {
|
||||||
if (st->cachedWholeLevel != wlv || st->cachedWholeImg == nullptr) {
|
const geopro::data::StoreMeta& m = store.meta();
|
||||||
st->cachedWholeImg =
|
const int brick = m.brick;
|
||||||
buildLevelImage(*st->store, wlv, st->store->meta());
|
const int totBx = store.bricksX(0);
|
||||||
st->cachedWholeLevel = wlv;
|
const int maxBrickCols = kViewMax3DTex / brick; // 段宽上限(体素 ≤16384)
|
||||||
}
|
|
||||||
std::vector<vtkSmartPointer<vtkImageData>> one{st->cachedWholeImg};
|
// 默认:以体中段为中心取 kViewDefaultLocalBricks 列(无相机或退化时)。
|
||||||
auto mb = makeMultiBlock(one);
|
int centerBx = totBx / 2;
|
||||||
st->mapper->SetInputDataObject(mb);
|
int halfCols = std::max(2, maxBrickCols / 4); // 视野估不出时给个稳妥宽度
|
||||||
st->mapper->Update();
|
|
||||||
return one.size();
|
if (cam != nullptr) {
|
||||||
}
|
// 相机焦点 X 投影到 level0 brick 列;视野宽度由相机到焦点距离粗估。
|
||||||
|
double fp[3], pos[3];
|
||||||
|
cam->GetFocalPoint(fp);
|
||||||
|
cam->GetPosition(pos);
|
||||||
|
const double x0 = m.origin[0];
|
||||||
|
const double xspan = (m.nx > 1) ? (m.nx - 1) * m.spacing[0] : 1.0;
|
||||||
|
const double fx = (fp[0] - x0) / (xspan > 0 ? xspan : 1.0); // 0..1
|
||||||
|
centerBx = std::clamp(static_cast<int>(fx * totBx), 0, totBx - 1);
|
||||||
|
// 视野半宽(世界)≈ 视距 × tan(半视角),再换成 brick 列;夹到合理区间。
|
||||||
|
const double ddx = pos[0] - fp[0], ddy = pos[1] - fp[1],
|
||||||
|
ddz = pos[2] - fp[2];
|
||||||
|
const double viewDist = std::sqrt(ddx * ddx + ddy * ddy + ddz * ddz);
|
||||||
|
const double halfAngle = 0.5 * cam->GetViewAngle() * 3.14159265 / 180.0;
|
||||||
|
const double halfWorld = viewDist * std::tan(halfAngle);
|
||||||
|
const double colWorld = brick * m.spacing[0];
|
||||||
|
halfCols = std::clamp(static_cast<int>(halfWorld / colWorld) + 1, 2,
|
||||||
|
maxBrickCols / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 全分辨率长线:分块 + budget。
|
bx0 = std::clamp(centerBx - halfCols, 0, std::max(0, totBx - 1));
|
||||||
auto imgs = st->src->currentImages();
|
count = std::min(2 * halfCols, totBx - bx0);
|
||||||
auto mb = makeMultiBlock(imgs);
|
count = std::max(1, std::min(count, maxBrickCols));
|
||||||
st->mapper->SetInputDataObject(mb);
|
}
|
||||||
|
|
||||||
|
// 单纹理刷新:按相机选 LOD,产【一张 image】喂单 SmartVolumeMapper。返回喂入的块数
|
||||||
|
// (恒为 1,单纹理)。同步刷新 st->lastLevel(fps 文本用)。
|
||||||
|
std::size_t viewRefreshSingle(ViewState* st) {
|
||||||
|
const int picked = viewPickLevel(*st->store, st->cam);
|
||||||
|
|
||||||
|
// 概览/中远:升到最细的「整卷 ≤16384」层,整卷一张纹理(缓存,仅 level 变才重组)。
|
||||||
|
const int wlv = wholeVolumeLevelFor(*st->store, picked);
|
||||||
|
if (wlv >= 0 && picked >= 1) {
|
||||||
|
if (st->cachedWholeLevel != wlv || st->cachedWholeImg == nullptr) {
|
||||||
|
st->cachedWholeImg = buildLevelImage(*st->store, wlv, st->store->meta());
|
||||||
|
st->cachedWholeLevel = wlv;
|
||||||
|
}
|
||||||
|
st->mapper->SetInputData(st->cachedWholeImg);
|
||||||
|
st->mapper->Update();
|
||||||
|
st->lastLevel = wlv;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拉近(picked==0,要全分辨率):取视野覆盖的 level0 X 子段,一张纹理。
|
||||||
|
int bx0 = 0, cnt = 1;
|
||||||
|
viewLocalBrickRange(*st->store, st->cam, bx0, cnt);
|
||||||
|
if (st->cachedLocalBx0 != bx0 || st->cachedLocalCount != cnt ||
|
||||||
|
st->cachedLocalImg == nullptr) {
|
||||||
|
st->cachedLocalImg =
|
||||||
|
buildLocalLevel0Image(*st->store, st->store->meta(), bx0, cnt);
|
||||||
|
st->cachedLocalBx0 = bx0;
|
||||||
|
st->cachedLocalCount = cnt;
|
||||||
|
}
|
||||||
|
st->mapper->SetInputData(st->cachedLocalImg);
|
||||||
st->mapper->Update();
|
st->mapper->Update();
|
||||||
return imgs.size();
|
st->lastLevel = 0;
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// interactor 回调:每次交互(旋转/缩放)结束后重选 LOD + 刷新 fps 文本。
|
// interactor 回调:每次交互(旋转/缩放)结束后重选 LOD + 刷新 fps 文本。
|
||||||
|
|
@ -2479,8 +2560,10 @@ void viewOnInteract(vtkObject*, unsigned long, void* clientData, void*) {
|
||||||
// 将无限递归。已在回调中则直接返回(双保险)。
|
// 将无限递归。已在回调中则直接返回(双保险)。
|
||||||
if (st->inCb) return;
|
if (st->inCb) return;
|
||||||
st->inCb = true;
|
st->inCb = true;
|
||||||
const std::size_t blocks = viewRefreshBlocks(st);
|
// EndInteraction 时重选 LOD + 重组单图(仅松手触发一次,避免拖动中卡)。
|
||||||
const int lvl = st->src->lastLevel();
|
const std::size_t blocks = viewRefreshSingle(st);
|
||||||
|
st->ren->ResetCameraClippingRange();
|
||||||
|
const int lvl = st->lastLevel;
|
||||||
|
|
||||||
// 真实渲染帧率:连渲若干帧,只累计 Render() 本身耗时(不含空闲)。首帧含切换后
|
// 真实渲染帧率:连渲若干帧,只累计 Render() 本身耗时(不含空闲)。首帧含切换后
|
||||||
// 的纹理上传/shader 编译,故跑 kFpsProbeFrames 帧取均值更可信。
|
// 的纹理上传/shader 编译,故跑 kFpsProbeFrames 帧取均值更可信。
|
||||||
|
|
@ -2522,12 +2605,12 @@ std::size_t viewSetupDefaultFrame(ViewState* st, vtkRenderer* ren) {
|
||||||
vtkSmartPointer<vtkImageData> locImg =
|
vtkSmartPointer<vtkImageData> locImg =
|
||||||
buildLocalLevel0Image(store, m, bx0, localBx);
|
buildLocalLevel0Image(store, m, bx0, localBx);
|
||||||
|
|
||||||
std::vector<vtkSmartPointer<vtkImageData>> one{locImg};
|
// 单纹理:一张 image 直接喂单 SmartVolumeMapper(与 --preview 同路径)。
|
||||||
auto mb = makeMultiBlock(one);
|
st->mapper->SetInputData(locImg);
|
||||||
st->mapper->SetInputDataObject(mb);
|
|
||||||
st->mapper->Update();
|
st->mapper->Update();
|
||||||
st->cachedWholeImg = locImg; // 持有引用,避免被释放
|
st->cachedLocalImg = locImg; // 持有引用并作缓存键,避免被释放/重组
|
||||||
st->cachedWholeLevel = 0;
|
st->cachedLocalBx0 = bx0;
|
||||||
|
st->cachedLocalCount = localBx;
|
||||||
st->lastLevel = 0;
|
st->lastLevel = 0;
|
||||||
|
|
||||||
// 框住局部段:用无参 ResetCamera(按 actor 的【已 SetScale(1,exagg,exagg)】缩放
|
// 框住局部段:用无参 ResetCamera(按 actor 的【已 SetScale(1,exagg,exagg)】缩放
|
||||||
|
|
@ -2539,7 +2622,7 @@ std::size_t viewSetupDefaultFrame(ViewState* st, vtkRenderer* ren) {
|
||||||
st->cam->Azimuth(kViewDefaultVariant.azimuth); // var4 取景:Az22
|
st->cam->Azimuth(kViewDefaultVariant.azimuth); // var4 取景:Az22
|
||||||
st->cam->Zoom(kViewDefaultVariant.zoom); // var4 取景:Zoom2.0 填满画面
|
st->cam->Zoom(kViewDefaultVariant.zoom); // var4 取景:Zoom2.0 填满画面
|
||||||
ren->ResetCameraClippingRange();
|
ren->ResetCameraClippingRange();
|
||||||
return one.size();
|
return 1; // 单纹理:恒一张 image
|
||||||
}
|
}
|
||||||
|
|
||||||
// 渲一组画廊变体并存 PNG,报告 结构像素 / 平均亮度 / fps。返回 0=OK。
|
// 渲一组画廊变体并存 PNG,报告 结构像素 / 平均亮度 / fps。返回 0=OK。
|
||||||
|
|
@ -2696,13 +2779,18 @@ 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-singletex):--preview --variant near(或 --near)走与真窗口
|
||||||
|
// 完全相同的单纹理拉近路径(viewRefreshSingle 选 level0 局部子区域),供控制方 Read
|
||||||
|
// 验证「拉近后」单图非空、完整、fps 高。
|
||||||
|
const bool nearPreview =
|
||||||
|
hasFlag("near") || (a.kv.count("variant") && a.get("variant", "") == "near");
|
||||||
|
|
||||||
// 画廊模式(Task 12d):渲 4 组视觉调参图供挑选。优先于其余路径。
|
// 画廊模式(Task 12d):渲 4 组视觉调参图供挑选。优先于其余路径。
|
||||||
if (hasFlag("gallery")) {
|
if (hasFlag("gallery")) {
|
||||||
return cmdViewGallery(dir, frames);
|
return cmdViewGallery(dir, frames);
|
||||||
}
|
}
|
||||||
// 单变体:view --preview --variant N(N=1..4),只渲第 N 组。
|
// 单变体:view --preview --variant N(N=1..4),只渲第 N 组(near 不走此路)。
|
||||||
if (preview && a.kv.count("variant")) {
|
if (preview && a.kv.count("variant") && !nearPreview) {
|
||||||
const int vi = std::stoi(a.get("variant", "1"));
|
const int vi = std::stoi(a.get("variant", "1"));
|
||||||
const int n = static_cast<int>(sizeof(kGalleryVariants) /
|
const int n = static_cast<int>(sizeof(kGalleryVariants) /
|
||||||
sizeof(kGalleryVariants[0]));
|
sizeof(kGalleryVariants[0]));
|
||||||
|
|
@ -2725,12 +2813,11 @@ int cmdView(int argc, char** argv) {
|
||||||
const bool offscreen = smoke || preview;
|
const bool offscreen = smoke || preview;
|
||||||
const int winW = 1280, winH = 800;
|
const int winW = 1280, winH = 800;
|
||||||
|
|
||||||
// 核外源(读 meta + 建 pager,不载整卷)。
|
// 单纹理统一路径(Task 12d-singletex):交互 view 只用 ChunkedVolumeStore 产
|
||||||
geopro::render::OutOfCoreSource src(dir, budget);
|
// 单图 + 单 SmartVolumeMapper,不再用 OutOfCoreSource/BrickPager/MultiBlock 分块。
|
||||||
// 整卷粗层渲染另开 store(粗层各轴 ≤16384 时整卷单 mapper 渲,绕过 budget 稀疏)。
|
(void)budget; // 交互 view 不再用 budget 分块(保留参数以兼容旧命令行)。
|
||||||
geopro::data::ChunkedVolumeStore store(dir);
|
geopro::data::ChunkedVolumeStore store(dir);
|
||||||
const auto& m = src.meta();
|
const auto& m = store.meta();
|
||||||
src.setAspect(static_cast<double>(winW) / winH);
|
|
||||||
const double vmin = m.vminPhys, vmax = m.vmaxPhys;
|
const double vmin = m.vminPhys, vmax = m.vmaxPhys;
|
||||||
// 配色/不透明度包络取自 var4:seismic + V 形实体包络(floor/mid + opacity 作峰值)。
|
// 配色/不透明度包络取自 var4:seismic + V 形实体包络(floor/mid + opacity 作峰值)。
|
||||||
const geopro::core::ColorScale cs = pickColor(dv.color, vmin, vmax);
|
const geopro::core::ColorScale cs = pickColor(dv.color, vmin, vmax);
|
||||||
|
|
@ -2751,8 +2838,12 @@ int cmdView(int argc, char** argv) {
|
||||||
ren->SetBackground(dv.bg[0], dv.bg[1], dv.bg[2]); // var4 略亮冷灰背景
|
ren->SetBackground(dv.bg[0], dv.bg[1], dv.bg[2]); // var4 略亮冷灰背景
|
||||||
rw->AddRenderer(ren);
|
rw->AddRenderer(ren);
|
||||||
|
|
||||||
vtkNew<vtkMultiBlockVolumeMapper> mapper;
|
// 单纹理:单 vtkSmartVolumeMapper(GPU 光线投射,整张 3D 纹理),与 --preview /
|
||||||
|
// gallery 同一 mapper 类型,保证交互画面 == 预览画面、fps 同档。
|
||||||
|
vtkNew<vtkSmartVolumeMapper> mapper;
|
||||||
mapper->SetRequestedRenderMode(vtkSmartVolumeMapper::GPURenderMode);
|
mapper->SetRequestedRenderMode(vtkSmartVolumeMapper::GPURenderMode);
|
||||||
|
mapper->SetAutoAdjustSampleDistances(0);
|
||||||
|
mapper->SetInteractiveAdjustSampleDistances(0);
|
||||||
|
|
||||||
auto volume = vtkSmartPointer<vtkVolume>::New();
|
auto volume = vtkSmartPointer<vtkVolume>::New();
|
||||||
volume->SetMapper(mapper);
|
volume->SetMapper(mapper);
|
||||||
|
|
@ -2773,9 +2864,9 @@ int cmdView(int argc, char** argv) {
|
||||||
vtkOutputWindow::SetInstance(capWin);
|
vtkOutputWindow::SetInstance(capWin);
|
||||||
|
|
||||||
ViewState st;
|
ViewState st;
|
||||||
st.src = &src;
|
|
||||||
st.store = &store;
|
st.store = &store;
|
||||||
st.mapper = mapper.Get();
|
st.mapper = mapper.Get();
|
||||||
|
st.ren = ren.Get();
|
||||||
st.fpsText = fpsText.Get();
|
st.fpsText = fpsText.Get();
|
||||||
st.rw = rw.Get();
|
st.rw = rw.Get();
|
||||||
st.exagg = exagg;
|
st.exagg = exagg;
|
||||||
|
|
@ -2783,11 +2874,20 @@ int cmdView(int argc, char** argv) {
|
||||||
// 相机初始定向(修复 1):默认框「局部段」而非整卷。整线横截面 1:34,框整卷
|
// 相机初始定向(修复 1):默认框「局部段」而非整卷。整线横截面 1:34,框整卷
|
||||||
// 即便 exagg=8 也是一条隐形细带(看着空白);改为对准沿线中段一个 ~768 道窗口
|
// 即便 exagg=8 也是一条隐形细带(看着空白);改为对准沿线中段一个 ~768 道窗口
|
||||||
// 的全分辨率局部体 → 开窗第一帧就看到一段有层状结构的体。三路径共用此取景。
|
// 的全分辨率局部体 → 开窗第一帧就看到一段有层状结构的体。三路径共用此取景。
|
||||||
const std::size_t warm = viewSetupDefaultFrame(&st, ren);
|
std::size_t warm = viewSetupDefaultFrame(&st, ren);
|
||||||
rw->Render();
|
rw->Render();
|
||||||
|
|
||||||
std::cout << "[view] 预热(默认局部段): level=" << st.lastLevel
|
// 拉近预览:在默认取景基础上拉近相机,再走 viewRefreshSingle(与真窗口缩放后
|
||||||
<< " 渲染块=" << warm << "\n";
|
// 完全相同的单纹理路径,选 level0 局部子区域),验证「拉近后」单图非空、完整。
|
||||||
|
if (nearPreview) {
|
||||||
|
st.cam->Dolly(2.5); // 拉近
|
||||||
|
ren->ResetCameraClippingRange();
|
||||||
|
warm = viewRefreshSingle(&st);
|
||||||
|
rw->Render();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "[view] 预热(" << (nearPreview ? "拉近局部段" : "默认局部段")
|
||||||
|
<< "): level=" << st.lastLevel << " 渲染块=" << warm << "\n";
|
||||||
|
|
||||||
const vtkIdType nonBlack = countNonBlackPixels(rw.Get(), winW, winH);
|
const vtkIdType nonBlack = countNonBlackPixels(rw.Get(), winW, winH);
|
||||||
const bool textureErr = capWin->textureError();
|
const bool textureErr = capWin->textureError();
|
||||||
|
|
@ -2799,7 +2899,9 @@ int cmdView(int argc, char** argv) {
|
||||||
const fs::path shotDir =
|
const fs::path shotDir =
|
||||||
fs::path("docs") / "superpowers" / "plans" / "poc-lod-shots";
|
fs::path("docs") / "superpowers" / "plans" / "poc-lod-shots";
|
||||||
fs::create_directories(shotDir);
|
fs::create_directories(shotDir);
|
||||||
const std::string pngPath = (shotDir / "view-default.png").string();
|
const std::string pngPath =
|
||||||
|
(shotDir / (nearPreview ? "view-near.png" : "view-default.png"))
|
||||||
|
.string();
|
||||||
savePng(rw.Get(), pngPath);
|
savePng(rw.Get(), pngPath);
|
||||||
// 结构像素计数:背景为深蓝灰(R/G≈10,B≈20),countNonBlackPixels(>10) 会把整屏
|
// 结构像素计数:背景为深蓝灰(R/G≈10,B≈20),countNonBlackPixels(>10) 会把整屏
|
||||||
// 背景都算「非空」,对验证「画面有结构」无意义。改为只数明显亮于背景的像素
|
// 背景都算「非空」,对验证「画面有结构」无意义。改为只数明显亮于背景的像素
|
||||||
|
|
@ -2859,17 +2961,17 @@ int cmdView(int argc, char** argv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (smoke) {
|
if (smoke) {
|
||||||
// 离屏 smoke:模拟一次缩放 → 验 LOD 切换 + 不崩。
|
// 离屏 smoke:模拟一次缩放 → 验 LOD 切换 + 不崩(单纹理路径)。
|
||||||
const int lvlNear = src.lastLevel();
|
const int lvlNear = st.lastLevel;
|
||||||
st.cam->Dolly(0.2); // 拉远 → 期望切粗 LOD
|
st.cam->Dolly(0.02); // 大幅拉远 → 期望切到粗 LOD(整卷粗层单纹理)
|
||||||
ren->ResetCameraClippingRange();
|
ren->ResetCameraClippingRange();
|
||||||
const std::size_t blocksFar = viewRefreshBlocks(&st);
|
const std::size_t blocksFar = viewRefreshSingle(&st);
|
||||||
const int lvlFar = src.lastLevel();
|
const int lvlFar = st.lastLevel;
|
||||||
rw->Render();
|
rw->Render();
|
||||||
st.cam->Dolly(8.0); // 拉近 → 期望切细 LOD
|
st.cam->Dolly(50.0); // 拉近回来 → 期望切回细 LOD(level0 局部子区域)
|
||||||
ren->ResetCameraClippingRange();
|
ren->ResetCameraClippingRange();
|
||||||
viewRefreshBlocks(&st);
|
viewRefreshSingle(&st);
|
||||||
const int lvlNear2 = src.lastLevel();
|
const int lvlNear2 = st.lastLevel;
|
||||||
rw->Render();
|
rw->Render();
|
||||||
const vtkIdType nb2 = countNonBlackPixels(rw.Get(), winW, winH);
|
const vtkIdType nb2 = countNonBlackPixels(rw.Get(), winW, winH);
|
||||||
|
|
||||||
|
|
@ -2933,7 +3035,8 @@ void usage() {
|
||||||
" gpr_poc fps-budget <storeDir> [--frames 90] "
|
" gpr_poc fps-budget <storeDir> [--frames 90] "
|
||||||
"[--bricks 4,16,64,128,256]\n"
|
"[--bricks 4,16,64,128,256]\n"
|
||||||
" gpr_poc view <storeDir> [--exagg 8] [--opacity 0.5] "
|
" gpr_poc view <storeDir> [--exagg 8] [--opacity 0.5] "
|
||||||
"[--budget 64] [--smoke] [--preview] [--frames 90]\n";
|
"[--smoke] [--preview] [--near] [--variant N] [--gallery] "
|
||||||
|
"[--frames 90]\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue