MPSL Shader 基本语法
MPSL 语法和 Rust 语法的区别
MPSL 着色器语言与 Rust 语言的写法类似,但它们也有一些本质区别。
类型系统的区别
MPSL 的类型系统更简单且专注于图形计算:
1// Rust 的类型系统
2let x: i32 = 42;
3let y: f64 = 3.14;
4let v: Vec<i32> = vec![1, 2, 3];
5
6// MPSL 的类型系统
7let x: float = 42.0;
8let v: vec4 = vec4(1.0, 0.0, 0.0, 1.0);
MPSL 主要关注图形相关的基本类型:float
、vec2
、vec3
、vec4
、mat4
等,而不支持 Rust 中的复杂数据结构如 Vec
、HashMap
等。这种简化是为了适应 GPU 的计算特点。
内存管理的区别
Rust 的所有权系统、默认不可变等在 MPSL 中完全不存在:
1// Rust 中的所有权和借用
2let mut s = String::from("hello");
3let r = &mut s; // 可变借用
4r.push_str(" world");
5
6// MPSL 中简单的值传递,默认可变
7let color = vec4(1.0, 0.0, 0.0, 1.0);
8let modified = color * 0.5; // 直接操作,没有所有权概念
MPSL 不需要考虑内存管理,因为着色器程序的生命周期是由 GPU 执行过程控制的。
函数定义和使用
MPSL 的函数系统更简单,主要围绕着顶点和像素着色:
1// Rust 函数定义
2fn calculate_sum(a: i32, b: i32) -> i32 {
3 a + b
4}
5
6// MPSL 着色器函数
7fn vertex(self) -> vec4 {
8 // 必须返回裁剪空间坐标
9 return self.transform_position();
10}
11
12fn pixel(self) -> vec4 {
13 // 必须返回颜色值
14 return self.color;
15}
MPSL 的 self 参数是特殊的,它包含了着色器的上下文信息,这与 Rust 的 self 概念有很大不同。
变量作用域
MPSL 引入了特殊的变量作用域概念:
1// MPSL 特有的变量声明
2instance border_width: float // 实例变量
3varying pos: vec2 // 顶点到像素着色器传递的变量
4uniform time: float // 统一变量
5
6// Rust 中的变量声明
7let border_width: f32 = 1.0; // 普通变量
8static TIME: f32 = 0.0; // 静态变量
这些特殊的变量类型反映了图形渲染管线的特点,在 Rust 中并不存在这样的概念。
控制流结构
MPSL 的控制流更受限:
1// Rust 的灵活控制流
2match value {
3 0 => println!("zero"),
4 1 => println!("one"),
5 _ => println!("other"),
6}
7
8// MPSL 的简化控制流
9if value < 0.5 {
10 return vec4(1.0, 0.0, 0.0, 1.0);
11} else {
12 return vec4(0.0, 1.0, 0.0, 1.0);
13}
MPSL 不支持 match
、loop
等 Rust 的高级控制流结构,主要是因为 GPU 执行模型的限制。
特殊功能支持
MPSL 包含了专门的图形处理功能:
1// MPSL 的 SDF 功能
2fn pixel(self) -> vec4 {
3 let sdf = Sdf2d::viewport(self.pos * self.rect_size);
4 sdf.circle(100.0, 100.0, 50.0);
5 return sdf.fill(self.color);
6}
7
8// Rust 中需要使用外部库实现类似功能
9use graphics_lib::draw_circle; // 假设的图形库
这些内置的图形处理功能是 MPSL 的特色,在 Rust 中通常需要依赖外部库。
错误处理
错误处理方式有很大差异:
1// Rust 的错误处理
2fn process() -> Result<i32, Error> {
3 let value = some_operation()?;
4 Ok(value)
5}
6
7// MPSL 基本没有错误处理
8fn pixel(self) -> vec4 {
9 // 着色器执行失败时会直接停止
10 let color = self.compute_color();
11 return color;
12}
MPSL 没有 Result 和 Option 类型,也不需要错误处理机制,因为着色器程序要求必须能够正确执行。
MPSL 基本语法
基本程序结构
MPSL 的着色器程序主要由以下部分组成:
1// 顶点着色器
2fn vertex(self) -> vec4 {
3 // 返回裁剪空间坐标
4 return self.clip_and_transform_vertex(self.rect_pos, self.rect_size);
5}
6
7// 片段(像素)着色器
8fn pixel(self) -> vec4 {
9 // 返回RGBA颜色值
10 return self.color;
11}
数据类型
MPSL 提供了以下基本数据类型:
1// 标量类型
2float // 浮点数
3int // 整数
4bool // 布尔值
5
6// 向量类型
7vec2 // 2维向量 (x,y)
8vec3 // 3维向量 (x,y,z)
9vec4 // 4维向量 (x,y,z,w) 常用于颜色(r,g,b,a)
10
11// 矩阵类型
12mat4 // 4x4矩阵
13
14// 采样器类型
15texture2d // 2D纹理
16textureOES // 外部纹理(视频等)
变量声明
1// 实例变量 - 可以针对每个绘制实例更改
2instance border_width: float
3instance color: vec4
4
5// varying变量 - 在顶点和像素着色器之间传递数据
6varying pos: vec2
7
8// uniform变量 - 全局统一的值
9uniform time: float
10
11// 纹理声明
12texture tex: texture2d
内置函数
1// 数学函数
2sin(x) // 正弦
3cos(x) // 余弦
4pow(x,y) // x的y次方
5sqrt(x) // 平方根
6mix(x,y,a) // 线性插值
7clamp(x,min,max) // 范围限制
8
9// 纹理采样
10sample2d(tex, uv) // 2D纹理采样
11sample2d_rt(tex, uv) // 渲染目标纹理采样
12sample2dOES(tex, uv) // 外部纹理采样
SDF(有符号距离场)
MPSL 提供了强大的 SDF 功能用于形状绘制:
1// 创建SDF视口
2let sdf = Sdf2d::viewport(self.pos * self.rect_size);
3
4// 基本形状
5sdf.circle(x, y, radius) // 圆形
6sdf.box(x, y, width, height, radius) // 圆角矩形
7sdf.rect(x, y, width, height) // 矩形
8
9// 操作
10sdf.fill(color) // 填充
11sdf.fill_keep(color) // 填充并保留形状
12sdf.stroke(color, width) // 描边
控制流语句
1// 条件语句
2if condition {
3 // 代码块
4} else {
5 // 代码块
6}
7
8// 循环
9for i in 0..count {
10 // 循环体
11}
12
13// 提前返回
14return value;
着色器属性
1draw_bg: {
2 // 可用的形状类型
3 shape: Solid, // 实心形状
4 shape: Rectangle, // 矩形
5 shape: Circle, // 圆形
6 shape: RoundedRect, // 圆角矩形
7
8 // 基本填充类型
9 fill: Color, // 纯色填充
10 fill: Texture, // 纹理填充
11 fill: Pattern, // 图案填充
12 fill: Gradient, // 渐变填充
13
14}
说明
- shape 和 fill 属性更像是一种声明式的标记,告诉系统和其他开发者这个着色器的意图
- 实际的绘制效果需要在 pixel 函数中通过 SDF API 来实现
实践示例
一个完整的圆角矩形着色器示例:
1// 1. 圆角矩形
2shapes = <View> {
3
4 show_bg: true, // 启用背景绘制
5 draw_bg: { // 主要的着色器配置
6 // 可以针对每个实例修改的实例变量
7 instance stroke_width: 1.0 // 描边宽度
8 instance border_width: 2.0 // 边框宽度
9 instance border_color: #000 // 边框颜色
10 instance fill_color: #f00f // 填充颜色
11
12 // 辅助函数:创建圆角矩形
13 fn rounded_rectangle(self) -> vec4 {
14 // 创建一个2D有符号距离场视口,映射到我们的矩形尺寸
15 let sdf = Sdf2d::viewport(self.pos * self.rect_size);
16
17 // 绘制一个盒子(圆角矩形),参数依次为:
18 sdf.box(
19 0.0, // x位置(左边缘)
20 0.0, // y位置(顶边缘)
21 self.rect_size.x, // 矩形宽度
22 self.rect_size.y, // 矩形高度
23 10.0 // 圆角半径 - 使角落变圆
24 );
25
26 // 用fill_color填充形状,同时保留SDF用于边框绘制
27 sdf.fill_keep(self.fill_color);
28
29 // 添加指定颜色和宽度的边框
30 sdf.stroke(self.border_color, self.border_width);
31
32 // 返回最终合成的颜色结果
33 return sdf.result;
34 }
35
36 // 主像素着色器函数
37 fn pixel(self) -> vec4 {
38 // 必须加 return
39 return self.rounded_rectangle();
40
41 }
42 }
43}