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 主要关注图形相关的基本类型:floatvec2vec3vec4mat4等,而不支持 Rust 中的复杂数据结构如 VecHashMap 等。这种简化是为了适应 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 不支持 matchloop 等 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}