Cesium中如何评估城市景观的视觉影响?答案:天际线!

366 阅读4分钟

大家好,我是日拱一卒的攻城师不浪,致力于技术与艺术的融合。这是2024年输出的第44/100篇文章。

天际线,这个场景我不晓得大家在项目开发中有没有涉及过这个概念。

简单来说,天际线效果就是让你的建筑物边缘在视觉上更加突出,就像给建筑物描了个边一样。这种效果在城市规划、建筑设计等领域非常有用。

主要应用场景如下:

  • 地理信息系统(GIS):在GIS领域,天际线分析可以用于评估城市景观的视觉影响,帮助决策者理解城市发展对景观的影响;

  • 城市规划与设计:天际线效果可以帮助规划师和设计师更清晰地展示城市的轮廓,特别是在高楼大厦较多的城市环境中,天际线效果能够突出建筑物的轮廓,增强视觉效果

在Cesium中,我们可以通过编写GLSL着色器代码来实现这个效果。

着色器代码是运行在显卡(GPU)上的,用来告诉显卡如何渲染图像。

创建天际线类

首先,我们定义了一个SkyLineAnalysis类。

export default class SkyLineAnalysis {
  constructor(viewer) {
    // 接收viewer参数
    this.viewer = viewer;
  }
}

2. 开启天际线效果

open方法中,我们首先检查是否已经开启了天际线效果,如果已经开启了,就直接返回。

如果没有开启,我们就创建一些后处理阶段(PostProcessStages),用来处理图像。

open() {
  if (this.skylineAnayStages) {
    this.silhouette.enabled = true;
    return;
  }
  // ...创建后处理阶段
  this.skylineAnayStages = this.viewer.scene.postProcessStages;
  let edgeDetection = Cesium.PostProcessStageLibrary.createEdgeDetectionStage();
}

着色器代码

接下来,我们来看最关键的部分——着色器代码。

着色器1:检测深度

uniform sampler2D colorTexture;
uniform sampler2D depthTexture;
in vec2 v_textureCoordinates;
out vec4 fragColor;

void main(void) {
  float depth = czm_readDepth(depthTexture, v_textureCoordinates);
  vec4 color = texture(colorTexture, v_textureCoordinates);
  if (depth < 1.0 - 0.000001) {
    fragColor = color;
  } else {
    fragColor = vec4(1.0, 0.0, 0.0, 1.0);
  }
}

这段代码的作用是读取深度纹理,如果深度值小于1(这里我们留了一点点误差空间),我们就保留原来的颜色,否则就将该像素渲染成红色。这个红色的像素就是我们天际线的边缘。

着色器2:混合颜色

uniform sampler2D colorTexture;
uniform sampler2D redTexture;
uniform sampler2D silhouetteTexture;
in vec2 v_textureCoordinates;
out vec4 fragColor;

void main(void) {
  vec4 redcolor = texture(redTexture, v_textureCoordinates);
  vec4 silhouetteColor = texture(silhouetteTexture, v_textureCoordinates);
  vec4 color = texture(colorTexture, v_textureCoordinates);
  if (redcolor.r == 1.0) {
    fragColor = mix(color, vec4(5.0, 0.0, 0.0, 1.0), silhouetteColor.a);
  } else {
    fragColor = color;
  }
}

这段代码的作用是混合颜色。如果检测到红色(天际线边缘),我们就将颜色混合成更深的红色,以此来突出天际线。

后处理组合

this.silhouette = new Cesium.PostProcessStageComposite({
  // PostProcessStage要按顺序执行的stage或组合的数组。
  stages: [edgeDetection, postProccessStage, postProccesStage_1],
  // 是否执行每个后处理阶段,其中一个阶段的输入是前一个阶段的输出。
  // 否则每个包含阶段的输入是组合之前执行的阶段的输出
  inputPreviousStageTexture: false,
  // 后处理阶段uniforms的别名
  uniforms: edgeDetection.uniforms
});

这段代码是Cesium中用于创建一个后处理阶段组合(PostProcessStageComposite)

PostProcessStageComposite是Cesium提供的一个类,允许我们将多个后处理阶段(PostProcessStage)组合在一起,以便按顺序执行这些阶段,从而对场景的渲染结果进行进一步的处理。

  1. stages:这是一个数组,包含了需要按顺序执行的后处理阶段。在这个例子中,我们有三个阶段:edgeDetectionpostProccessStagepostProccesStage_1。这些阶段将按照它们在数组中的顺序被执行。

  2. inputPreviousStageTexture:接收一个布尔值,用于指定每个阶段的输入纹理来源。如果设置为true,则每个阶段的输入纹理是前一个阶段的输出纹理。如果设置为false(如这段代码所示),则所有阶段的输入纹理都是相同的,即它们都是原始场景渲染到的输出纹理。

  3. uniforms:这是一组uniform变量,用于传递给着色器程序。在这个例子中,我们使用了edgeDetection阶段的uniforms。Uniforms是GLSL着色器中的一种变量类型,用于在顶点着色器和片段着色器之间传递数据。

关闭天际线效果

最后,我们还有一个close方法,用来关闭天际线效果。

close() {
  this.silhouette.enabled = false;
}

最后

以上我们解析了在Cesium中渲染天际线的主要流程,如果想要更完整的代码可以查看作者的开源项目:github.com/tingyuxuan2…

如果认为有帮助请给予我们一个star支持鼓励我们开源更多!

如果想系统学习Cesium,可以了解下作者的Cesium系列教程《Cesium从入门到实战》,将Cesium的知识点进行串联,让不了解Cesium的小伙伴拥有一个完整的学习路线,并最终完成一个智慧城市的完整项目,+作者:brown_7778(备注来意)了解教程细节。

有需要进可视化&Webgis交流群可以加我:brown_7778(备注来意)。