WebGL后处理与Cesium后处理阶段

发布于:2024-12-18 ⋅ 阅读:(131) ⋅ 点赞:(0)

Cesium 中,Material 是一个强大的工具,用于定义几何体外观。它允许开发者通过 顶点着色器(Vertex Shader)和 片段着色器(Fragment Shader)实现自定义效果。以下将从 Material 架构着色器编程、以及 GPU 与 CPU 数据传递 等方面进行详解。


1. Cesium 的 Material 架构

在 Cesium 中,Material 是通过 GLSL 代码(WebGL 的着色器语言)实现的。Cesium 提供了一些预定义的 Material 类型(例如 ColorMaterialProperty),也允许用户定义自定义着色器。

Material 的构成
  1. 顶点着色器(Vertex Shader):处理每个顶点的逻辑,计算顶点的变换(位置、法线等)。
  2. 片段着色器(Fragment Shader):处理每个片元的逻辑,定义像素的颜色、纹理等属性。
  3. Uniforms:从 CPU 向 GPU 传递的全局数据,通常是不变的值(如时间、模型矩阵)。
  4. Varyings:从顶点着色器传递到片段着色器的中间数据,用于共享信息。
  5. Attributes:每个顶点的数据(如位置、法线、颜色等)。

2. 自定义 Material 示例

以下是创建一个动态颜色渐变 Material 的示例。

// 创建自定义材质
const customMaterial = new Cesium.Material({
    fabric: {
        type: 'CustomMaterial', // 自定义类型名
        uniforms: {
            u_time: 0.0, // 时间参数(Uniform)
            u_color1: new Cesium.Color(1.0, 0.0, 0.0, 1.0), // 起始颜色
            u_color2: new Cesium.Color(0.0, 0.0, 1.0, 1.0), // 结束颜色
        },
        source: `
            // 顶点着色器代码
            czm_material czm_getMaterial(czm_materialInput materialInput) {
                czm_material material = czm_getDefaultMaterial(materialInput);
                float mixRatio = abs(sin(u_time)); // 动态变化的混合因子
                material.diffuse = mix(u_color1.rgb, u_color2.rgb, mixRatio); // 颜色渐变
                material.alpha = 1.0; // 不透明
                return material;
            }
        `,
    },
});

// 创建带自定义材质的实体
viewer.entities.add({
    position: Cesium.Cartesian3.fromDegrees(0, 0, 0),
    ellipsoid: {
        radii: new Cesium.Cartesian3(500000.0, 500000.0, 500000.0),
        material: customMaterial, // 应用自定义材质
    },
});

// 更新时间参数
viewer.clock.onTick.addEventListener(() => {
    customMaterial.uniforms.u_time += viewer.clock.deltaTime;
});
代码说明
  1. fabric 定义了材质结构。
  2. uniforms 是从 CPU 传递到 GPU 的参数,支持动态更新。
  3. source 是 GLSL 着色器代码,定义了颜色渐变逻辑。

3. 顶点着色器详解

顶点着色器的主要作用是处理顶点数据并计算最终的顶点位置。

关键点
  1. 输入
    • attributes:顶点属性,如位置、法线、纹理坐标。
    • uniforms:全局常量参数。
  2. 输出
    • gl_Position:顶点在屏幕上的位置。
    • varyings:传递给片段着色器的数据。
示例
attribute vec3 position;
attribute vec3 normal;

uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;

varying vec3 vNormal;

void main() {
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    vNormal = normal;
}

4. 片段着色器详解

片段着色器负责计算每个像素的颜色。

关键点
  1. 输入
    • varyings:从顶点着色器传递的数据。
    • uniforms:全局参数。
  2. 输出
    • gl_FragColor:最终片元颜色。
示例
varying vec3 vNormal;

uniform vec3 lightDirection;

void main() {
    float brightness = max(dot(normalize(vNormal), normalize(lightDirection)), 0.0);
    gl_FragColor = vec4(vec3(brightness), 1.0);
}

5. GPU 和 CPU 之间的数据传递

Cesium 提供了友好的接口来传递数据:

  1. Uniforms

    • 用于传递全局数据,如时间、颜色、纹理等。
    • Material 中通过 uniforms 字段定义。
    • 动态更新方法:
      customMaterial.uniforms.u_time = newValue;
      
  2. Attributes

    • 每个顶点的数据,如位置、法线。
    • Cesium 自动处理基本的顶点数据,但你也可以通过 Geometry 自定义。
  3. Textures

    • Cesium 支持将纹理传递给 GPU,通常通过 Uniform 实现。
    • 示例:
      const texture = new Cesium.Texture({
          context: viewer.scene.context,
          source: imageElement,
      });
      customMaterial.uniforms.u_texture = texture;
      

6. 着色器编程中的关键概念

1. 模型-视图-投影矩阵
  • 用于将世界坐标系的顶点转换到屏幕坐标系。
  • Cesium 自动为大多数几何体处理这些矩阵。
2. 法线和光照
  • 在顶点着色器中计算法线,并传递给片段着色器以实现光照效果。
3. 颜色混合
  • 使用 GLSL 的 mix 函数,可以实现颜色渐变或插值。
4. 动态效果
  • 利用时间参数(u_time)可以实现波浪、脉冲等动态视觉效果。

7. 实现复杂效果的技巧

1. 多纹理混合

将多个纹理通过自定义逻辑混合,创建复杂的表面外观。

uniform sampler2D texture1;
uniform sampler2D texture2;
uniform float mixRatio;

void main() {
    vec4 color1 = texture2D(texture1, gl_TexCoord[0].st);
    vec4 color2 = texture2D(texture2, gl_TexCoord[0].st);
    gl_FragColor = mix(color1, color2, mixRatio);
}
2. 法线贴图

使用法线贴图来模拟复杂的表面细节。


总结

  • 顶点着色器:处理顶点位置,计算中间数据。
  • 片段着色器:处理像素颜色,定义最终的视觉效果。
  • GPU-CPU 数据传递:通过 Uniforms 和 Attributes 实现,Cesium 提供了友好的接口。

通过以上知识,可以在 Cesium 中实现丰富的自定义渲染效果!
Cesium 的后处理(Post-Processing)阶段是指在场景渲染完成后,对已经生成的帧缓冲区(Frame Buffer)进行处理,以添加视觉效果。常见的后处理效果包括 泛光景深色调映射抗锯齿模糊 等。

Cesium 支持后处理的实现主要通过 PostProcessStage,同时允许开发者自定义后处理管线。


1. 后处理的基础架构

渲染管线

Cesium 的渲染分为以下几个阶段:

  1. 几何阶段:将 3D 场景的几何数据通过顶点着色器和片段着色器渲染到帧缓冲区。
  2. 光照阶段:应用光照模型计算像素颜色。
  3. 后处理阶段:对帧缓冲区的结果应用额外的效果。
后处理的实现方式

Cesium 的后处理通过以下方式实现:

  • 使用 PostProcessStage 对帧缓冲区中的像素进行操作。
  • 使用 PostProcessStageComposite 将多个后处理阶段组合成一个管线。

2. Cesium 内置的后处理效果

Cesium 提供了一些常见的后处理效果:

1. 泛光(Bloom)

为高亮部分添加光晕效果。

const bloom = viewer.scene.postProcessStages.bloom;
bloom.enabled = true;
bloom.threshold = 0.8;  // 高亮阈值
bloom.intensity = 2.0;  // 光晕强度
2. 景深(Depth of Field)

模糊远处或近处的对象,模拟相机对焦效果。

const depthOfField = Cesium.PostProcessStageLibrary.createDepthOfFieldStage();
viewer.scene.postProcessStages.add(depthOfField);
depthOfField.enabled = true;
depthOfField.uniforms.focalDistance = 200.0; // 对焦距离
depthOfField.uniforms.delta = 1.5;          // 模糊强度
depthOfField.uniforms.sigma = 2.0;
depthOfField.uniforms.stepSize = 5.0;
3. 色调映射(Tonemapping)

调整场景的亮度和对比度。

const tonemapping = Cesium.PostProcessStageLibrary.createTonemappingStage();
viewer.scene.postProcessStages.add(tonemapping);
tonemapping.enabled = true;
tonemapping.uniforms.brightness = 1.2; // 亮度
4. FXAA 抗锯齿

通过快速近似抗锯齿算法减少锯齿。

const fxaa = Cesium.PostProcessStageLibrary.createFXAA();
viewer.scene.postProcessStages.add(fxaa);
fxaa.enabled = true;

3. 自定义后处理效果

Cesium 允许通过自定义 GLSL 着色器创建新的后处理效果。

示例:实现灰度效果

将场景渲染为灰度图像。

const grayScaleStage = new Cesium.PostProcessStage({
    name: 'customGrayScale', // 阶段名称
    fragmentShader: `
        uniform sampler2D colorTexture;
        varying vec2 v_textureCoordinates;

        void main() {
            vec4 color = texture2D(colorTexture, v_textureCoordinates);
            float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114)); // 灰度公式
            gl_FragColor = vec4(vec3(gray), 1.0);
        }
    `,
});

// 添加到场景
viewer.scene.postProcessStages.add(grayScaleStage);
代码解读
  • uniform sampler2D colorTexture: 传入当前帧缓冲区的纹理。
  • v_textureCoordinates: 当前像素的纹理坐标。
  • texture2D: 从帧缓冲区中取出像素颜色。

4. 后处理阶段的管线组合

Cesium 提供了 PostProcessStageComposite,用于将多个后处理阶段组合为一个管线。

示例:组合灰度和泛光
const grayScale = new Cesium.PostProcessStage({
    name: 'grayScale',
    fragmentShader: `
        uniform sampler2D colorTexture;
        varying vec2 v_textureCoordinates;
        void main() {
            vec4 color = texture2D(colorTexture, v_textureCoordinates);
            float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114));
            gl_FragColor = vec4(vec3(gray), 1.0);
        }
    `,
});

const bloom = Cesium.PostProcessStageLibrary.createBloomStage();

const compositeStage = new Cesium.PostProcessStageComposite({
    stages: [grayScale, bloom],
});

// 添加到场景
viewer.scene.postProcessStages.add(compositeStage);

5. GPU 与 CPU 数据交互

Uniforms

用于从 CPU 传递数据到 GLSL 着色器中,适合传递全局参数,例如时间、亮度。

示例
const customStage = new Cesium.PostProcessStage({
    name: 'timeBasedEffect',
    fragmentShader: `
        uniform sampler2D colorTexture;
        uniform float u_time;
        varying vec2 v_textureCoordinates;

        void main() {
            vec4 color = texture2D(colorTexture, v_textureCoordinates);
            float wave = abs(sin(u_time));
            gl_FragColor = vec4(color.rgb * wave, 1.0);
        }
    `,
    uniforms: {
        u_time: 0.0,
    },
});

// 动态更新时间
viewer.scene.postProcessStages.add(customStage);
viewer.clock.onTick.addEventListener(() => {
    customStage.uniforms.u_time += viewer.clock.deltaTime;
});
Attributes

Cesium 的后处理阶段通常操作整个屏幕像素,因此主要通过 uniforms 传递全局数据。


6. 性能优化

  1. 避免过多的后处理阶段
    • 每个后处理阶段都会增加 GPU 的负载,尽量减少不必要的阶段。
  2. 降低分辨率
    • 可以通过降低后处理阶段的纹理分辨率来提高性能。
  3. 合并后处理阶段
    • 使用 PostProcessStageComposite 合并多个阶段,减少渲染开销。

总结

Cesium 的后处理阶段是对场景渲染的补充和增强,通过 PostProcessStage 和 GLSL 着色器可以实现丰富的视觉效果。以下是关键点:

  • 内置后处理效果:如泛光、景深、抗锯齿等。
  • 自定义效果:通过 GLSL 自定义片段着色器实现独特的视觉效果。
  • 性能优化:合理管理后处理阶段数量和复杂度。

熟练使用后处理技术,能极大提升 Cesium 应用的视觉表现力!
PostProcessStage 类是 Cesium 中用于实现后处理效果的核心类。后处理效果是指在渲染过程的最后阶段对场景进行的一些图像处理,例如泛光(bloom)、景深(depth of field)、色调映射(tonemapping)等。

PostProcessStage 类概述

PostProcessStage 允许开发者对渲染的帧进行后处理操作。它通过使用 GLSL 片段着色器(fragment shader)来改变场景的渲染结果。开发者可以通过 PostProcessStage 类来实现自定义的后处理效果,或者使用 Cesium 提供的内置后处理阶段。


1. PostProcessStage 类的结构和属性

主要属性
  1. name (String):

    • 后处理阶段的名称,用于标识该阶段。
    • 默认值为 ""
  2. fragmentShader (String):

    • 自定义的 GLSL 片段着色器代码,用于处理图像的像素。
    • 如果没有指定 fragmentShader,则需要设置 uniformstextures
  3. uniforms (Object):

    • 一个对象,用于传递给着色器的参数。参数是从 CPUGPU 的数据,通常是全局数据,如时间、颜色等。
    • 例如:
      uniforms: {
          u_time: 0.0, // 时间参数
      }
      
  4. enabled (Boolean):

    • 设置是否启用该后处理阶段。
    • 默认为 true,即启用。
  5. clearColor (Color):

    • 如果该后处理阶段的渲染输出区域需要清除,可以使用该属性指定清除颜色。
    • 默认为 undefined,表示不清除。
  6. inputTexture (Texture):

    • 输入纹理,通常为当前场景的帧缓冲区纹理。
    • 默认情况下,PostProcessStage 会使用 Cesium 的默认场景纹理。

2. 创建和应用后处理阶段

在 Cesium 中创建后处理阶段的步骤通常如下:

  1. 定义自定义着色器
    编写一个自定义的 GLSL 片段着色器,处理图像的颜色、模糊、变换等。

  2. 将后处理阶段添加到场景
    使用 viewer.scene.postProcessStages.add() 方法将后处理阶段添加到场景中。

示例:自定义后处理效果(灰度化)
const grayScaleStage = new Cesium.PostProcessStage({
    name: 'customGrayScale',
    fragmentShader: `
        uniform sampler2D colorTexture;
        varying vec2 v_textureCoordinates;

        void main() {
            vec4 color = texture2D(colorTexture, v_textureCoordinates);
            float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114)); // 灰度公式
            gl_FragColor = vec4(vec3(gray), 1.0);
        }
    `,
    uniforms: {
        colorTexture: viewer.scene.frameState.context.getFramebufferTexture(),
    }
});

// 将自定义灰度化后处理阶段添加到场景
viewer.scene.postProcessStages.add(grayScaleStage);
代码解读:
  • fragmentShader:自定义 GLSL 代码,将颜色转换为灰度。
  • uniforms:将当前的帧缓冲纹理传递给着色器,colorTexture 是后处理阶段的输入纹理。
  • PostProcessStage 创建后添加到场景中

3. 内置的后处理阶段

Cesium 提供了一些常用的内置后处理效果,可以直接在场景中启用。这些内置阶段通过 PostProcessStageLibrary 提供。

常见的内置后处理效果:
  1. Bloom(泛光)
    用于突出显示高亮区域,模拟光晕效果。

    const bloom = viewer.scene.postProcessStages.bloom;
    bloom.enabled = true;
    bloom.threshold = 0.8;  // 高亮阈值
    bloom.intensity = 2.0;  // 光晕强度
    
  2. FXAA(快速近似抗锯齿)
    用于减少图像的锯齿现象。

    const fxaa = Cesium.PostProcessStageLibrary.createFXAA();
    viewer.scene.postProcessStages.add(fxaa);
    fxaa.enabled = true;
    
  3. Depth of Field(景深)
    模拟相机的景深效果,模糊远近对象。

    const depthOfField = Cesium.PostProcessStageLibrary.createDepthOfFieldStage();
    viewer.scene.postProcessStages.add(depthOfField);
    depthOfField.enabled = true;
    depthOfField.uniforms.focalDistance = 200.0; // 对焦距离
    depthOfField.uniforms.delta = 1.5;          // 模糊强度
    depthOfField.uniforms.sigma = 2.0;
    
  4. Tonemapping(色调映射)
    调整场景的亮度和对比度。

    const tonemapping = Cesium.PostProcessStageLibrary.createTonemappingStage();
    viewer.scene.postProcessStages.add(tonemapping);
    tonemapping.enabled = true;
    tonemapping.uniforms.brightness = 1.2; // 亮度
    

4. 管理后处理阶段的顺序

多个后处理阶段可以通过 PostProcessStageComposite 类进行组合,按顺序处理图像。这样可以创建复杂的后处理效果。

示例:组合多个后处理阶段(灰度和泛光)
const grayScale = new Cesium.PostProcessStage({
    name: 'grayScale',
    fragmentShader: `
        uniform sampler2D colorTexture;
        varying vec2 v_textureCoordinates;
        void main() {
            vec4 color = texture2D(colorTexture, v_textureCoordinates);
            float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114));
            gl_FragColor = vec4(vec3(gray), 1.0);
        }
    `,
});

const bloom = Cesium.PostProcessStageLibrary.createBloomStage();

const compositeStage = new Cesium.PostProcessStageComposite({
    stages: [grayScale, bloom],  // 按顺序应用灰度和泛光效果
});

// 将组合后的后处理阶段添加到场景
viewer.scene.postProcessStages.add(compositeStage);
代码解读:
  • PostProcessStageComposite:将多个后处理阶段(如灰度和泛光)组合成一个处理管线。
  • stages:设置多个后处理阶段的顺序。

5. 性能优化

在使用后处理阶段时,尤其是自定义效果时,可能会面临性能问题。以下是一些优化建议:

  1. 减少后处理阶段的数量:每增加一个后处理阶段,GPU 渲染的负载就会增加,因此应尽量减少不必要的后处理阶段。
  2. 调整分辨率:可以通过将后处理阶段应用于较低分辨率的帧缓冲区,减少图像处理的开销。
  3. 批量处理:尽量将多个后处理阶段合并成一个处理管线,减少渲染状态切换。

6. 总结

PostProcessStageCesium 中一个非常重要的类,它允许开发者使用自定义的 GLSL 着色器和内置效果来处理渲染输出图像。通过 PostProcessStage,开发者可以实现各种后处理效果,如泛光、景深、色调映射等。使用 PostProcessStageComposite,可以将多个后处理阶段组合起来,创建复杂的效果。

  • 内置效果:如泛光、景深、抗锯齿等。
  • 自定义效果:通过编写 GLSL 着色器来创建新的后处理效果。
  • 性能考虑:合理控制后处理阶段的数量和复杂度。

网站公告

今日签到

点亮在社区的每一天
去签到