feat/vtk-3d-view #7
|
|
@ -0,0 +1,53 @@
|
|||
# Task 12d-fix 报告:修 gpr_poc view 空窗 + 控制台乱码
|
||||
|
||||
## 状态
|
||||
DONE。两 bug 均修复,构建通过(Community vcvars64 直驱 ninja,exit 0),离屏自检通过。
|
||||
|
||||
## 提交短哈希
|
||||
`1495d0e`(feat/vtk-3d-view 分支)
|
||||
|
||||
## 改动文件
|
||||
仅 `tools/gpr_poc/main.cpp`(+70 -1)。
|
||||
|
||||
### Bug 1:概览空窗(LOD 策略错)
|
||||
- 根因:`view` 每帧 `viewRefreshBlocks` 无脑走分块路径,相机概览时 `pickLevel` 选 level1(696 块)被 budget=64 砍到 64/696(9% 稀疏)→ 看着空。
|
||||
- 修复:`viewRefreshBlocks` 按相机选中 level 分流(同 12c renderLOD 已验):
|
||||
- 相机选中 **level0**(最近、要全分辨率,X=44476 无法成单纹理)→ 分块 + budget(核外 LRU,原路径不变)。
|
||||
- 相机选中 **level≥1**(概览/中远)→ `wholeVolumeLevelFor` 从 picked 起向粗找第一个“整卷各轴 ≤16384”的层(本数据 level0/1 的 X=44476/22238>16384 → 升 level2),用 `buildLevelImage` 整卷重组单张 image,单块喂 mapper(忽略 budget,粗层本就小)。整卷 image 按 level 缓存,仅 level 变化时重组。
|
||||
- 效果:概览不再是 64/696 稀疏块,而是 **1 个整卷块**渲染完整体。
|
||||
|
||||
### Bug 2:控制台中文乱码(GBK)
|
||||
- 修复:`main()` 入口 `#ifdef _WIN32` 下 `SetConsoleOutputCP(CP_UTF8);`(含 `<windows.h>`)。保留全文件已有中文输出,全子命令受益。
|
||||
|
||||
## 离屏自检结果(view --smoke,tmp\store_lod_001)
|
||||
修复前:
|
||||
```
|
||||
[view] 预热: level=1 视野块=696/696 驻留=64 渲染块=64 ← 64/696 稀疏
|
||||
```
|
||||
修复后:
|
||||
```
|
||||
[view] 预热: level=1 视野块=696/696 驻留=64 渲染块=1 ← 整卷单块(升 level2)
|
||||
=== view --smoke 离屏冒烟 ===
|
||||
近观 level=1 → 拉远 level=3 → 再拉近 level=1
|
||||
LOD 随缩放切换 : 是 ✔ (blocksFar=1)
|
||||
纹理维度错误 : 否
|
||||
渲出非空像素 : 是 (近=1024000 远拉近=1024000)
|
||||
smoke 结果 : OK ✔ 不崩
|
||||
```
|
||||
- **概览渲染块 64 → 1(整卷)**:核心修复,整卷完整渲染而非 9% 稀疏。
|
||||
- 渲出非空像素:是(1024000,无纹理错、不崩)。注:该视角整卷与原稀疏块均填满帧,像素计数饱和,故区分性证据是“渲染块 64→1(整卷)”。
|
||||
- **编码正常**:`=== view --smoke 离屏冒烟 ===` 等中文在 UTF-8 控制台正确显示,无 GBK 乱码。
|
||||
|
||||
## 提交干净性确认
|
||||
- `git diff --cached --stat` 提交前确认 index 仅含 `tools/gpr_poc/main.cpp`,无 chart/scatter/quill/rangeslider/Dialog/FormK 等并行会话文件。
|
||||
- 仅 `git add tools/gpr_poc/main.cpp`(及本报告),绝无 `git add -A`。
|
||||
|
||||
## 给用户的重跑命令
|
||||
真窗口交互(开窗即见完整粗层体,滚轮拉近变清晰/分块):
|
||||
```
|
||||
build\release\tools\gpr_poc\gpr_poc.exe view tmp\store_lod_001 --exagg 8 --opacity 0.6
|
||||
```
|
||||
离屏自检:
|
||||
```
|
||||
build\release\tools\gpr_poc\gpr_poc.exe view tmp\store_lod_001 --exagg 8 --opacity 0.6 --smoke
|
||||
```
|
||||
|
|
@ -73,6 +73,10 @@
|
|||
#include <vtkVolume.h>
|
||||
#include <vtkWindowToImageFilter.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h> // SetConsoleOutputCP(修中文控制台 GBK 乱码)
|
||||
#endif
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
using geopro::tools::Probe;
|
||||
using geopro::tools::Stopwatch;
|
||||
|
|
@ -2209,9 +2213,13 @@ int cmdFpsBudget(int argc, char** argv) {
|
|||
//
|
||||
// 离屏 smoke:--smoke 时不开真窗口,只离屏建管线 + 渲一帧 + 验非空像素,确保不崩。
|
||||
|
||||
// 整卷单张 3D 纹理的轴上限(同 renderLOD/renderB 实测 GL_MAX_3D_TEXTURE_SIZE)。
|
||||
constexpr int kViewMax3DTex = 16384;
|
||||
|
||||
// view 的每帧回调共享状态(挂到 interactor 的 EndInteraction/Timer/Render 上)。
|
||||
struct ViewState {
|
||||
geopro::render::OutOfCoreSource* src = nullptr;
|
||||
geopro::data::ChunkedVolumeStore* store = nullptr; // 整卷粗层渲染走它
|
||||
vtkMultiBlockVolumeMapper* mapper = nullptr;
|
||||
vtkCamera* cam = nullptr;
|
||||
vtkTextActor* fpsText = nullptr;
|
||||
|
|
@ -2219,11 +2227,65 @@ struct ViewState {
|
|||
Stopwatch frameTimer;
|
||||
double exagg = 8.0;
|
||||
int lastLevel = -1;
|
||||
// 整卷粗层 image 缓存(按 level 缓存,避免每帧重组整卷)。
|
||||
int cachedWholeLevel = -1;
|
||||
vtkSmartPointer<vtkImageData> cachedWholeImg;
|
||||
};
|
||||
|
||||
// 用 source 当前工作集刷新 mapper 输入(每块成 MultiBlock)。返回块数。
|
||||
// 某 level 整卷各轴是否都 ≤16384(可成单张 3D 纹理 → 整卷单 mapper 渲染)。
|
||||
bool levelFitsSingleTexture(const geopro::data::ChunkedVolumeStore& store,
|
||||
int level) {
|
||||
int nx = 0, ny = 0, nz = 0;
|
||||
store.dims(level, nx, ny, nz);
|
||||
return nx <= kViewMax3DTex && ny <= kViewMax3DTex && nz <= kViewMax3DTex;
|
||||
}
|
||||
|
||||
// 给定相机选中的 level,返回真正用于整卷渲染的 level:从 picked 起向粗逐层找,
|
||||
// 取第一个整卷各轴 ≤16384 的层(如 level0/1 长线 X 超 16384,则升到 level2)。
|
||||
// 找不到(极端情况)返回 -1,调用方退回分块路径。
|
||||
int wholeVolumeLevelFor(const geopro::data::ChunkedVolumeStore& store,
|
||||
int picked) {
|
||||
const int maxLevel = store.levels() - 1;
|
||||
for (int lv = std::max(0, picked); lv <= maxLevel; ++lv) {
|
||||
if (levelFitsSingleTexture(store, lv)) {
|
||||
return lv;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 用 source 当前工作集刷新 mapper 输入。返回喂给 mapper 的块数。
|
||||
//
|
||||
// 策略(同 12c renderLOD 已验,修概览空窗):
|
||||
// - 概览/中远视角(相机选中粗层)→ 升到最细的“整卷各轴 ≤16384”层,整卷重组成
|
||||
// 单张 image 单块喂 mapper(忽略 budget,粗层本就小),任何缩放都显示完整体,
|
||||
// 不再是 budget 砍后 9% 稀疏。本数据 level0/1 的 X(44476/22238)>16384 → 升 level2。
|
||||
// - 只有拉近到要全分辨率(相机选中 level0,X=44476 无法成单纹理)→ 退回分块 +
|
||||
// budget 路径(核外 LRU 驻留,只渲视野内子区域)。
|
||||
std::size_t viewRefreshBlocks(ViewState* st) {
|
||||
// 先让 source 按相机选好 LOD(同时刷新 lastLevel/视野统计供 fps 文本用)。
|
||||
st->src->update(st->cam);
|
||||
const int picked = st->src->lastLevel();
|
||||
|
||||
// 仅当相机选中 level0(最近、要全分辨率)才分块;其余(概览/中远)整卷渲染。
|
||||
if (st->store != nullptr && picked > 0) {
|
||||
const int wlv = wholeVolumeLevelFor(*st->store, picked);
|
||||
if (wlv >= 0) {
|
||||
// 整卷粗层:按 level 缓存整卷 image,仅在 level 变化时重组。
|
||||
if (st->cachedWholeLevel != wlv || st->cachedWholeImg == nullptr) {
|
||||
st->cachedWholeImg =
|
||||
buildLevelImage(*st->store, wlv, st->store->meta());
|
||||
st->cachedWholeLevel = wlv;
|
||||
}
|
||||
std::vector<vtkSmartPointer<vtkImageData>> one{st->cachedWholeImg};
|
||||
auto mb = makeMultiBlock(one);
|
||||
st->mapper->SetInputDataObject(mb);
|
||||
st->mapper->Update();
|
||||
return one.size();
|
||||
}
|
||||
}
|
||||
|
||||
// 全分辨率长线:分块 + budget。
|
||||
auto imgs = st->src->currentImages();
|
||||
auto mb = makeMultiBlock(imgs);
|
||||
st->mapper->SetInputDataObject(mb);
|
||||
|
|
@ -2272,6 +2334,8 @@ int cmdView(int argc, char** argv) {
|
|||
|
||||
// 核外源(读 meta + 建 pager,不载整卷)。
|
||||
geopro::render::OutOfCoreSource src(dir, budget);
|
||||
// 整卷粗层渲染另开 store(粗层各轴 ≤16384 时整卷单 mapper 渲,绕过 budget 稀疏)。
|
||||
geopro::data::ChunkedVolumeStore store(dir);
|
||||
const auto& m = src.meta();
|
||||
src.setAspect(static_cast<double>(winW) / winH);
|
||||
const double vmin = m.vminPhys, vmax = m.vmaxPhys;
|
||||
|
|
@ -2316,6 +2380,7 @@ int cmdView(int argc, char** argv) {
|
|||
|
||||
ViewState st;
|
||||
st.src = &src;
|
||||
st.store = &store;
|
||||
st.mapper = mapper.Get();
|
||||
st.fpsText = fpsText.Get();
|
||||
st.rw = rw.Get();
|
||||
|
|
@ -2431,6 +2496,10 @@ void usage() {
|
|||
} // namespace
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
#ifdef _WIN32
|
||||
// Windows 控制台默认 GBK,会把 UTF-8 中文输出显示为乱码。设为 UTF-8 码页修复。
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
#endif
|
||||
if (argc < 2) {
|
||||
usage();
|
||||
return 2;
|
||||
|
|
|
|||
Loading…
Reference in New Issue