WGSL 着色器
Rive 中的着色器让您编写自定义 WGSL 程序,这些程序在 GPU 上运行并渲染到 GPU 画布,然后可以绘制到您的 Rive 文件中。它们适用于效果、程序化图形以及超出矢量渲染器和标准节点脚本所能产生的视觉效果。
Rive 着色器使用标准 WGSL,为兼容 Rive 支持的各渲染后端而有一些限制。支持的功能与限制请参阅 WGSL 支持。
什么是着色器?
着色器是在 GPU 上运行的小型程序。GPU 擅长在多个顶点或像素上并行运行同一程序,这使得着色器非常适用于图形效果。
典型的 GPU 管线使用两个着色器函数:
- 顶点着色器 - 决定顶点绘制的位置。
- 片段着色器 - 决定这些顶点覆盖的每个像素的颜色。
创建着色器
创建一个新脚本,选择 WGSL Shader 作为类型,并将其命名为 BasicShader。
AI Agent 可以成为创建或迭代着色器代码的有用方式。描述您想要的效果,然后审查并测试文件中生成的 WGSL。
基本 WGSL 着色器
此着色器绘制一个覆盖渲染目标的大三角形。三角形超出渲染目标的部分会被裁剪,从而覆盖整个目标。这是一种称为全屏三角形(fullscreen triangle)的常见技术。
然后片段着色器使用渐变对每个像素进行着色。不需要顶点或索引缓冲区,因为三角形的顶点直接定义在着色器中。
社区示例文件:WGSL Shader
// 使用一个超大三角形实现全屏渐变,无需顶点或索引缓冲区。
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) uv: vec2<f32>,
}
@vertex
fn vs_main(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
// 超出视口范围,裁剪后恰好覆盖整个屏幕。
var pos = array<vec2<f32>, 3>(
vec2<f32>(-1.0, -1.0),
vec2<f32>(3.0, -1.0),
vec2<f32>(-1.0, 3.0),
);
let p = pos[vertexIndex];
var out: VertexOutput;
out.position = vec4<f32>(p, 0.0, 1.0);
out.uv = p * 0.5 + vec2<f32>(0.5, 0.5);
return out;
}
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
return vec4<f32>(in.uv.x, in.uv.y, 0.5, 1.0);
}
使用脚本渲染着色器
type ShaderDemo = {
-- 用于着色器输出的离屏渲染目标。
canvas: GPUCanvas?,
-- 编译后的着色器与渲染设置。
pipeline: GPUPipeline?,
}
function init(self: ShaderDemo, context: Context): boolean
-- 创建一个 200x200 的渲染目标。
self.canvas = context:gpuCanvas({ width = 200, height = 200 })
-- 获取 WGSL 着色器资源。
local shader = context:shader('BasicShader')
if shader and self.canvas then
-- 顶点与片段阶段使用同一个着色器。
self.pipeline = GPUPipeline.new({
vertex = shader,
fragment = shader,
-- 位置来自 vertex_index,因此不需要顶点缓冲区。
vertexLayout = {},
-- 与渲染目标的格式匹配。
colorTargets = { { format = self.canvas.format } },
})
end
return true
end
function draw(self: ShaderDemo, renderer: Renderer)
if self.canvas and self.pipeline then
-- 渲染到 GPU 画布。
local pass = self.canvas:beginRenderPass({
color = {
{ loadOp = 'clear', storeOp = 'store', clearColor = { 0, 0, 0, 1 } },
},
})
pass:setPipeline(self.pipeline)
-- 绘制全屏三角形。
pass:draw(3)
pass:finish()
end
if self.canvas then
local sampler = ImageSampler('clamp', 'clamp', 'bilinear')
renderer:drawImage(self.canvas.image, sampler, 'srcOver', 1)
end
end
return function(): Node<ShaderDemo>
return {
canvas = nil,
pipeline = nil,
init = init,
draw = draw,
}
end
Uniforms(统一变量)
Uniforms 将脚本中的值传递给着色器,让着色器能够响应运行时变化的值。可以使用 uniforms 传递颜色、透明度、时间或动画参数等值。
社区示例文件:Shader Uniforms
在 WGSL 着色器中定义 uniform 数据,然后将其分配给一个 bind group 和 binding。Bind group 用于组织传入着色器的资源,而 binding 标识每个组内的具体资源:
struct Uniforms {
blue: f32,
}
@group(0) @binding(0)
var<uniform> uniforms: Uniforms;
着色器随后可以像使用其他值一样使用该 uniform:
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
return vec4<f32>(in.uv.x, in.uv.y, uniforms.blue, 1.0);
}
在脚本侧,创建一个包含 uniform 数据的 GPUBuffer,并将其添加到 GPUBindGroup 中。bind group 和槽位必须与着色器中声明的 @group 和 @binding 值匹配。
在绘制之前将 bind group 绑定到渲染 pass。当 uniform 发生变化时,将新值写入其缓冲区以供着色器使用。Uniform 值可以来自脚本输入(Script Inputs),从而可以在编辑器中配置、制作动画或通过数据绑定控制。
纹理(Textures)
纹理让着色器可以读取图像数据。着色器会在一组坐标处采样纹理以获取该位置的颜色,并在计算输出时使用或修改该颜色。
社区示例文件:Shader Texture
要在 WGSL 中使用纹理,请定义 texture 和 sampler:
@group(0) @binding(1)
var srcTexture: texture_2d<f32>;
@group(0) @binding(2)
var srcSampler: sampler;
使用 textureSample 在一组 UV 坐标处从纹理读取颜色:
let color = textureSample(srcTexture, srcSampler, in.uv);
UV 坐标标识纹理中的位置,通常每个轴从 0 到 1。例如,(0, 0) 表示纹理的一个角,(1, 1) 表示对角。
在脚本侧,将 GPUTextureView 和 GPUSampler 添加到 GPUBindGroup。它们的槽位必须与着色器中声明的 @binding 值匹配。
将画板用作纹理
着色器采样的是纹理,而不是直接采样 Rive 画板。若要将着色器应用到画板,请先将画板渲染到离屏画布:
local srcRenderer = srcCanvas:beginFrame({
clearColor = Color.rgba(0, 0, 0, 0),
})
instance:draw(srcRenderer)
srcCanvas:endFrame()
调用 srcCanvas.image:view() 以获得 GPUTextureView,然后将该视图添加到着色器的 GPUBindGroup。当画板发生变化时,将其重新绘制到画布上,以保持纹理为最新。
链接的 Marketplace 示例使用此技术,通过一个用动画噪声扭曲纹理坐标的着色器来渲染画板。
着色器目标
Rive 着色器使用 WGSL 编写。导出文件时,Rive 会为选定的目标编译每个着色器,并将这些变体包含在 .riv 文件中。原始 WGSL 源码不会包含在内。
当编辑器中未选择任何内容时,检查器会显示文件级别的着色器目标选项。选择文件将使用的运行时和平台所需的目标:
- Metal (MSL)
- GLSL ES 300
- WGSL
- HLSL
- SPIR-V
WebGPU 后端直接使用 WGSL。
如果未选择某个目标,该着色器将不会针对该后端包含在文件中,文件在该后端上运行时着色器将无法加载。

WGSL 支持
Rive 使用标准 WGSL,为确保着色器在支持的渲染后端上正常工作而有一些限制。Rive 支持顶点着色器和片段着色器,最多支持四个 bind group。不支持 compute 着色器和 override 声明。部分 WGSL 特性也不可用,包括 64 位浮点和整数类型、push constants、subgroup 操作以及非一致资源索引(non-uniform resource indexing)。