着色器参数
着色器从 uniform 读取输入。引擎会自行设置许多值,例如内置着色器 uniform中的相机矩阵;StandardMaterial 也会设置自身属性对应的 uniform。应用可以在三个位置设置值:
- 材质:供使用该材质的全部网格实例使用。
- 网格实例:仅供该实例的绘制使用。
- 全局:在图形设备作用域设置,供所有未自行设置值的绘制使用。
每次绘制中,网格实例值优先于材质值,材质值优先于全局值。
值可以是数字、表示向量和矩阵的数字数组,以及纹理。修改数组内容后,应重新设置数组;直接原地修改不保证被识别。
声明 uniform
按通常方式在着色器中声明。例如,片元着色器读取 tint 颜色:
- GLSL
- WGSL
uniform vec3 tint;
void main(void) {
gl_FragColor = vec4(tint, 1.0);
}
uniform tint: vec3f;
@fragment
fn fragmentMain(input: FragmentInput) -> FragmentOutput {
var output: FragmentOutput;
output.color = vec4f(uniform.tint, 1.0);
return output;
}
材质参数
Material.setParameter() 为使用该材质的全部网格实例设置值,Material.deleteParameter() 移除参数。ShaderMaterial 或自定义代码块的 uniform 通过这种方式提供:
material.setParameter('tint', [1, 0.5, 0]);
StandardMaterial 自行提供 diffuse、emissive 和 gloss 等属性的 uniform,应设置这些属性并调用 update()。对 material_diffuse 等对应 uniform 名称调用 setParameter() 不生效,调试构建会警告。
材质绘制结束后,其参数不会重置。后续材质若没有同名参数,会读取上一个材质设置的值。
网格实例参数
MeshInstance.setParameter() 仅为该网格实例的绘制覆盖值,MeshInstance.deleteParameter() 移除覆盖。不影响使用相同材质的其他实例:
const meshInstance = entity.render.meshInstances[0];
meshInstance.setParameter('tint', [0, 1, 0]);
网格实例也可通过 uniform 名称覆盖 StandardMaterial 属性值及纹理。该值是着色器实际接收的值,因此颜色需用线性空间,而 diffuse 属性本身接收 sRGB 颜色:
meshInstance.setParameter('material_diffuse', new Float32Array([0.8, 0.1, 0.1]));
逐实例覆盖材质数值或纹理并非低开销方案。实例会获得独立的材质 uniform 数据副本,占用额外 GPU 内存;材质或覆盖值变化时重新上传,每次绘制单独绑定。若大量实例在这些值上不同,应优先使用独立材质,或带逐实例数据的硬件实例化。
全局参数
也可以在图形设备作用域全局设置值。材质和网格实例均未自行设置时,每次绘制都使用它:
app.graphicsDevice.scope.resolve('tint').setValue([1, 0, 0]);
网格实例覆盖全局值时,仅其自身绘制使用覆盖值,后续绘制重新读取全局值。
由于材质参数在绘制后不重置,作为全局默认值的名称不应只在部分材质上设为参数。否则其他材质会读取这些材质最后设置的值,而非全局值。
计算着色器不使用全局值,每个值都设置在计算实例上,参见计算着色器。
渲染器设置的值
渲染器为每个渲染通道自行设置部分 uniform,包括内置 uniform中的相机值、灯光和雾。对这些名称在材质、网格实例或全局设置值均不生效。
在 WebGPU 上,这也适用于渲染器逐通道向 StandardMaterial 提供的纹理,例如环境图集、场景颜色和深度贴图,以及簇光照阴影和 cookie 图集。不能逐材质或网格实例替换,调试构建会警告。