feat/vtk-3d-view #7
|
|
@ -6,6 +6,7 @@
|
|||
#include <vtkColorTransferFunction.h>
|
||||
#include <vtkDoubleArray.h>
|
||||
#include <vtkNew.h>
|
||||
#include <vtkShortArray.h>
|
||||
#include <vtkSmartVolumeMapper.h>
|
||||
#include <vtkPiecewiseFunction.h>
|
||||
#include <vtkPointData.h>
|
||||
|
|
@ -23,6 +24,30 @@ constexpr double kMaxOpacity = 0.15;
|
|||
// NaN/留空格的哨兵:落在 [vmin,vmax] 之外,传递函数把它映射为完全透明。
|
||||
double sentinel(double vmin) { return vmin - 1.0; }
|
||||
|
||||
// double/int16 两版公用的 mapper+property+volume 组装(行为与原 double 版一致)。
|
||||
vtkSmartPointer<vtkVolume> assembleVolume(vtkImageData* img,
|
||||
vtkColorTransferFunction* color,
|
||||
vtkPiecewiseFunction* opacity)
|
||||
{
|
||||
// SmartVolumeMapper:有 GPU 走 GPU ray cast,否则自动回退 CPU,避免无 GPU 时卡死/失败。
|
||||
vtkNew<vtkSmartVolumeMapper> mapper;
|
||||
mapper->SetInputData(img);
|
||||
// 全程统一全质量(GPU 足够快, 实测 ~7ms/帧):关掉交互降采样, 避免"停手补高清"那一帧突跳停顿。
|
||||
mapper->SetAutoAdjustSampleDistances(0);
|
||||
mapper->SetInteractiveAdjustSampleDistances(0);
|
||||
|
||||
vtkNew<vtkVolumeProperty> prop;
|
||||
prop->SetColor(color);
|
||||
prop->SetScalarOpacity(opacity);
|
||||
prop->SetInterpolationTypeToLinear();
|
||||
prop->ShadeOff();
|
||||
|
||||
auto volume = vtkSmartPointer<vtkVolume>::New();
|
||||
volume->SetMapper(mapper);
|
||||
volume->SetProperty(prop);
|
||||
return volume;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
vtkSmartPointer<vtkVolume> buildVoxel(const geopro::core::ScalarVolume& vol,
|
||||
|
|
@ -71,23 +96,65 @@ vtkSmartPointer<vtkVolume> buildVoxel(const geopro::core::ScalarVolume& vol,
|
|||
opacity->AddPoint(vmin, 0.0);
|
||||
opacity->AddPoint(vmax, kMaxOpacity);
|
||||
|
||||
// SmartVolumeMapper:有 GPU 走 GPU ray cast,否则自动回退 CPU,避免无 GPU 时卡死/失败。
|
||||
vtkNew<vtkSmartVolumeMapper> mapper;
|
||||
mapper->SetInputData(img);
|
||||
// 全程统一全质量(GPU 足够快, 实测 ~7ms/帧):关掉交互降采样, 避免"停手补高清"那一帧突跳停顿。
|
||||
mapper->SetAutoAdjustSampleDistances(0);
|
||||
mapper->SetInteractiveAdjustSampleDistances(0);
|
||||
return assembleVolume(img, color, opacity);
|
||||
}
|
||||
|
||||
vtkNew<vtkVolumeProperty> prop;
|
||||
prop->SetColor(color);
|
||||
prop->SetScalarOpacity(opacity);
|
||||
prop->SetInterpolationTypeToLinear();
|
||||
prop->ShadeOff();
|
||||
vtkSmartPointer<vtkVolume> buildVoxelI16(const geopro::core::ScalarVolumeI16& vol,
|
||||
const geopro::core::Quant& q,
|
||||
const geopro::core::ColorScale& cs,
|
||||
double ox, double oy, double oz,
|
||||
double dx, double dy, double dz,
|
||||
double vminPhys, double vmaxPhys,
|
||||
vtkSmartPointer<vtkImageData>& outImage)
|
||||
{
|
||||
const int nx = vol.nx(), ny = vol.ny(), nz = vol.nz();
|
||||
|
||||
auto volume = vtkSmartPointer<vtkVolume>::New();
|
||||
volume->SetMapper(mapper);
|
||||
volume->SetProperty(prop);
|
||||
return volume;
|
||||
// vmin/vmax 退化兜底,避免传递函数区间为零。
|
||||
if (vminPhys >= vmaxPhys) vmaxPhys = vminPhys + 1.0;
|
||||
|
||||
auto img = vtkSmartPointer<vtkImageData>::New();
|
||||
img->SetDimensions(nx, ny, nz);
|
||||
img->SetOrigin(ox, oy, oz);
|
||||
img->SetSpacing(dx, dy, dz);
|
||||
|
||||
vtkNew<vtkShortArray> sc;
|
||||
sc->SetName("v");
|
||||
sc->SetNumberOfTuples(static_cast<vtkIdType>(nx) * ny * nz);
|
||||
// 点序 i 最快、j 次之、k 最慢(匹配 vtkImageData 与 ScalarVolumeI16::idx)。
|
||||
// kBlank 原样保留,由量化域传递函数映射为透明。
|
||||
for (int k = 0; k < nz; ++k)
|
||||
for (int j = 0; j < ny; ++j)
|
||||
for (int i = 0; i < nx; ++i) {
|
||||
const std::int16_t qv = vol.at(i, j, k);
|
||||
const vtkIdType id = (static_cast<vtkIdType>(k) * ny + j) * nx + i;
|
||||
sc->SetValue(id, qv);
|
||||
}
|
||||
img->GetPointData()->SetScalars(sc);
|
||||
outImage = img;
|
||||
|
||||
// 传递函数在量化域取(标量本身是 int16 量化值)。
|
||||
const std::int16_t qmin = q.toQ(vminPhys);
|
||||
const std::int16_t qmax = q.toQ(vmaxPhys);
|
||||
const double qminD = static_cast<double>(qmin);
|
||||
const double qmaxD = static_cast<double>(qmax);
|
||||
|
||||
// 颜色传递函数:对每个量化级 qv,物理值 phys=q.toPhys(qv),用 double 版相同方式取色。
|
||||
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);
|
||||
}
|
||||
|
||||
// 不透明度传递函数(量化域):kBlank → 0(透明);[qmin,qmax] 线性递增到 kMaxOpacity。
|
||||
vtkNew<vtkPiecewiseFunction> opacity;
|
||||
opacity->AddPoint(static_cast<double>(geopro::core::ScalarVolumeI16::kBlank), 0.0);
|
||||
opacity->AddPoint(qminD, 0.0);
|
||||
opacity->AddPoint(qmaxD, kMaxOpacity);
|
||||
|
||||
return assembleVolume(img, color, opacity);
|
||||
}
|
||||
|
||||
vtkSmartPointer<vtkVolume> buildVoxel(const geopro::core::ScalarVolume& vol,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include <vtkImageData.h>
|
||||
#include "model/Field.hpp"
|
||||
#include "model/ColorScale.hpp"
|
||||
#include "model/ScalarVolumeI16.hpp"
|
||||
namespace geopro::render {
|
||||
|
||||
// 把 core 规则标量体(IDW 输出,含 NaN 留空)转 vtkImageData,再建 GPU 光线投射体绘制。
|
||||
|
|
@ -23,4 +24,16 @@ vtkSmartPointer<vtkVolume> buildVoxel(const geopro::core::ScalarVolume& vol,
|
|||
double vmin, double vmax,
|
||||
vtkSmartPointer<vtkImageData>& outImage);
|
||||
|
||||
// int16 量化体 → vtkImageData(vtkShortArray) → GPU 体绘制(与 double 版并列)。
|
||||
// 传函在量化域取:qmin=q.toQ(vminPhys)、qmax=q.toQ(vmaxPhys);kBlank → 不透明度 0(透明)。
|
||||
// color 按 ColorScale 在 [vminPhys,vmaxPhys] 采样(对每个量化级 qv 用 q.toPhys(qv) 查色)。
|
||||
// outImage 暴露内部 vtkImageData,供建交互切片 widget。返回 vtkVolume。
|
||||
vtkSmartPointer<vtkVolume> buildVoxelI16(const geopro::core::ScalarVolumeI16& vol,
|
||||
const geopro::core::Quant& q,
|
||||
const geopro::core::ColorScale& cs,
|
||||
double ox, double oy, double oz,
|
||||
double dx, double dy, double dz,
|
||||
double vminPhys, double vmaxPhys,
|
||||
vtkSmartPointer<vtkImageData>& outImage);
|
||||
|
||||
} // namespace geopro::render
|
||||
|
|
|
|||
|
|
@ -91,6 +91,8 @@ target_sources(geopro_tests PRIVATE render/test_color_lut.cpp)
|
|||
target_sources(geopro_tests PRIVATE render/test_contour_bands.cpp)
|
||||
# dd_voxel:buildVoxel(ScalarVolume->vtkImageData->GPU 体绘制) 构建不崩 + dims 正确。
|
||||
target_sources(geopro_tests PRIVATE render/test_voxel_build.cpp)
|
||||
# dd_voxel int16:buildVoxelI16(ScalarVolumeI16->vtkImageData(vtkShortArray)) 类型/dims/值/blank。
|
||||
target_sources(geopro_tests PRIVATE render/test_voxel_i16_smoke.cpp)
|
||||
# dd_voxel 回归:buildVoxelFromScatters(散点 projX/Y -EPSG:4547-> 世界系 + IDW) 配准+充填(需 PROJ_DATA)。
|
||||
target_sources(geopro_tests PRIVATE render/test_voxel_register.cpp)
|
||||
# Curtain:buildCurtain(Grid+GeoLocalFrame->vtkStructuredGrid 帘面) 非空 actor + 点数=nx*ny。
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
#include <gtest/gtest.h>
|
||||
|
||||
#include <vtkImageData.h>
|
||||
|
||||
#include "actors/VoxelActor.hpp"
|
||||
#include "model/ColorScale.hpp"
|
||||
#include "model/ScalarVolumeI16.hpp"
|
||||
|
||||
using namespace geopro;
|
||||
|
||||
// buildVoxelI16 无窗冒烟:int16 量化体 → vtkImageData(vtkShortArray)。
|
||||
// 仅验 image 构造/类型(VTK_SHORT)/dims/逐体素值/blank 原样保留;不实际 GPU 渲染。
|
||||
TEST(VoxelActorI16, BuildsShortImageWithBlank) {
|
||||
core::ScalarVolumeI16 v(2, 2, 2);
|
||||
for (auto& x : v.data()) x = 10;
|
||||
v.at(0, 0, 0) = core::ScalarVolumeI16::kBlank;
|
||||
|
||||
core::Quant q{1.0, 0.0};
|
||||
|
||||
core::ColorScale cs;
|
||||
cs.addStop(0.0, core::Rgba{0, 0, 255, 255});
|
||||
cs.addStop(20.0, core::Rgba{255, 0, 0, 255});
|
||||
|
||||
vtkSmartPointer<vtkImageData> img;
|
||||
auto vol = render::buildVoxelI16(v, q, cs, 0, 0, 0, 1, 1, 1, 0.0, 20.0, img);
|
||||
|
||||
ASSERT_NE(vol.Get(), nullptr);
|
||||
ASSERT_NE(img.Get(), nullptr);
|
||||
EXPECT_EQ(img->GetScalarType(), VTK_SHORT);
|
||||
|
||||
int dims[3];
|
||||
img->GetDimensions(dims);
|
||||
EXPECT_EQ(dims[0], 2);
|
||||
EXPECT_EQ(dims[1], 2);
|
||||
EXPECT_EQ(dims[2], 2);
|
||||
|
||||
EXPECT_EQ(*static_cast<short*>(img->GetScalarPointer(1, 1, 1)), 10);
|
||||
EXPECT_EQ(*static_cast<short*>(img->GetScalarPointer(0, 0, 0)),
|
||||
core::ScalarVolumeI16::kBlank);
|
||||
}
|
||||
Loading…
Reference in New Issue