<think>我们已经在使用osgEarth的GDALElevationLayer加载高程TIFF文件。为了显示高程颜色图(即根据高程值着色),我们需要添加一个颜色映射层(ColorRamp)。在osgEarth中,我们可以通过创建一个自定义的着色器(Shader)或者使用内置的ElevationLayer的渲染技术来实现。一种简单的方法是使用osgEarth的Util::TopographicMapTechnique,它提供了高程颜色映射。但是,这里我们将采用另一种方法:使用osgEarth的ColorRamp类创建一个颜色渐变,然后将其应用到高程图层上。步骤:1.创建并添加高程图层(GDALElevationLayer)。2.创建一个颜色渐变(ColorRamp)并生成一个纹理(Texture)。3.创建一个地形效果(TerrainEffect)来应用这个颜色渐变纹理。4.将地形效果添加到地图(Map)中。以下是如何在已有的代码基础上(即已经加载了高程图层)实现高程颜色图的显示:注意:我们假设已经有一个Map对象,并且已经将高程图层添加到Map中。我们将创建一个自定义的TerrainEffect,它会在渲染地形时使用颜色渐变纹理。具体步骤如下:1.创建颜色渐变纹理。2.编写一个着色器程序,该程序将高程值映射到颜色渐变纹理上。3.创建一个TerrainEffect,将着色器程序应用到地形上。由于osgEarth的TerrainEffect需要添加到Map的TerrainEngine中,我们可以在MapNode创建之后添加这个效果。示例代码:首先,在创建MapNode之后,我们添加以下代码:```cpp#include<osgEarth/MapNode>#include <osgEarth/Color>#include <osgEarth/ShaderLoader>
#include<osgEarth/TerrainEngineNode>
#include<osgEarth/TextureCompositor>
#include<osgEarth/ColorRamp>
#include<osgEarth/Shaders>#include <osgEarth/Registry>#include <osgEarth/Capabilities>
#include<osgEarth/VirtualProgram>
//创建颜色渐变纹理osg::Texture2D* createColorRampTexture()
{osgEarth::ColorRampramp;//设置颜色渐变:从低到高,例如:蓝色->绿色->黄色->红色ramp.add(osgEarth::Color(0,0,1,1),0.0f);//蓝色,低高程ramp.add(osgEarth::Color(0,1,0,1),0.5f);//绿色ramp.add(osgEarth::Color(1,1,0,1),0.75f);//黄色ramp.add(osgEarth::Color(1,0,0,1),1.0f);//红色,高高程osg::Image*image =ramp.createImage(256);osg::Texture2D*texture =new osg::Texture2D();texture->setImage(image);texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);return texture;
}//创建并添加高程颜色映射效果void addElevationColorRampEffect(osgEarth::MapNode* mapNode){//创建颜色渐变纹理osg::Texture2D* colorRampTex= createColorRampTexture();//创建一个虚拟程序(着色器程序)osgEarth::VirtualProgram*vp= newosgEarth::VirtualProgram();vp->setName("elevation_color_ramp");//加载着色器代码std::stringvs=osgEarth::ShaderLoader::load(osgEarth::ShaderPackage("elevation_color_ramp"),"elevation_color_ramp.vert",osgEarth::Registry::instance()->shaderGenerator());std::string fs= osgEarth::ShaderLoader::load(osgEarth::ShaderPackage("elevation_color_ramp"),"elevation_color_ramp.frag",osgEarth::Registry::instance()->shaderGenerator());vp->setShader("elevation_color_ramp_vert",newosg::Shader(osg::Shader::VERTEX,vs));vp->setShader("elevation_color_ramp_frag", newosg::Shader(osg::Shader::FRAGMENT, fs));//将纹理作为uniform传递给着色器osg::StateSet*ss =mapNode->getOrCreateStateSet();ss->setTextureAttributeAndModes(1, colorRampTex,osg::StateAttribute::ON);ss->addUniform(newosg::Uniform("oe_elevation_ramp",1)); //纹理单元1//添加虚拟程序到状态集ss->setAttributeAndModes(vp,osg::StateAttribute::ON);//获取地形引擎节点osgEarth::TerrainEngineNode*terrainEngine= mapNode->getTerrainEngine();terrainEngine->getStateSet()->setAttributeAndModes(vp, osg::StateAttribute::ON);
}//在创建MapNode后调用osg::Node*createScene()
{//创建Map对象osgEarth::Map*map =new osgEarth::Map();//添加高程图层(如之前代码)osgEarth::GDALElevationLayer::Options elevationOptions;elevationOptions.url()= ".././elevation/dem_90m.tif";osgEarth::GDALElevationLayer* elevLayer =new osgEarth::GDALElevationLayer(elevationOptions);map->addLayer(elevLayer);//创建MapNodeosgEarth::MapNode* mapNode =new osgEarth::MapNode(map);//添加高程颜色映射效果addElevationColorRampEffect(mapNode);return mapNode;}```着色器代码:我们需要两个着色器文件:`elevation_color_ramp.vert`和`elevation_color_ramp.frag`。我们将它们放在与代码相同的目录,或者通过osgEarth的shader包机制加载。
**elevation_color_ramp.vert**:
```glsl//osgEarth内置的顶点着色器已经计算了高程,我们只需要传递高程值到片段着色器varying floatoe_elevation;void main()
{//调用内置的顶点处理函数vec4 vertex= gl_Vertex;vec3normal =gl_Normal;//计算世界坐标(使用osgEarth的模型矩阵)vec4 vertex_view= gl_ModelViewMatrix* vertex;//传递高程值(假设在模型坐标中,顶点的高度即为高程)oe_elevation =vertex.z;//注意:这里假设地形在模型坐标中,z轴表示高程。实际中可能需要转换。gl_Position =gl_ModelViewProjectionMatrix *vertex;}
```**elevation_color_ramp.frag**:```glsluniform sampler2Doe_elevation_ramp;varyingfloat oe_elevation;void main()
{//归一化高程值(假设高程范围0~1000,这里需要根据实际调整)float minElevation=0.0;floatmaxElevation =1000.0;floatnormalizedElevation =(oe_elevation -minElevation)/ (maxElevation -minElevation);normalizedElevation= clamp(normalizedElevation,0.0,1.0);//从颜色渐变纹理中采样vec4 color= texture2D(oe_elevation_ramp, vec2(normalizedElevation,0.0));gl_FragColor =color;}
```注意:上面的着色器是一个简化的例子。实际中,高程值需要从地形中获取,并且可能需要通过统一变量(uniform)传递高程范围。此外,顶点着色器中的高程计算可能需要根据实际地形进行调整。为了更精确地控制高程范围,我们可以通过uniform变量传递最小和最大高程值。改进的片段着色器:```glsluniform sampler2Doe_elevation_ramp;uniform floatoe_min_elevation;
uniformfloat oe_max_elevation;
varying floatoe_elevation;
voidmain(){//归一化高程值float normalizedElevation= (oe_elevation- oe_min_elevation) /(oe_max_elevation- oe_min_elevation);normalizedElevation =clamp(normalizedElevation,0.0,1.0);//从颜色渐变纹理中采样vec4 color= texture2D(oe_elevation_ramp, vec2(normalizedElevation,0.0));gl_FragColor =color;}
```然后在C++代码中设置这两个uniform:```cpp//在addElevationColorRampEffect函数中,添加:ss->addUniform(newosg::Uniform("oe_min_elevation",0.0f));ss->addUniform(new osg::Uniform("oe_max_elevation",1000.0f));
```这样,我们就实现了在osgEarth中加载高程TIFF文件并显示高程颜色图。注意:实际的高程范围需要根据你的数据来设定,可以通过GDALElevationLayer获取实际的高程范围。获取高程范围:```cpposgEarth::ElevationLayer::ElevationMinMaxminmax= elevLayer->getMinMaxElevation();floatminElev=minmax.minElevation();float maxElev =minmax.maxElevation();```然后设置uniform:```cppss->addUniform(newosg::Uniform("oe_min_elevation", minElev));ss->addUniform(new osg::Uniform("oe_max_elevation",maxElev));
```这样,颜色映射就会根据实际的高程范围进行归一化。总结:我们通过创建自定义的着色器程序,将高程值映射到颜色渐变纹理上,实现了高程颜色图的显示。注意:如果地形有多个图层,可能需要考虑如何合并高程值。这里我们只使用了一个高程图层。如果遇到问题,可以检查着色器是否编译成功,以及纹理单元是否正确绑定。最后,将createScene返回的节点设置为场景根节点即可。以上就是在已有高程图层基础上显示高程颜色图的方法。
§§相关问题§§:1.如何动态改变高程颜色映射的渐变颜色?
2.如何在osgEarth中同时显示高程颜色图和卫星影像?3.如何优化着色器以处理非常大的高程范围?
4.如何将高程颜色图与地形光照效果结合?</think>在已有代码基础上使用 `osgEarth::GDALElevationLayer` 加载高程 TIFF 文件后,要显示高程颜色图(高程渐变着色),需要添加一个颜色映射效果。以下是完整的解决方案:
### ✅ 解决方案:添加高程颜色映射
```cpp
#include <osgEarth/MapNode>
#include <osgEarth/Color>
#include <osgEarth/ShaderLoader>
#include <osgEarth/TerrainEngineNode>
#include <osgEarth/TextureCompositor>
#include <osgEarth/ColorRamp>
#include <osgEarth/Shaders>
#include <osgEarth/Registry>
#include <osgEarth/Capabilities>
// 创建高程颜色映射效果
osg::Node* createElevationColorMap(osgEarth::MapNode* mapNode)
{
// 1. 创建颜色渐变
osgEarth::ColorRamp ramp;
ramp.add(osgEarth::Color::Blue, 0.0f); // 低高程 - 蓝色
ramp.add(osgEarth::Color::Green, 0.3f); // 中高程 - 绿色
ramp.add(osgEarth::Color::Yellow, 0.6f); // 中高程 - 黄色
ramp.add(osgEarth::Color::Red, 1.0f); // 高高程 - 红色
// 2. 创建着色器程序
osgEarth::VirtualProgram* vp = new osgEarth::VirtualProgram();
vp->setName("elevation_colormap");
// 3. 添加着色器代码
std::string vs = R"(
#version 330
in vec3 osg_Vertex;
out float oe_terrain_elev;
void main() {
gl_Position = gl_ModelViewProjectionMatrix * vec4(osg_Vertex, 1.0);
oe_terrain_elev = osg_Vertex.z;
}
)";
std::string fs = R"(
#version 330
uniform sampler2D oe_elevation_ramp;
uniform float oe_min_elev;
uniform float oe_max_elev;
in float oe_terrain_elev;
out vec4 fragColor;
void main() {
// 归一化高程值 (0.0-1.0)
float normalizedElev = (oe_terrain_elev - oe_min_elev) / (oe_max_elev - oe_min_elev);
normalizedElev = clamp(normalizedElev, 0.0, 1.0);
// 从渐变纹理采样颜色
vec4 color = texture(oe_elevation_ramp, vec2(normalizedElev, 0.0));
fragColor = color;
}
)";
vp->setShader("elevation_colormap_vert", new osg::Shader(osg::Shader::VERTEX, vs));
vp->setShader("elevation_colormap_frag", new osg::Shader(osg::Shader::FRAGMENT, fs));
// 4. 创建渐变纹理
osg::Image* image = ramp.createImage(256);
osg::Texture2D* rampTex = new osg::Texture2D();
rampTex->setImage(image);
rampTex->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
rampTex->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
// 5. 添加到状态集
osg::StateSet* ss = mapNode->getOrCreateStateSet();
ss->setTextureAttributeAndModes(0, rampTex, osg::StateAttribute::ON);
ss->addUniform(new osg::Uniform("oe_elevation_ramp", 0)); // 纹理单元0
ss->addUniform(new osg::Uniform("oe_min_elev", 0.0f)); // 最小高程值
ss->addUniform(new osg::Uniform("oe_max_elev", 1000.0f)); // 最大高程值
ss->setAttributeAndModes(vp, osg::StateAttribute::ON);
return mapNode;
}
// 在您的主代码中使用
osg::Group* root = new osg::Group();
// 创建地图节点
osgEarth::Map* map = new osgEarth::Map();
osgEarth::MapNode* mapNode = new osgEarth::MapNode(map);
root->addChild(mapNode);
// 添加高程层(您的代码)
osgEarth::GDALElevationLayer::Options elevationOptions;
elevationOptions.url() = ".././elevation/dem_90m.tif";
osgEarth::GDALElevationLayer* elevLayer = new osgEarth::GDALElevationLayer(elevationOptions);
map->addLayer(elevLayer);
// 添加颜色映射效果
root->addChild(createElevationColorMap(mapNode));
// 添加到查看器
osgViewer::Viewer viewer;
viewer.setSceneData(root);
```
### 🔍 代码解释:
1. **颜色渐变创建**:
- 使用 `osgEarth::ColorRamp` 定义从低到高的颜色渐变
- 添加了蓝→绿→黄→红的颜色过渡
2. **着色器程序**:
- **顶点着色器**:传递地形高程值
- **片段着色器**:根据高程值从渐变纹理采样颜色
- 使用 `normalizedElev` 将高程值归一化到 0-1 范围
3. **渐变纹理**:
- 将颜色渐变转换为 256×1 的纹理
- 设置纹理过滤模式为线性插值
4. **状态设置**:
- 将渐变纹理绑定到纹理单元0
- 设置高程范围统一变量(需根据实际数据调整)
- 启用着色器程序
### ⚙️ 高程范围调整:
要获取实际高程范围,可在加载高程层后添加:
```cpp
// 获取实际高程范围
osgEarth::ElevationLayer::ElevationMinMax minmax = elevLayer->getMinMaxElevation();
float minElev = minmax.minElevation();
float maxElev = minmax.maxElevation();
// 更新统一变量
ss->getUniform("oe_min_elev")->set(minElev);
ss->getUniform("oe_max_elev")->set(maxElev);
```
### 🌈 自定义颜色方案:
修改 `ColorRamp` 创建不同颜色方案:
```cpp
// 冰川方案
ramp.add(osgEarth::Color(0.8, 0.9, 1.0), 0.0f); // 淡蓝
ramp.add(osgEarth::Color(0.6, 0.8, 1.0), 0.3f); // 中蓝
ramp.add(osgEarth::Color(0.9, 0.95, 1.0), 0.7f); // 淡青
ramp.add(osgEarth::Color::White, 1.0f); // 白色
// 沙漠方案
ramp.add(osgEarth::Color(0.9, 0.8, 0.5), 0.0f); // 沙色
ramp.add(osgEarth::Color(0.7, 0.5, 0.3), 0.5f); // 棕色
ramp.add(osgEarth::Color(0.5, 0.3, 0.2), 1.0f); // 深棕
```
###