feat/vtk-3d-view #7
|
|
@ -2895,12 +2895,13 @@ vtkSmartPointer<vtkVolumeProperty> makeVariantProperty(
|
|||
prop->SetGradientOpacity(grad);
|
||||
}
|
||||
|
||||
// 光照:ShadeOn + Ambient/Diffuse/Specular,让层界面带立体明暗(区别于纯平涂)。
|
||||
// 光照:ShadeOn + Ambient/Diffuse,保留立体明暗;Specular 压到 0.05(近乎关)避免
|
||||
// 旋转时视角相关的高光在体表游走形成「移动白斑」。保留 ambient/diffuse 立体感。
|
||||
if (v.useShade) {
|
||||
prop->ShadeOn();
|
||||
prop->SetAmbient(0.3);
|
||||
prop->SetDiffuse(0.7);
|
||||
prop->SetSpecular(0.2);
|
||||
prop->SetSpecular(0.05);
|
||||
prop->SetSpecularPower(10.0);
|
||||
}
|
||||
return prop;
|
||||
|
|
@ -2942,6 +2943,7 @@ constexpr int kViewMax3DTex = 16384;
|
|||
struct ViewState {
|
||||
geopro::render::ViewAdaptiveVolumeSource* source = nullptr;
|
||||
vtkSmartVolumeMapper* mapper = nullptr; // 高清层:单 SmartVolumeMapper(叠在底图上)
|
||||
vtkSmartVolumeMapper* baseMapper = nullptr; // 常驻底图层 mapper(用于按高清块挖空 cropping)
|
||||
vtkRenderer* ren = nullptr;
|
||||
vtkCamera* cam = nullptr;
|
||||
vtkTextActor* fpsText = nullptr;
|
||||
|
|
@ -2954,6 +2956,28 @@ struct ViewState {
|
|||
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(若有新结果)。不阻塞主线程——
|
||||
// 后台 builder 没新结果就沿用上一帧(拖动跟手的关键)。返回 1=喂了新图,0=无变化。
|
||||
std::size_t viewPickLatest(ViewState* st) {
|
||||
|
|
@ -2964,6 +2988,7 @@ std::size_t viewPickLatest(ViewState* st) {
|
|||
st->lastLevel = st->source->lastLevel();
|
||||
st->mapper->SetInputData(st->currentImg);
|
||||
st->mapper->Update();
|
||||
viewSyncBaseCropping(st); // 高清块换位 → 同步更新底图挖空盒,保持无缝无重叠
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -3270,6 +3295,10 @@ int cmdView(int argc, char** argv) {
|
|||
};
|
||||
const bool smoke = hasFlag("smoke");
|
||||
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)模拟「交互态:只渲常驻粗底图」——
|
||||
// 隐去高清叠加层、只渲整卷最粗 ≤16384 层单纹理 → 整卷概览、盖全、绝不空白。
|
||||
const bool basePreview = hasFlag("base");
|
||||
|
|
@ -3360,7 +3389,7 @@ int cmdView(int argc, char** argv) {
|
|||
baseMapper->Update();
|
||||
baseVolume->SetMapper(baseMapper);
|
||||
baseVolume->SetProperty(prop); // 与高清层共用传函(同配色/不透明度)
|
||||
baseVolume->SetScale(1.0, exagg, exagg); // 同垂向夸张 → 与高清层空间对齐
|
||||
baseVolume->SetScale(1.0, 1.0, exagg); // 垂向夸张只放大深度(Z);横向路宽(Y)不动 → 与高清层空间对齐
|
||||
ren->AddVolume(baseVolume); // 先加底图 → 底层常渲
|
||||
}
|
||||
|
||||
|
|
@ -3380,7 +3409,7 @@ int cmdView(int argc, char** argv) {
|
|||
auto volume = vtkSmartPointer<vtkVolume>::New();
|
||||
volume->SetMapper(mapper);
|
||||
volume->SetProperty(prop);
|
||||
volume->SetScale(1.0, exagg, exagg); // 垂向夸张(默认 var4 exagg)
|
||||
volume->SetScale(1.0, 1.0, exagg); // 垂向夸张只放大深度(Z);横向路宽(Y)保持真实比例(修 GPR 路被压胖)
|
||||
ren->AddVolume(volume); // 后加高清 → 叠在底图上
|
||||
|
||||
// 屏幕左上角实时 fps 文本。
|
||||
|
|
@ -3398,6 +3427,7 @@ int cmdView(int argc, char** argv) {
|
|||
ViewState st;
|
||||
st.source = &source;
|
||||
st.mapper = mapper.Get();
|
||||
st.baseMapper = baseMapper.Get(); // 供 viewSyncBaseCropping 按高清块挖空底图
|
||||
st.ren = ren.Get();
|
||||
st.fpsText = fpsText.Get();
|
||||
st.rw = rw.Get();
|
||||
|
|
@ -3414,6 +3444,7 @@ int cmdView(int argc, char** argv) {
|
|||
// 拖动/缩放时即使高清全部缺位,底图也独立盖住整个体(永不空白的命脉)。
|
||||
if (preview && basePreview) {
|
||||
volume->SetVisibility(0); // 隐去高清层 → 只剩常驻底图
|
||||
baseMapper->SetCropping(0); // 高清隐了 → 底图取消挖空,整卷全渲(证明永不空白)
|
||||
// 框整卷(无参 ResetCamera 按场景中可见 actor 包围盒;高清隐了 → 框底图全卷)。
|
||||
ren->ResetCamera();
|
||||
st.cam = ren->GetActiveCamera();
|
||||
|
|
@ -3495,6 +3526,32 @@ int cmdView(int argc, char** argv) {
|
|||
(shotDir / (nearPreview ? "view-near.png" : "view-default.png"))
|
||||
.string();
|
||||
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) 会把整屏
|
||||
// 背景都算「非空」,对验证「画面有结构」无意义。改为只数明显亮于背景的像素
|
||||
// (任一通道 >50),作为「确有渲出的体结构」的诚实判据。
|
||||
|
|
|
|||
Loading…
Reference in New Issue