跳到主要内容

着色器参数

着色器从 uniform 读取输入。引擎会自行设置许多值,例如内置着色器 uniform中的相机矩阵;StandardMaterial 也会设置自身属性对应的 uniform。应用可以在三个位置设置值:

  • 材质:供使用该材质的全部网格实例使用。
  • 网格实例:仅供该实例的绘制使用。
  • 全局:在图形设备作用域设置,供所有未自行设置值的绘制使用。

每次绘制中,网格实例值优先于材质值,材质值优先于全局值。

值可以是数字、表示向量和矩阵的数字数组,以及纹理。修改数组内容后,应重新设置数组;直接原地修改不保证被识别。

声明 uniform​

按通常方式在着色器中声明。例如,片元着色器读取 tint 颜色:

uniform vec3 tint;

void main(void) {
gl_FragColor = vec4(tint, 1.0);
}

材质参数​

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 图集。不能逐材质或网格实例替换,调试构建会警告。