feat/vtk-3d-view #7
|
|
@ -2895,12 +2895,13 @@ vtkSmartPointer<vtkVolumeProperty> makeVariantProperty(
|
||||||
prop->SetGradientOpacity(grad);
|
prop->SetGradientOpacity(grad);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 光照:ShadeOn + Ambient/Diffuse/Specular,让层界面带立体明暗(区别于纯平涂)。
|
// 光照:ShadeOn + Ambient/Diffuse,保留立体明暗;Specular 压到 0.05(近乎关)避免
|
||||||
|
// 旋转时视角相关的高光在体表游走形成「移动白斑」。保留 ambient/diffuse 立体感。
|
||||||
if (v.useShade) {
|
if (v.useShade) {
|
||||||
prop->ShadeOn();
|
prop->ShadeOn();
|
||||||
prop->SetAmbient(0.3);
|
prop->SetAmbient(0.3);
|
||||||
prop->SetDiffuse(0.7);
|
prop->SetDiffuse(0.7);
|
||||||
prop->SetSpecular(0.2);
|
prop->SetSpecular(0.05);
|
||||||
prop->SetSpecularPower(10.0);
|
prop->SetSpecularPower(10.0);
|
||||||
}
|
}
|
||||||
return prop;
|
return prop;
|
||||||
|
|
@ -2942,6 +2943,7 @@ constexpr int kViewMax3DTex = 16384;
|
||||||
struct ViewState {
|
struct ViewState {
|
||||||
geopro::render::ViewAdaptiveVolumeSource* source = nullptr;
|
geopro::render::ViewAdaptiveVolumeSource* source = nullptr;
|
||||||
vtkSmartVolumeMapper* mapper = nullptr; // 高清层:单 SmartVolumeMapper(叠在底图上)
|
vtkSmartVolumeMapper* mapper = nullptr; // 高清层:单 SmartVolumeMapper(叠在底图上)
|
||||||
|
vtkSmartVolumeMapper* baseMapper = nullptr; // 常驻底图层 mapper(用于按高清块挖空 cropping)
|
||||||
vtkRenderer* ren = nullptr;
|
vtkRenderer* ren = nullptr;
|
||||||
vtkCamera* cam = nullptr;
|
vtkCamera* cam = nullptr;
|
||||||
vtkTextActor* fpsText = nullptr;
|
vtkTextActor* fpsText = nullptr;
|
||||||
|
|
@ -2954,6 +2956,28 @@ struct ViewState {
|
||||||
bool inCb = false;
|
bool inCb = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// C3-8 底图按高清块挖空:用 vtkVolumeMapper 的 Cropping 在【底图 mapper】上裁掉高清块
|
||||||
|
// 覆盖的那一块,使任意时刻 = 底图(高清区外) + 高清(高清区内),无空间重叠 → 不再双渲发白。
|
||||||
|
//
|
||||||
|
// 裁剪平面用高清单图的【模型坐标包围盒】(GetBounds)。底图与高清两 actor 用同一份
|
||||||
|
// SetScale(1,1,exagg),且高清单图自带绝对世界 origin(buildLocalLevel0Image 沿 X 偏移),
|
||||||
|
// 与底图同坐标系 → 高清块的模型盒平面直接作底图 cropping 平面即对齐(两层 scale 一致)。
|
||||||
|
//
|
||||||
|
// CroppingRegionFlags:6 个平面把空间分成 3×3×3=27 区,中心区(盒内)= bit 0x0002000
|
||||||
|
// (VTK_CROP_SUBVOLUME)。要「渲盒外、挖掉盒内」→ 全 27 区减中心区 = 0x7ffffff & ~0x0002000。
|
||||||
|
void viewSyncBaseCropping(ViewState* st) {
|
||||||
|
if (st->baseMapper == nullptr) return;
|
||||||
|
if (st->currentImg == nullptr) { // 高清未就绪:底图不裁剪,全渲(绝不空白)
|
||||||
|
st->baseMapper->SetCropping(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
double b[6];
|
||||||
|
st->currentImg->GetBounds(b); // 模型坐标盒(含绝对 X origin),与底图同系
|
||||||
|
st->baseMapper->SetCroppingRegionPlanes(b[0], b[1], b[2], b[3], b[4], b[5]);
|
||||||
|
st->baseMapper->SetCroppingRegionFlags(0x7ffffff & ~VTK_CROP_SUBVOLUME);
|
||||||
|
st->baseMapper->SetCropping(1);
|
||||||
|
}
|
||||||
|
|
||||||
// C3-2 非阻塞拉取:把最新已就绪单图喂 mapper(若有新结果)。不阻塞主线程——
|
// C3-2 非阻塞拉取:把最新已就绪单图喂 mapper(若有新结果)。不阻塞主线程——
|
||||||
// 后台 builder 没新结果就沿用上一帧(拖动跟手的关键)。返回 1=喂了新图,0=无变化。
|
// 后台 builder 没新结果就沿用上一帧(拖动跟手的关键)。返回 1=喂了新图,0=无变化。
|
||||||
std::size_t viewPickLatest(ViewState* st) {
|
std::size_t viewPickLatest(ViewState* st) {
|
||||||
|
|
@ -2964,6 +2988,7 @@ std::size_t viewPickLatest(ViewState* st) {
|
||||||
st->lastLevel = st->source->lastLevel();
|
st->lastLevel = st->source->lastLevel();
|
||||||
st->mapper->SetInputData(st->currentImg);
|
st->mapper->SetInputData(st->currentImg);
|
||||||
st->mapper->Update();
|
st->mapper->Update();
|
||||||
|
viewSyncBaseCropping(st); // 高清块换位 → 同步更新底图挖空盒,保持无缝无重叠
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3270,6 +3295,10 @@ 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");
|
||||||
|
// C3-8 验收用:--preview --shots 额外从【真 view 场景(base+hires+cropping)】多旋转角
|
||||||
|
// 离屏出图,用于人工核对「路细长不胖 / 拼接无缝无白 / 旋转无移动白斑」。只加图,不改
|
||||||
|
// 默认行为(无 --shots 时与原 preview 完全一致)。
|
||||||
|
const bool shots = hasFlag("shots");
|
||||||
// C3-6 底图预览:--preview --base(或 --base)模拟「交互态:只渲常驻粗底图」——
|
// C3-6 底图预览:--preview --base(或 --base)模拟「交互态:只渲常驻粗底图」——
|
||||||
// 隐去高清叠加层、只渲整卷最粗 ≤16384 层单纹理 → 整卷概览、盖全、绝不空白。
|
// 隐去高清叠加层、只渲整卷最粗 ≤16384 层单纹理 → 整卷概览、盖全、绝不空白。
|
||||||
const bool basePreview = hasFlag("base");
|
const bool basePreview = hasFlag("base");
|
||||||
|
|
@ -3360,7 +3389,7 @@ int cmdView(int argc, char** argv) {
|
||||||
baseMapper->Update();
|
baseMapper->Update();
|
||||||
baseVolume->SetMapper(baseMapper);
|
baseVolume->SetMapper(baseMapper);
|
||||||
baseVolume->SetProperty(prop); // 与高清层共用传函(同配色/不透明度)
|
baseVolume->SetProperty(prop); // 与高清层共用传函(同配色/不透明度)
|
||||||
baseVolume->SetScale(1.0, exagg, exagg); // 同垂向夸张 → 与高清层空间对齐
|
baseVolume->SetScale(1.0, 1.0, exagg); // 垂向夸张只放大深度(Z);横向路宽(Y)不动 → 与高清层空间对齐
|
||||||
ren->AddVolume(baseVolume); // 先加底图 → 底层常渲
|
ren->AddVolume(baseVolume); // 先加底图 → 底层常渲
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3380,7 +3409,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); // 垂向夸张(默认 var4 exagg)
|
volume->SetScale(1.0, 1.0, exagg); // 垂向夸张只放大深度(Z);横向路宽(Y)保持真实比例(修 GPR 路被压胖)
|
||||||
ren->AddVolume(volume); // 后加高清 → 叠在底图上
|
ren->AddVolume(volume); // 后加高清 → 叠在底图上
|
||||||
|
|
||||||
// 屏幕左上角实时 fps 文本。
|
// 屏幕左上角实时 fps 文本。
|
||||||
|
|
@ -3398,6 +3427,7 @@ int cmdView(int argc, char** argv) {
|
||||||
ViewState st;
|
ViewState st;
|
||||||
st.source = &source;
|
st.source = &source;
|
||||||
st.mapper = mapper.Get();
|
st.mapper = mapper.Get();
|
||||||
|
st.baseMapper = baseMapper.Get(); // 供 viewSyncBaseCropping 按高清块挖空底图
|
||||||
st.ren = ren.Get();
|
st.ren = ren.Get();
|
||||||
st.fpsText = fpsText.Get();
|
st.fpsText = fpsText.Get();
|
||||||
st.rw = rw.Get();
|
st.rw = rw.Get();
|
||||||
|
|
@ -3414,6 +3444,7 @@ int cmdView(int argc, char** argv) {
|
||||||
// 拖动/缩放时即使高清全部缺位,底图也独立盖住整个体(永不空白的命脉)。
|
// 拖动/缩放时即使高清全部缺位,底图也独立盖住整个体(永不空白的命脉)。
|
||||||
if (preview && basePreview) {
|
if (preview && basePreview) {
|
||||||
volume->SetVisibility(0); // 隐去高清层 → 只剩常驻底图
|
volume->SetVisibility(0); // 隐去高清层 → 只剩常驻底图
|
||||||
|
baseMapper->SetCropping(0); // 高清隐了 → 底图取消挖空,整卷全渲(证明永不空白)
|
||||||
// 框整卷(无参 ResetCamera 按场景中可见 actor 包围盒;高清隐了 → 框底图全卷)。
|
// 框整卷(无参 ResetCamera 按场景中可见 actor 包围盒;高清隐了 → 框底图全卷)。
|
||||||
ren->ResetCamera();
|
ren->ResetCamera();
|
||||||
st.cam = ren->GetActiveCamera();
|
st.cam = ren->GetActiveCamera();
|
||||||
|
|
@ -3495,6 +3526,32 @@ int cmdView(int argc, char** argv) {
|
||||||
(shotDir / (nearPreview ? "view-near.png" : "view-default.png"))
|
(shotDir / (nearPreview ? "view-near.png" : "view-default.png"))
|
||||||
.string();
|
.string();
|
||||||
savePng(rw.Get(), pngPath);
|
savePng(rw.Get(), pngPath);
|
||||||
|
// C3-8:多旋转角出图(同一真 view 场景:base+hires 共用 SetScale(1,1,exagg) + 底图
|
||||||
|
// 按高清块 cropping 挖空)。从默认相机起取若干 (azimuth,elevation) 离屏存图,供人工
|
||||||
|
// 核对路细长比例 / 拼接无缝 / 旋转无移动白斑。无 --shots 不执行。
|
||||||
|
if (shots) {
|
||||||
|
const struct {
|
||||||
|
const char* name;
|
||||||
|
double az;
|
||||||
|
double el;
|
||||||
|
} kAngles[] = {
|
||||||
|
{"view-shot-az0", 0.0, 0.0}, {"view-shot-az30", 30.0, 0.0},
|
||||||
|
{"view-shot-az60", 60.0, 0.0}, {"view-shot-az-30", -30.0, 0.0},
|
||||||
|
{"view-shot-el20", 0.0, 20.0}, {"view-shot-az45el15", 45.0, 15.0},
|
||||||
|
};
|
||||||
|
for (const auto& s : kAngles) {
|
||||||
|
st.cam->Azimuth(s.az);
|
||||||
|
st.cam->Elevation(s.el);
|
||||||
|
ren->ResetCameraClippingRange();
|
||||||
|
rw->Render();
|
||||||
|
const std::string sp = (shotDir / (std::string(s.name) + ".png")).string();
|
||||||
|
savePng(rw.Get(), sp);
|
||||||
|
std::cout << "[view] 旋转角出图: " << sp << " (az=" << s.az
|
||||||
|
<< " el=" << s.el << ")\n";
|
||||||
|
st.cam->Elevation(-s.el); // 复位到默认朝向,下一角从默认起算
|
||||||
|
st.cam->Azimuth(-s.az);
|
||||||
|
}
|
||||||
|
}
|
||||||
// 结构像素计数:背景为深蓝灰(R/G≈10,B≈20),countNonBlackPixels(>10) 会把整屏
|
// 结构像素计数:背景为深蓝灰(R/G≈10,B≈20),countNonBlackPixels(>10) 会把整屏
|
||||||
// 背景都算「非空」,对验证「画面有结构」无意义。改为只数明显亮于背景的像素
|
// 背景都算「非空」,对验证「画面有结构」无意义。改为只数明显亮于背景的像素
|
||||||
// (任一通道 >50),作为「确有渲出的体结构」的诚实判据。
|
// (任一通道 >50),作为「确有渲出的体结构」的诚实判据。
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue