WGSL 入门
WGSL(WebGPU Shading Language)是 WebGPU 标准的着色器语言,也是 Bevy 的默认着色器语言。它长得像 Rust 和 GLSL 的混血——有 struct、fn、let,也有 vec4<f32>、@binding 这些 GPU 专属语法。
Shader 模块与导入
Bevy 的着色器不是孤立的文件。每个 .wgsl 文件是一个模块,可以声明自己的导入路径,也可以用 #import 引入其他模块的类型和函数。
// 声明本模块的路径
#define_import_path my_crate::my_shader
// 从 Bevy 内置模块导入类型
#import bevy_pbr::forward_io::VertexOutput#import 有两种形式:
- 子项导入:
#import bevy_pbr::forward_io::VertexOutput——只引入VertexOutput一个符号 - 模块导入:
#import bevy_pbr::forward_io——引入整个模块,使用时需用完整路径
导入路径用 :: 分隔,和 Rust 的模块路径语法一致。bevy_pbr::、bevy_render:: 是 Bevy 内置的着色器模块前缀;你也可以导入自己写的 .wgsl 文件,路径用引号包裹:
#import "shaders/my_lib.wgsl"::MY_CONSTANTVertex 与 VertexOutput
Bevy 在 bevy_pbr::forward_io 中定义了两个核心结构体:
Vertex——顶点输入,由 Mesh 数据驱动,包含position(vec3<f32>)、normal(vec3<f32>)、uv(vec2<f32>)等字段,用@location(N)标注绑定点VertexOutput——顶点着色器的输出 / 片段着色器的输入,包含插值后的position(@builtin(position))、world_position、world_normal、uv等
当你的材质只覆盖片段着色器(绝大多数情况),Bevy 的默认顶点着色器会把 Vertex 变换为 VertexOutput,你直接在 @fragment 函数里接收 VertexOutput 就行。
最简片段着色器
一个"什么都不做"的片段着色器只需要三行:
#import bevy_pbr::forward_io::VertexOutput
@fragment
fn fragment(mesh: VertexOutput) -> @location(0) vec4<f32> {
return vec4<f32>(0.2, 0.6, 1.0, 1.0);
}@fragment 标注这是一个片段入口函数。返回值带 @location(0)——它会被写入渲染目标(通常是屏幕颜色缓冲)。mesh 参数携带了顶点着色器插值下来的所有数据:UV 坐标、世界空间法线、世界空间位置等。
#{MATERIAL_BIND_GROUP} 宏
WGSL 里用 @group 和 @binding 指定资源的绑定点。Bevy 自定义材质的绑定组索引固定为 3(引擎内部约定),但写死数字不安全——如果引擎改了怎么办?Bevy 提供了一个预处理宏 #{MATERIAL_BIND_GROUP},编译时自动替换为正确的索引值:
@group(#{MATERIAL_BIND_GROUP}) @binding(0) var<uniform> material_color: vec4<f32>;这行声明了一个 uniform 变量,绑定在"材质绑定组的第 0 号槽位"。Rust 侧的 #[uniform(0)] 和这里的 @binding(0) 必须一一对应——这是你和 GPU 之间的"合同"。
访问引擎内置数据
Bevy 的着色器模块暴露了大量引擎数据。常用的有:
bevy_pbr::mesh_view_bindings::globals——全局数据,包含globals.time(启动至今的秒数,一小时后归零)和globals.delta_timebevy_pbr::mesh_functions::get_world_from_local——从实例索引获取模型矩阵bevy_pbr::mesh_functions::mesh_position_local_to_clip——将本地坐标变换到裁剪空间
在自定义顶点着色器里,你需要手动调用这些函数完成坐标变换;在自定义片段着色器里,坐标变换已经由默认顶点着色器完成了,你只管用插值后的 VertexOutput。
下一节,我们把这些着色器和 Rust 侧的 Material trait 连接起来。