跳转至

体渲染绘制地球

源码 EarthVisualization

1 参考

2 纹理贴图

使用 vtkTexturedSphereSource 可以快速构建一个贴图地球。

Sphere → Texture → Mapper → Actor

2.1 关键代码

vtkNew<vtkTexturedSphereSource> sphere;
sphere->SetPhiResolution(64);
sphere->SetThetaResolution(64);
sphere->SetRadius(1.0);

vtkNew<vtkImageReader2Factory> factory;
vtkImageReader2* reader = factory->CreateImageReader2("earth.jpg");
reader->SetFileName("earth.jpg");

vtkNew<vtkTexture> texture;
texture->SetInputConnection(reader->GetOutputPort());

vtkNew<vtkPolyDataMapper> mapper;
mapper->SetInputConnection(sphere->GetOutputPort());

vtkNew<vtkActor> actor;
actor->SetMapper(mapper);
actor->SetTexture(texture);

3 体渲染

构建一个球形体素场,并基于 NASA 地形数据进行体渲染。

自定义隐函数 → 体素采样 → VolumeMapper → Volume

3.1 自定义隐函数

继承 vtkImplicitFunction,在 EvaluateFunction 中完成:

  1. 判断点是否位于球壳

  2. 计算球坐标

  3. 映射到纹理坐标

  4. 从地形灰度图采样

  5. 返回标量值

_球面映射公式

`r = sqrt(x² + y² + z²)

若在球壳附近:

|r - R| < ε

计算:

φ = acos(z / r)
θ = atan2(y, x)
u = θ / (2π)
v = φ / π

映射到纹理:

int texX = u * width;
int texY = (1 - v) * height;

读取灰度值并映射为高程:

double topoValue = 6400.0 * (static_cast<double>(*topoPixel) / 255.0);
double bathValue = -8000.0 * (1.0 - static_cast<double>(*bathPixel) / 255.0);
pixel = topoValue + bathValue;

返回该值作为体素标量。

3.2 构建体数据

vtkNew<vtkSampleFunction> sample;
sample->SetImplicitFunction(earthSphere);
sample->SetSampleDimensions(255,255,255);
sample->SetModelBounds(-1,1,-1,1,-1,1);

生成 vtkImageData 体素场。

3.3 体渲染参数

透明度函数

opacityFunction->AddPoint(-8100, 0.0);
opacityFunction->AddPoint(-8000, 1.0);
opacityFunction->AddPoint(6400, 1.0);
opacityFunction->AddPoint(6500, .0);

颜色函数

vtkNew<vtkColorTransferFunction> colorFunction;
// 海洋深度 (-8000 到 0)
colorFunction->AddRGBPoint(-8000, 0.0, 0.0, 0.2); // 深海沟
colorFunction->AddRGBPoint(-4000, 0.0, 0.1, 0.4); // 深海
colorFunction->AddRGBPoint(-2000, 0.0, 0.2, 0.6); // 中深海
colorFunction->AddRGBPoint(-500, 0.1, 0.4, 0.8); // 浅海
colorFunction->AddRGBPoint(-100, 0.15, 0.5, 0.85); // 近海
colorFunction->AddRGBPoint(0, 0.3, 0.6, 0.3); // 海岸线(改为绿色调)
// 陆地高程 (0 到 6400)
colorFunction->AddRGBPoint(50, 0.3, 0.65, 0.25); // 海岸湿地
colorFunction->AddRGBPoint(200, 0.2, 0.75, 0.2); // 低地平原(鲜绿)
colorFunction->AddRGBPoint(500, 0.4, 0.85, 0.3); // 平原(亮绿,突出)
colorFunction->AddRGBPoint(800, 0.5, 0.8, 0.35); // 高平原
colorFunction->AddRGBPoint(1200, 0.65, 0.75, 0.35); // 丘陵过渡
colorFunction->AddRGBPoint(1800, 0.75, 0.7, 0.3); // 丘陵(黄绿)
colorFunction->AddRGBPoint(2500, 0.7, 0.55, 0.25); // 低山(褐色)
colorFunction->AddRGBPoint(3500, 0.65, 0.45, 0.2); // 中山
colorFunction->AddRGBPoint(4500, 0.6, 0.35, 0.15); // 高山(深褐)
colorFunction->AddRGBPoint(5000, 0.7, 0.7, 0.7); // 雪线开始
colorFunction->AddRGBPoint(5500, 0.85, 0.85, 0.85); // 积雪
colorFunction->AddRGBPoint(6400, 1.0, 1.0, 1.0); // 雪峰(纯白)