第一章:Shader入门——从零开始认识着色器
大家好,欢迎来到《Unity着色器开发:从基础到进阶》的第一章。
说实话,我刚开始学Shader的时候,也是一头雾水。什么顶点、片段、渲染管线……听着就头大。但别担心,这一章我会用最直白的方式,带你搞懂Shader到底是什么、它在渲染管线里扮演什么角色、以及Unity里的Shader长什么样。
1.1 什么是Shader?
Shader,中文叫「着色器」。说白了,它就是一段运行在GPU上的小程序。
它的任务很简单:告诉GPU怎么给每个像素上色。
你想想看,游戏里一个3D模型,从一堆顶点数据变成屏幕上五彩斑斓的画面,中间经历了什么?嗯,就是Shader在干活。
我个人习惯把Shader比作「画家」。模型是画布,纹理是颜料,而Shader就是那个拿着画笔的人。它决定了哪里该亮、哪里该暗、哪里该反射、哪里该透明。
核心要点:Shader = 运行在GPU上的、控制渲染效果的代码。
我在项目中遇到过不少新手,以为Shader是某种神秘的黑魔法。其实不是。它就是一种工具,跟C#脚本一样,只不过它跑在GPU上,而且更贴近图形硬件。
1.2 Shader在渲染管线中的位置
要理解Shader,必须先理解渲染管线。
渲染管线,说白了就是「从3D数据到2D屏幕」的流水线。它分很多阶段,但Shader主要参与其中两个关键阶段:
| 阶段 | Shader类型 | 干什么的 |
|---|---|---|
| 顶点处理 | 顶点着色器(Vertex Shader) | 处理每个顶点的位置、法线、UV等 |
| 片段处理 | 片段着色器(Fragment Shader) | 计算每个像素的最终颜色 |
整个流程大致是这样的:
- 输入装配:把模型的顶点数据送进GPU
- 顶点着色器:对每个顶点做变换(比如把模型从局部空间转到屏幕空间)
- 光栅化:把顶点连成三角形,再拆成一个个像素
- 片段着色器:对每个像素计算颜色
- 输出合并:处理深度测试、透明度混合等,最终输出到屏幕
你看,Shader就插在第二步和第四步。这两个阶段是我们可以编程控制的。其他阶段?嗯,硬件帮你搞定了,你改不了。
小提示:Unity里你写的Shader代码,主要就是顶点着色器和片段着色器。其他阶段Unity帮你封装好了。
1.3 Unity Shader的基本结构
好,现在咱们来看看Unity里的Shader到底长什么样。
Unity的Shader文件,后缀是.shader。它用一种叫ShaderLab的语言来写。ShaderLab是Unity自己封装的一套标记语言,里面可以嵌套HLSL(或CG)代码。
一个最基本的Unity Shader结构是这样的:
Shader "Custom/MyFirstShader"
{
Properties
{
_MainTex ("纹理", 2D) = "white" {}
_Color ("颜色", Color) = (1,1,1,1)
}
SubShader
{
Tags { "RenderType"="Opaque" }
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _Color;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv) * _Color;
return col;
}
ENDCG
}
}
}
看着有点长?别怕,我拆开给你讲。
1.3.1 Shader名称
第一行:Shader "Custom/MyFirstShader"
这个字符串就是Shader在Unity材质面板里显示的名字。斜杠表示层级,比如"Custom/..."会出现在Custom菜单下。
1.3.2 Properties(属性块)
Properties定义了这个Shader暴露给编辑器的参数。比如纹理、颜色、数值等。
格式很简单:变量名 ("显示名称", 类型) = 默认值
我个人习惯把常用的属性放在前面,比如纹理、颜色、光滑度。这样美术同事用起来也方便。
1.3.3 SubShader(子着色器)
一个Shader可以包含多个SubShader。Unity会按顺序尝试,选第一个能跑在当前硬件上的。
SubShader里可以设置Tags(标签),比如RenderType、Queue等。这些标签告诉Unity这个Shader该怎么渲染。
注意:Tags写错了,可能会导致渲染顺序出问题。我曾经因为忘了写Queue标签,透明物体渲染得乱七八糟……嗯,排查了半天。
1.3.4 Pass(通道)
每个SubShader里可以有一个或多个Pass。每个Pass代表一次完整的渲染过程。
最简单的Shader只有一个Pass。复杂一点的,比如多Pass可以做轮廓描边、阴影等效果。
1.3.5 CGPROGRAM ... ENDCG
这就是真正的着色器代码了。用HLSL/CG语言写的。
里面包含:
- #pragma指令:指定顶点和片段着色器的入口函数名
- 结构体:定义输入输出数据的格式
- 顶点着色器函数:处理顶点变换
- 片段着色器函数:计算像素颜色
你看,其实没那么复杂。顶点着色器把顶点坐标从模型空间转到裁剪空间,片段着色器采样纹理并乘以颜色,就完事了。
避坑指南:我曾经在写顶点着色器时忘了调用UnityObjectToClipPos,结果模型全跑到屏幕外面去了。记住:顶点着色器必须把坐标转换到裁剪空间,否则GPU不知道这个顶点该画在哪。
1.4 小结
这一章我们聊了:
- Shader是运行在GPU上的小程序,控制渲染效果
- 它在渲染管线中主要负责顶点处理和片段处理
- Unity Shader由ShaderLab编写,包含Properties、SubShader、Pass等部分
下一章,我们会真正动手写第一个Shader。到时候你会发现,原来写Shader跟写C#脚本一样,有套路可循。
嗯,今天就到这里。有什么问题,欢迎随时交流。
公众号:蓝海资料掘金营,微信deep3321