书接上文,上文我们重点介绍了片元着色器的数据来源,这次我们来详细解析一下在 Cesium 中编写自定义着色器(特别是顶点着色器)时,可以使用的数据来源。
Cesium 为自定义着色器提供了丰富的数据输入,让你能够基于原始模型的几何信息、材质、外观以及 Cesium 特有的上下文(如时间、位置)来修改顶点。
这些数据主要通过着色器的 attributes、uniforms 和 varyings 来传递。
1. 顶点属性
顶点属性是逐顶点 的数据,它们来自原始的 3D 模型(如 glTF)或 Cesium 原生图元(如 Cesium3DTileset, Model, Primitive)。在自定义着色器中,你可以通过 attributes 对象来访问它们。
以下是一些最常用和最重要的顶点属性:
| 属性名 | 类型 | 描述 |
|---|---|---|
position |
vec3 |
模型的顶点坐标(模型空间) 。这是最关键的属性,修改它可以直接改变顶点的位置,实现动画、扭曲等效果。 |
normal |
vec3 |
顶点法线(模型空间) 。用于光照计算。如果你移动了顶点,通常也需要相应地重新计算或变换法线。 |
texCoord |
vec2 |
第一套纹理坐标。用于从纹理中采样颜色、高度图等。 |
texCoord1 |
vec2 |
第二套纹理坐标(如果模型有的话)。 |
color |
vec4 |
顶点颜色。通常是 RGBA 格式。 |
batchId |
float |
批次ID 。主要用于 Cesium3DTileset,用于区分同一个瓦片内不同对象的标识符,常与 czm_batchTable 配合使用。 |
示例代码:在自定义着色器中访问属性
arduino
const vertexShader = `
// 将属性从 JavaScript 作用域传递到 GLSL 作用域
in vec3 position;
in vec3 normal;
in vec2 texCoord;
void vertexMain() {
// 示例1:让模型在Y轴上上下波动
float offset = sin(position.x * 10.0) * 0.1; // 基于x坐标的正弦波
vec3 modifiedPosition = position + vec3(0.0, offset, 0.0);
// 将修改后的位置赋值给内置变量
positionMC = modifiedPosition; // positionMC 是 Cesium 约定的输出变量,表示模型空间坐标
// 示例2:将纹理坐标传递给片元着色器(如果需要)
v_texCoord = texCoord;
// 示例3:传递法线(如果片元着色器需要计算光照)
v_normal = normal;
}
`;
// 在 CustomShader 中配置
const customShader = new Cesium.CustomShader({
vertexShader: vertexShader,
// 定义我们使用了哪些属性
attributes: {
position: new Cesium.Attribute("vec3"),
normal: new Cesium.Attribute("vec3"),
texCoord: new Cesium.Attribute("vec2"),
},
// 定义要传递给片元着色器的 Varyings
varyings: {
v_texCoord: new Cesium.Varying("vec2"),
v_normal: new Cesium.Varying("vec3"),
},
});
2. Uniforms
Uniforms 是全局常量 ,对于所有顶点和片元都是相同的值。它们可以来自 Cesium 自动提供的 czm_ 系列内置变量,也可以是你自己定义的。
A. 内置 Uniforms (Cesium Provided)
Cesium 提供了一个庞大的内置 Uniform 库,以 czm_ 为前缀。它们在 cesium_modules 中自动可用。
常用内置 Uniforms 举例:
| Uniform 名 | 类型 | 描述 |
|---|---|---|
czm_frame |
float |
当前的帧号。可用于与时间相关的动画。 |
czm_model |
mat4 |
模型矩阵。将模型空间坐标转换为世界坐标。 |
czm_modelView |
mat4 |
模型视图矩阵。将模型空间坐标转换为眼睛坐标(相机空间)。 |
czm_projection |
mat4 |
投影矩阵。将眼睛坐标转换为裁剪坐标。 |
czm_modelViewProjection |
mat4 |
模型视图投影矩阵。将模型空间坐标直接转换为裁剪坐标。 |
czm_worldToModel |
mat4 |
世界坐标到模型坐标的逆矩阵。 |
czm_encodedCameraPositionMCD |
vec4 |
相机在模型空间中的编码位置,用于高精度渲染。 |
示例代码:使用内置 Uniform 进行变换
ini
void vertexMain() {
// 标准变换:将模型空间坐标转换为裁剪坐标
positionMC = position;
positionWC = (czm_model * vec4(position, 1.0)).xyz; // 世界坐标
gl_Position = czm_modelViewProjection * vec4(position, 1.0);
}
B. 自定义 Uniforms
你可以定义自己的 Uniforms,并在运行时从 JavaScript 更新它们的值。
示例代码:自定义时间 Uniform
ini
const vertexShader = `
uniform float u_time;
uniform float u_speed;
uniform float u_amplitude;
in vec3 position;
void vertexMain() {
// 使用自定义的 Uniforms 来控制波动效果
float offset = sin(position.x * u_speed + u_time) * u_amplitude;
vec3 modifiedPosition = position + vec3(0.0, offset, 0.0);
positionMC = modifiedPosition;
}
`;
const customShader = new Cesium.CustomShader({
vertexShader: vertexShader,
uniforms: {
u_time: {
type: Cesium.UniformType.FLOAT,
value: 0.0,
},
u_speed: {
type: Cesium.UniformType.FLOAT,
value: 5.0,
},
u_amplitude: {
type: Cesium.UniformType.FLOAT,
value: 0.2,
},
},
// ... 其他配置
});
// 在渲染循环中更新 uniform
viewer.scene.preRender.addEventListener(function (scene, time) {
customShader.setUniform("u_time", Cesium.JulianDate.toDate(time).getTime() / 1000.0);
});
3. Varyings
Varyings 本身不是数据来源 ,而是数据从顶点着色器传递到片元着色器的通道。你在顶点着色器中计算出的、需要在片元着色器中使用的逐顶点数据,必须通过 Varyings 来传递。
关键点:
- 在顶点着色器中写入 Varying。
- 在片元着色器中读取同一个 Varying(它会经过光栅化插值)。
- 必须在
CustomShader的varyings配置中声明。
示例:
上面的第一个代码示例已经展示了如何定义和使用 v_texCoord 和 v_normal 这两个 varyings。
4. 特殊数据源
A. 纹理 (Textures as Uniforms)
你可以将纹理(如高度图、噪声图)作为 Uniform 传入,并在顶点着色器中采样。虽然顶点着色器通常不大量采样纹理,但这是一个非常强大的技术,常用于:
- 顶点置换:使用高度图或噪声图来偏移顶点位置。
- 动态地形。
示例代码:使用噪声纹理
ini
uniform sampler2D u_noiseTexture;
in vec3 position;
in vec2 texCoord;
void vertexMain() {
// 采样噪声纹理(注意:texCoord 可能需要在模型表面展开)
vec4 noise = texture(u_noiseTexture, texCoord * 5.0); // 缩放纹理坐标
// 使用噪声的r通道来偏移y轴
vec3 modifiedPosition = position + vec3(0.0, noise.r * 0.5, 0.0);
positionMC = modifiedPosition;
}
在 JavaScript 中,你需要将 u_noiseTexture 定义为一个 UniformType.SAMPLER_2D 并为其设置一个图像资源。
B. 批次表 (Batch Table)
对于 3D Tiles,batchId 属性可以与 czm_batchTable 函数结合,来获取每个要素的特定属性。
ini
in float batchId;
void vertexMain() {
// 从批次表中获取一个名为 "Height" 的属性
float height = czm_batchTable_getProperty(batchId, "Height");
// 使用这个属性来影响顶点
vec3 modifiedPosition = position;
modifiedPosition.y += height;
positionMC = modifiedPosition;
}
总结
Cesium 顶点着色器的数据来源非常丰富,可以归纳为:
| 类别 | 关键点 | 主要用途 |
|---|---|---|
| Attributes | 逐顶点,来自原始模型 | 获取每个顶点的位置、法线、UV等基础信息。 |
| Built-in Uniforms | 全局,Cesium 自动提供 | 进行坐标变换、获取时间、相机位置等上下文信息。 |
| Custom Uniforms | 全局,由开发者定义 | 从 JavaScript 端传入控制参数(如时间、强度、颜色)。 |
| Textures | 作为 Uniform 传入 | 提供复杂的、基于图像的数据来影响顶点(如高度图、噪声)。 |
| Batch Table | 通过 batchId 属性访问 |
在 3D Tiles 中,根据要素ID获取其属性数据。 |
| Varyings | 顶点到片元的桥梁 | 将顶点着色器的计算结果(如新的法线、自定义数据)传递给片元着色器。 |