跳到主要内容

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_2dsampler_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 创建,参见计算着色器。