WGSL 反射
PlayCanvas 直接从 WGSL 源码反射资源:声明 uniform、纹理和存储缓冲区时无需 @group/@binding 索引,引擎会解析声明、构建绑定组格式并自动分配绑定。顶点、片元和计算着色器均使用这种简化语法。
以下介绍资源声明和反射方式。仅用于顶点/片元的属性、varying 和输出,参见 WGSL 顶点和片元着色器。
简化着色器接口语法
标准 WGSL(WebGPU 着色语言)声明 uniform、属性和 varying 等接口时,需要显式指定资源位置或绑定;为各资源配置 @group 和 @binding 容易繁琐出错,尤其是常见模式。
为改善易用性,采用类似 GLSL 的简化语法。不得手动指定 @group 或 @binding;处理过程中引擎会移除这些注解,再按预定义布局重新分配。
示例对比
标准 WGSL:
struct Uniforms {
uTime: f32,
};
struct FragmentInput {
@location(0) uv0: vec2f,
@builtin(position) position: vec4f
};
@group(0) @binding(0) var<uniform> ub: Uniforms;
@fragment fn fragmentMain(FragmentInput) -> @location(0) vec4f {
// body
}
相比之下,简化语法避免了大量样板代码。
uniform uTime: f32;
varying uv0: vec2f;
@fragment fn fragmentMain(input: FragmentInput) -> FragmentOutput {
// body
}
Uniform
Uniform 用于从引擎向着色器传递数值资源。
使用以下简化语法声明:
uniform view_position: vec3f;
uniform tints: array<vec3f, 4>;
uniform weights: array<f32, 8>;
内部自动放入 uniform 缓冲区,代码中通过 uniform. 前缀访问:
var pos = uniform.view_position;
var color = uniform.tints[2];
// f32 and vec2<> types used in an array are due to alignment requirements wrapped
// in an aligned structure, and the value is available as its `element` property.
// struct WrappedF32 { @size(16) element: f32 }
var weight = uniform.weights[3].element;
引擎在前向渲染中提供内置 uniform 的值。自定义值由应用设置,例如 material.setParameter()。
目前系统仅支持简单类型,包括 f32、i32、u32、向量和矩阵(如 vec4f、mat4x4f)。暂不支持结构体,因此所有值必须声明为独立的基本类型变量。
纹理资源
纹理资源使用简化 WGSL,必须省略各资源的 @group 和 @binding 索引。
采样纹理
WGSL 将纹理和采样器作为独立对象,GLSL 则将两者合并。
要采样纹理,即读取滤波纹素值,必须声明纹理对象,并在其后紧接着声明采样器。
// 2d texture with a sampler declaration
var diffuseMap: texture_2d<f32>;
var diffuseMapSampler: sampler;
// texture sampling
var texel = textureSample(diffuseMap, diffuseMapSampler, coords);
读取纹素
若只需原始纹素数据,不需要滤波、mipmap 或寻址模式,可用 textureLoad 替代 textureSample。这称为非滤波访问或纹素读取。
此时不需要也不允许采样器。例如:
// cubemap texture without a sampler
var noSamplerMap: texture_cube<f32>;
// fetching the texel
let texel = textureLoad(noSamplerMap, coords, mipLevel);
不可滤波纹理
WebGPU 支持不可滤波浮点纹理,常用于不允许滤波的深度纹理等特殊用途。但 WGSL 声明语法没有区分这种采样类型。为正确根据声明自动生成绑定组,引入 uff(unfilterable-float)采样类型。
使用 uff 可显式声明不可滤波浮点纹理:
// declaration
var colorMap: texture_2d<uff>;
// sampling
let data: vec4f = textureLoad(colorMap, uv, 0);
该扩展使引擎正确识别采样能力并绑定。内部会将 texture_2d<uff> 改写为最终 WGSL 的 texture_2d<f32>,并自动生成采用 SAMPLETYPE_UNFILTERABLE_FLOAT 的 BindTextureFormat。
将 uff 与采样器配对
uff 纹理也可与 sampler 配对,以使用 textureSampleLevel、textureGather 或不滤波的 textureSample。这是将深度目标作为原始浮点数据读取的标准方式,例如层次 Z、深度感知模糊或 SSAO。
var srcDepth: texture_2d<uff>;
var srcDepthSampler: sampler;
// Single-tap sampled read at an explicit mip level.
let z = textureSampleLevel(srcDepth, srcDepthSampler, uv, 0.0).r;
// 2x2 gather (returns the r channel of four adjacent texels).
let four = textureGather(0, srcDepth, srcDepthSampler, uv);
配对时,绑定的 pc.Texture 必须使用非滤波采样配置,通常为 minFilter: FILTER_NEAREST、magFilter: FILTER_NEAREST 和 *_NEAREST mip 滤波。滤波采样器仅对可滤波浮点纹理合法;与不可滤波纹理配对时,WebGPU 会在绘制阶段拒绝绑定组。
选择深度纹理绑定方式
在 WebGPU 中,深度纹理(任意 pc.PIXELFORMAT_DEPTH*)不能作为普通 texture_2d<f32> 采样,除非设备支持可选的 float32-filterable。应按用途选择声明:
| 用途 | WGSL 声明 | 采样器 |
|---|---|---|
阴影比较(textureSampleCompare*) | texture_depth_2d | sampler_comparison |
| 原始深度浮点值,不滤波(HZB、SSAO、深度感知模糊) | texture_2d<uff> | 非滤波 sampler,或使用 textureLoad 不配采样器 |
| 线性滤波深度 | texture_2d<f32> | 滤波 sampler,需要 float32-filterable 设备功能 |
绑定组格式由着色器资源声明推导。创建后修改 Shader 的 meshBindGroupFormat 没有效果,着色器处理会从源码重新生成。要控制采样类型,应使用 uff 或其他对应 WGSL 类型声明,而不是事后覆盖格式。
暂不支持 texture_external,未来会加入。
存储缓冲区
存储缓冲区是 GPU 可访问的内存资源,让着色器随机读写任意数据。WGSL 中通过 var<storage> 声明,适合粒子系统、计算数据和动态几何体等大型或结构化数据。与 uniform 不同,在适当访问权限下支持读写。
顶点着色器使用存储缓冲区的示例:
struct Particle {
position: vec3f,
velocity: vec3f,
}
// particle storage buffer in read-only mode
var<storage, read> particles: array<Particle>;
存储纹理
存储纹理让着色器无需采样器即可直接写入,以及可选地读取纹素,最常用作计算着色器输出。使用简化 texture_storage_* 语法声明,指定格式和访问模式:
// write-only storage texture (the common case for compute output)
var outputTexture: texture_storage_2d<rgba8unorm, write>;
// writing a texel
textureStore(outputTexture, vec2i(global_id.xy), color);
访问模式可为 write、read 或 read_write。读取(read / read_write)需要 device.supportsStorageTextureRead 及作者提供的 requires readonly_and_readwrite_storage_textures;,参见 WGSL 能力。
绑定的纹理必须以 storage: true 创建,参见计算着色器。