Shader 基础概念
Makepad Shader
是自定义的 MPSL
着色语言。 MPSL 可以生成为 glsl 等着色语言。
推荐前置知识学习:GLSL 着色器入门教程 https://thebookofshaders.com/?lan=ch
什么是Shader
让我们先从一个简单的比喻开始理解什么是 shader。想象你是一个画家,面前有一块巨大的画布,这个画布被分成了数百万个小格子(像素)。
现在你有两个任务:
- 决定每个形状的位置和大小(顶点着色器)
- 决定每个格子应该填充什么颜色(像素/片元着色器)
1// 一个最基础的 Makepad shader
2MyFirstShader = {{MyFirstShader}} {
3 // 1. 顶点着色器 - 处理位置
4 fn vertex(self) -> vec4 {
5 // 将形状放在正确的位置
6 let position = self.geom_pos * self.rect_size + self.rect_pos;
7 return self.camera_projection * vec4(position, 0.0, 1.0);
8 }
9
10 // 2. 片元着色器 - 处理颜色
11 fn pixel(self) -> vec4 {
12 // 给每个像素上色
13 return vec4(1.0, 0.0, 0.0, 1.0); // 红色
14 }
15};
让我们可视化一下这个过程:
1顶点着色阶段: 像素着色阶段:
2
3 P1●─────●P2 ░░░░░░░░
4 │ │ ░██████░
5 │ │ => ░██████░
6 │ │ ░██████░
7 P3●─────●P4 ░░░░░░░░
8
9 (确定形状位置) (填充每个像素)
GPU vs CPU 渲染的区别
为什么要用GPU渲染?让我们通过一个实例来理解。
假设要渲染一个 1000x1000 像素的区域:
1CPU 渲染:
2- 依次处理每个像素
3- 100万个像素需要依次处理
4- 类似于用一个画笔一点点填充
5
6GPU 渲染:
7- 大量像素并行处理
8- 100万个像素可以同时处理
9- 类似于用喷枪快速覆盖
视觉化表示:
1CPU 渲染进度: GPU 渲染进度:
2█░░░░░░░░░ 10% ▒▒▒▒▒▒▒▒▒▒ 100%
3██░░░░░░░░ 20% (同时处理)
4███░░░░░░░ 30%
5...
6██████████ 100%
本质上是为了提升性能,GPU 是一个专门用于图形渲染的硬件,它可以同时处理大量像素,因此比 CPU 更适合渲染图形。
坐标系统详解
在 Makepad 中,我们需要处理三种主要的坐标系统:
归一化设备坐标(NDC)
[-1,1]范围,提供设备无关的标准化空间,确保渲染结果在不同分辨率下保持一致。
- 使用场景:当你需要进行设备无关的渲染或者处理3D变换时。
- 比如:3D渲染、视图裁剪、透视投影、跨设备一致性渲染等。
像素坐标
实际屏幕像素,用于精确定位屏幕上的实际位置,直接对应物理显示设备。
- 使用场景:当你需要精确控制渲染目标在屏幕上的确切位置时。
- 比如:UI元素的精确定位、像素边框的绘制、文本渲染的对齐等。
UV 坐标
:
[0,1]范围的纹理坐标,用于纹理映射和参数化表面, 提供独立于分辨率的相对位置。
- 使用场景:当你需要处理纹理映射或者创建参数化的视觉效果时。
- 比如:纹理映射、渐变效果、UV动画、程序化纹理生成、参数化形状等。
代码和图示来理解坐标转换
1让我们通过代码和图示来理解坐标转换:
2fn vertex(self) -> vec4 {
3 // 1. 几何坐标到像素坐标的映射
4 let pixel_pos = self.geom_pos * self.rect_size + self.rect_pos;
5
6 // 2. 像素坐标到 UV 坐标的映射
7 self.uv = (pixel_pos - self.rect_pos) / self.rect_size;
8
9 // 3. 像素坐标到 NDC 的映射
10 let ndc = self.camera_projection * vec4(pixel_pos, 0.0, 1.0);
11
12 return ndc;
13}
坐标系统的可视化:
1归一化设备坐标(NDC) 像素坐标 UV坐标
2 (-1,1) (0,0) (0,0)
3 ┃ ┃ ┃
4 ┃ ┃ ┃
5 ━━━━━━●━━━━━━ ━━━━━●━━━━━ ━━━━━━━●━━━━━━━
6 ┃ ┃ ┃
7 ┃ ┃ ┃
8 (1,-1) (width, (1,1)
9 height)
10
11坐标映射流程图:
12
13像素坐标 (100, 100) UV坐标 (0.5, 0.5) NDC (0.0, 0.0)
14 ┌──────────┐ ┌──────────┐ ┌──────────┐
15 │(0,0) │ ÷size │(0,0) │ *2-1 │(-1,1) │
16 │ │ ────────────► │ │ ────────────► │ │
17 │ ● │ │ ● │ │ ● │
18 │ │ │ │ │ │
19 │ (w,h)│ │ (1,1)│ │ (1,-1)│
20 └──────────┘ └──────────┘ └──────────┘
21
22
23需求判断:
24┌────────────────────┐
25│ 是否需要像素精度? │
26└─────────┬──────────┘
27 │
28 ┌─────┴─────┐
29 │ Yes │ No
30 ▼ ▼
31像素坐标 ┌──────────────┐
32 | 是否需要纹理 |
33 │ 或渐变? │
34 └──────┬───────┘
35 │
36 ┌────┴────┐
37 │ Yes │ No
38 ▼ ▼
39 UV坐标 NDC坐标
颜色与像素
在 Makepad 中,颜色使用 vec4 表示,包含 RGBA 四个通道:
1fn pixel(self) -> vec4 {
2 //---------- 红 绿 蓝 透明度
3 return vec4(1.0, 0.0, 0.0, 1.0);
4}
颜色混合示意:
1基础颜色: 透明度混合:
2Red (1,0,0) ██ 不透明 (alpha = 1.0)
3Green (0,1,0) + ▒▒ 半透明 (alpha = 0.5)
4Blue (0,0,1) ░░ 透明 (alpha = 0.0)
预乘Alpha的概念:
1// 普通 RGBA
2vec4(1.0, 0.0, 0.0, 0.5) // 半透明红色
3
4// 预乘 Alpha (推荐)
5vec4(0.5, 0.0, 0.0, 0.5) // RGB 通道已乘以 alpha