第5章:Unity Shader 编写基础

本博客文章为冯乐乐著《Unity Shader 入门精要》第五章:开始 Unity Shader 学习之旅 的部分读书笔记,仅供参考,实际知识请以书中内容为准。

5.1 软件和环境

书中使用的 Unity 版本为 Unity 5.2.1 免费版,本人使用的版本为 Unity 2022.3.48f1c1 LTS 长期支持版本,对本章节的大部分代码支持良好。

如果需要学习 Unity Shader,建议使用 Jetbrains Rider IDE,可以很好地与 Unity 项目和 Shader 代码协同工作,且支持 ShaderLab 代码的显示高亮和代码提示功能。

截至本文结束,使用的开发平台为 Windows 11,可以确保代码正确运行在 Windows 平台上,暂无法验证代码在 Mac OS 平台上的正确性。

注意:Mac OS 使用的图像编程接口为 OpenGL,而 Windows 多为 DirectX。在 OpenGL 中,渲染纹理的原点 (0,0) 位于左下角,而在 DirectX 中位于左上角。

5.2 一个最简单的顶点 / 片元着色器

顶点 / 片元着色器的基本结构

一个 Unity Shader 着色器代码包含 ShaderPropertiesSubShaderFallback 等语义块,结构如下代码所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
Shader "MyShaderName"
{
Properties {
// 属性
}

SubShader {
// 针对显卡 A 的 SubShader
Pass {
// 设置渲染状态和标签

// 标志着开始 CG 代码的语句
CGPROGRAM

// 该代码片段的编译指令,下面两个为示例
#pragma vertex vert
#pragma fragment frag

// CG 代码写在这里

// 标志着结束 CG 代码的语句
ENDCG

// 其他设置
}

// 其他需要的 Pass
}

SubShader {
// 针对显卡 B 的 SubShader
}

// 上述 SubShader 都失败后,用于回调的 Unity Shader 预案
Fallback "VertexLit"
}

在这些代码块中,最重要的部分是 Pass 语义块,绝大多数代码都写在这个里面。接下来,我们就基于这个最基本的结构来实现一个最简单的着色器。

前期准备

以下是将 Shader 代码应用到 Unity 的场景中、使代码的效果体现的通用办法:

  1. 初始化场景:在 Lighting 窗口中选择 Environment 选项,将其中的 Skybox Material 设置为 None 来清除天空盒,确保 Shader 的效果不受天空盒光照的影响。

  2. 新建 Shader 脚本:可以在资产窗口中右键并选择 Create - Shader 来创建,也可以在 Rider 中直接右键“添加” - “Unity 着色器”创建。

  3. 新建材质:创建材质并将 Shader 脚本直接拖到材质上,将自定义 Shader 脚本赋给材质。

  4. 新建预览物体:用来预览 Shader 效果,这里使用球体 Sphere,在场景中创建后将材质赋给物体。

  5. 删去创建的 Shader 脚本中的全部内容,开始编程。

书写最基本的着色器代码

我们先从最简单的代码开始,这个着色器只会包含顶点转换和片元渲染功能。堪称 Shader 脚本中的 “Hello World”(至少我是这么认为的)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Shader "Unity Shader Book/Chapter 5/Simple Shader"
{
SubShader {
Pass {
CGPROGRAM

#pragma vertex vert
#pragma fragment frag

float4 vert(float4 v : POSITION) : SV_POSITION {
return mul(UNITY_MATRIX_MVP, v);
}

fixed4 frag() : SV_Target {
return fixed4(1.0, 1.0, 1.0, 1.0);
}

ENDCG
}
}
}

当你保存并回到 Unity 中查看时,如果你已经将材质赋给了物体,你会发现物体变成了纯白——没有光照体现、看上去就像二维图形的完全白色。着色器在这里什么都没干,只是单纯设置了材质的颜色。