Skip to content

WGSL 入门

WGSL(WebGPU Shading Language)是 WebGPU 标准的着色器语言,也是 Bevy 的默认着色器语言。它长得像 Rust 和 GLSL 的混血——有 structfnlet,也有 vec4<f32>@binding 这些 GPU 专属语法。

Shader 模块与导入

Bevy 的着色器不是孤立的文件。每个 .wgsl 文件是一个模块,可以声明自己的导入路径,也可以用 #import 引入其他模块的类型和函数。

wgsl
// 声明本模块的路径
#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 文件,路径用引号包裹:

wgsl
#import "shaders/my_lib.wgsl"::MY_CONSTANT

Vertex 与 VertexOutput

Bevy 在 bevy_pbr::forward_io 中定义了两个核心结构体:

  • Vertex——顶点输入,由 Mesh 数据驱动,包含 positionvec3<f32>)、normalvec3<f32>)、uvvec2<f32>)等字段,用 @location(N) 标注绑定点
  • VertexOutput——顶点着色器的输出 / 片段着色器的输入,包含插值后的 position@builtin(position))、world_positionworld_normaluv

当你的材质只覆盖片段着色器(绝大多数情况),Bevy 的默认顶点着色器会把 Vertex 变换为 VertexOutput,你直接在 @fragment 函数里接收 VertexOutput 就行。

最简片段着色器

一个"什么都不做"的片段着色器只需要三行:

wgsl
#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},编译时自动替换为正确的索引值:

wgsl
@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_time
  • bevy_pbr::mesh_functions::get_world_from_local——从实例索引获取模型矩阵
  • bevy_pbr::mesh_functions::mesh_position_local_to_clip——将本地坐标变换到裁剪空间

在自定义顶点着色器里,你需要手动调用这些函数完成坐标变换;在自定义片段着色器里,坐标变换已经由默认顶点着色器完成了,你只管用插值后的 VertexOutput

下一节,我们把这些着色器和 Rust 侧的 Material trait 连接起来。