Unity Shader 超详细学习指南
Unity Shader 是一种在 GPU 上运行的微型程序,它决定了你的 3D 模型在屏幕上最终呈现的外观,学习 Shader 编程是进入图形学大门的关键一步,它能让你实现各种酷炫的视觉效果。

本教程将分为以下几个部分:
- 基础概念篇:理解 Shader 的本质和工作原理。
- Shader Graph 可视化篇:无需编写代码,轻松创建 Shader。
- ShaderLab 与 HLSL 编程篇:手写代码的核心。
- 实用案例篇:通过实例巩固所学知识。
- 学习资源推荐:继续深造的路径。
第一部分:基础概念篇
在开始之前,你必须理解几个核心概念。
1 什么是 Shader(着色器)?
Shader 是一段运行在 GPU 上的程序,它的主要任务是告诉 GPU 如何计算一个像素(片元)的颜色,它接收模型的顶点数据、纹理、光照等信息,并输出最终的像素颜色。
2 渲染流水线
想象一下,GPU 如何将一个 3D 模型变成屏幕上的 2D 图像?这个过程就是渲染流水线,主要分为三个阶段:

- 应用阶段:CPU 负责,处理逻辑、计算动画、准备要渲染的物体等,CPU 会将模型数据(顶点、法线、UV 等)发送给 GPU。
- 几何阶段:GPU 负责,处理顶点数据,进行模型视图投影变换、裁剪、光栅化等,最终将 3D 坐标转换为屏幕上的 2D 像素位置。
- 光栅化阶段:GPU 负责,确定哪些像素被哪些三角形覆盖,并调用 Shader 来计算这些像素的最终颜色。
Shader 主要在光栅化阶段工作。
3 Shader 的基本结构
一个 Shader 程序通常包含两个核心部分:
- 顶点着色器:处理每个顶点的数据,计算顶点在屏幕上的最终位置、传递光照信息给片元着色器等。
- 片元着色器:处理每个像素(或称片元)的数据,这是决定像素颜色的关键部分,它会接收顶点着色器传递过来的插值数据,并进行颜色计算。
一个形象的比喻:
- 顶点着色器:像是在给一个网格模型的每个顶点“打点”,告诉 GPU 这个点最终应该画在屏幕的哪个位置。
- 片元着色器:像是在两个点之间“填充颜色”,并决定这个填充区域的每一个像素应该是什么颜色。
4 Unity 中的 Shader 种类
Unity 提供了多种类型的 Shader,以满足不同需求:

- 内置 Standard Shader (PBR):基于物理的渲染,是目前最主流、效果最好的 Shader,它模拟了真实世界中的光照反射,能产生非常逼真的材质效果。
- URP/Lit Shader (URP/LWRP):URP (Universal Render Pipeline) 和 LWRP (Lightweight Render Pipeline) 的标准 Shader,是 Unity 未来发展的方向,性能和跨平台能力更好。
- Shader Graph:一种节点化的 Shader 创建工具,通过连接节点来生成 Shader,无需编写代码,非常适合视觉设计师和初学者。
- 内置着色器语言 (ShaderLab + HLSL/GLSL):这是传统的编写 Shader 的方式,需要直接编写代码,功能最强大,灵活性最高,也是本教程的重点。
第二部分:Shader Graph 可视化篇(零代码入门)
如果你是新手,或者不想面对复杂的代码,Shader Graph 是你最好的起点。
1 为什么选择 Shader Graph?
- 直观:所见即所得,通过拖拽节点即可看到实时效果。
- 快速:大大缩短了创建和迭代视觉效果的时间。
- 易学:基本逻辑与编程思想一致,但避免了语法错误。
2 创建第一个 Shader Graph
- 在 Project 窗口中,右键 -> Create -> Shader -> URP -> PBR Graph。
- 双击打开这个
.shadergraph文件。 - 你会看到一个空白的画布和左侧的节点窗口。
- 从左侧的 "Blackboard" 或 "Search" 窗口中,拖出一个
PBR Master节点到画布上,这是 Shader 的输出节点。 - 拖出一个
Texture Sample节点,并将一张图片赋值给它。 - 将
Texture Sample节点的RGBA输出连接到PBR Master节点的Base Color输入。 - 恭喜! 你已经创建了一个基础的纹理显示 Shader,将这个 Shader 应用到一个材质球上,再将材质球赋给一个 3D 模型,你就能看到效果了。
3 实践案例:制作一个卡通风格的 Shader
- 创建新 Graph:创建一个新的 PBR Graph。
- 使用 Step 节点:从搜索框中找到
Step节点。Step(edge, value)函数,value大于edge,输出 1,否则输出 0,这是制作硬边效果的利器。 - 获取法线信息:拖出一个
Normal Vector节点,它提供模型表面每个点的朝向。 - 计算光照:拖出一个
Dot Product(点积)节点,将Normal Vector连接到A,拖出一个View Direction(视角方向)节点连接到B,点积的结果可以用来判断这个面是朝向你还是背向你,从而模拟一个简单的光照。 - 创建阈值:连接
Dot Product的结果到Step节点的Value输入,给Edge输入一个值(0.7),大于这个值的面就是亮的,小于的就是暗的。 - 应用颜色:创建两个
Color节点,一个亮色(如白色),一个暗色(如黑色),将它们连接到一个Lerp(插值)节点的A和B,将Step节点的输出连接到Lerp的T(权重)输入。 - 连接到输出:将
Lerp节点的输出连接到PBR Master的Base Color。 - 你的模型就有了卡通风格的明暗交界线,你还可以通过调整
Edge值来控制线条的粗细。
通过这种方式,你可以组合各种节点(如数学运算、时间、噪声、采样等)来创造出几乎任何你想要的视觉效果。
第三部分:ShaderLab 与 HLSL 编程篇(核心与进阶)
这是最强大、最灵活的 Shader 开发方式,我们需要学习 Unity 的 ShaderLab 语言和图形 API HLSL (High-Level Shading Language)。
1 ShaderLab 的结构
ShaderLab 是 Unity 用来组织 Shader 文件的“壳”,它定义了 Shader 的名称、属性、渲染状态等,一个基本的 Shader 文件结构如下:
Shader "MyCustomShader/Name"
{
Properties // 在 Inspector 面板中显示的属性
{
_MainTex ("Texture", 2D) = "white" {} // 定义一个纹理属性
_Color ("Color", Color) = (1,1,1,1) // 定义一个颜色属性
}
SubShader // 一个 Shader 可以包含多个 SubShader,会依次尝试,直到找到一个能运行的
{
Tags { "RenderType"="Opaque" } // 标签,告诉渲染器如何处理这个 Shader
Pass // 一次完整的渲染过程,一个 SubShader 可以有多个 Pass
{
// 渲染状态设置
Cull Off // 关闭背面剔除
ZWrite On // 开启深度写入
HLSLPROGRAM // HLSL 代码开始
#pragma vertex vert // 声明顶点着色器函数名为 vert
#pragma fragment frag // 声明片元着色器函数名为 frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" // 引入 URP 核心库
// CBUFFER 是常量缓冲区,用于向 GPU 传递数据,性能更好
CBUFFER_START(UnityPerMaterial)
float4 _MainTex_ST; // 纹理的 Tiling 和 Offset
float4 _Color;
CBUFFER_END
// 从 Properties 中声明的变量
TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex);
// 顶点着色器的输入结构体
struct Attributes
{
float4 positionOS : POSITION; // 对象空间下的顶点位置
float2 uv : TEXCOORD0; // UV 坐标
};
// 顶点着色器输出 -> 片元着色器输入 的结构体
struct Varyings
{
float4 positionCS : SV_POSITION; // 裁剪空间下的顶点位置 (系统值)
float2 uv : TEXCOORD0; // 传递给片元着色器的 UV
};
// 顶点着色器函数
Varyings vert (Attributes input)
{
Varyings output;
output.positionCS = TransformObjectToHClip(input.positionOS.xyz); // 将顶点位置转换到裁剪空间
output.uv = TRANSFORM_TEX(input.uv, _MainTex); // 应用 UV 的 Tiling 和 Offset
return output;
}
// 片元着色器函数
float4 frag (Varyings input) : SV_Target
{
// 采样纹理
float4 col = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv);
col *= _Color; // 应用颜色
return col; // 返回最终颜色
}
ENDHLSL // HLSL 代码结束
}
}
}
2 HLSL 编程核心
- 数据类型:
float,float2,float3,float4(向量),float4x4(矩阵),TEXTURE2D,SAMPLER等。 - 内置函数:
TransformObjectToHClip,TRANSFORM_TEX,SAMPLE_TEXTURE2D等,URP/HDRP 提供了大量方便的内置函数。 - 核心函数:
- 顶点着色器:核心工作是坐标变换,将顶点从对象空间 -> 世界空间 -> 视图空间 -> 裁剪空间,Unity 的
TransformObjectToHClip帮你完成了大部分工作。 - 片元着色器:核心工作是计算颜色,最简单的就是采样纹理 (
SAMPLE_TEXTURE2D) 并返回。
- 顶点着色器:核心工作是坐标变换,将顶点从对象空间 -> 世界空间 -> 视图空间 -> 裁剪空间,Unity 的
3 实践案例:实现一个边缘发光效果
这个效果在很多游戏中都很常见,通常用后处理实现,但也可以在模型上直接实现。
思路:
- 在片元着色器中,获取当前像素的法线。
- 获取当前像素的视角方向。
- 计算法线和视角方向的点积,如果它们接近 90 度(点积接近 0),说明这个像素处于模型的边缘。
- 根据这个值,插值出基础颜色和发光颜色。
Shader 代码修改:
// 在 Properties 中添加
_EdgeColor ("Edge Color", Color) = (1,1,1,1)
_EdgeWidth ("Edge Width", Range(0, 1)) = 0.1
// 在 CBUFFER 中添加
float4 _EdgeColor;
float _EdgeWidth;
// 在 frag 函数中修改
float4 frag (Varyings input) : SV_Target
{
// 1. 采样基础颜色
float4 baseColor = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv);
// 2. 获取法线和视角方向 (URP 中有内置变量)
float3 normal = normalize(input.normalWS); // 需要从顶点着色器传递法线过来
float3 viewDir = normalize(GetViewDirection(input.positionWS)); // 需要从顶点着色器传递位置过来
// (需要修改顶点着色器来传递 normalWS 和 positionWS)
// ...
// 3. 计算边缘强度
float fresnel = 1.0 - saturate(dot(normal, viewDir)); // fresnel 效应,边缘处值大
float edgeStrength = smoothstep(0.0, _EdgeWidth, fresnel); // 使用 smoothstep 让边缘过渡更平滑
// 4. 混合颜色
float4 finalColor = lerp(baseColor, _EdgeColor, edgeStrength);
return finalColor;
}
注意:这个例子需要修改
Attributes和Varyings结构体,并在顶点着色器中计算并传递normalWS和positionWS,这展示了 Shader 编程中常见的“传递数据”模式。
第四部分:实用案例篇
- 水波纹效果:在片元着色器中使用
sin函数,结合时间_Time.y,根据 UV 坐标计算出偏移量来扰动纹理采样坐标。 - 溶解效果:在片元着色器中采样一张噪声纹理,如果噪声值小于一个随时间变化的阈值,就丢弃这个像素 (
clip(value))。 - 卡通描边:可以在模型渲染后,再 Pass 一次,用一个非常小的模型顶点偏移来绘制轮廓(称为“扩张法”),或者像上面边缘发光那样计算法线与视角的夹角。
- 顶点动画:在顶点着色器中修改
input.positionOS,例如通过sin函数让旗帜飘动,或者通过噪声函数让地面起伏。
第五部分:学习资源推荐
中文资源
- Bilibili (必看!):
- 小新学图形学:国内最好的 Unity Shader 入门系列之一,从零开始,讲解清晰,实例丰富。
- 某某(UWA):也有很多高质量的 Shader 教程,偏向于实战和优化。
- CSDN / 知乎 / 掘金:搜索关键词 "Unity Shader 教程",有大量技术博客和文章,可以解决具体问题。
- Unity 官方文档:虽然是英文,但翻译质量很高,是查阅 API 和概念最权威的地方。
英文资源
- Catlike Coding:强烈推荐! 这可能是全世界最好的 Unity 图形学教程网站,从最基础的 C# 和数学讲起,逐步深入到 Shader、光线追踪等高级主题,逻辑严谨,循序渐进。
- The Book of Shaders:专注于 GLSL(与 HLSL 语法非常相似),从数学基础讲起,教你如何从零开始构建着色器,对理解图形学原理帮助巨大。
- Unity Learn:Unity 官方学习平台,有很多官方的交互式项目和教程。
- Urchin (by Alan Zucconi):一本免费的在线书籍,深入浅出地讲解了图形学在游戏中的应用,包括 Shader。
学习路径建议
- 新手入门:从 Shader Graph 开始,快速建立信心,理解 Shader 的基本逻辑。
- 理论学习:开始学习 Catlike Coding 的前几章,补充线性代数、三角函数等基础知识。
- 代码入门:当对 Shader Graph 熟悉后,尝试阅读和修改 Unity 自带的简单 Shader(如
Unlit/Texture),然后跟着教程(如小新学图形学)手写一个简单的 Shader。 - 实战进阶:尝试自己实现一些效果(如溶解、水波),遇到问题再查资料,这是最快成长的方式。
- 精通之路:深入阅读 GPU Gems、Real-Time Rendering 等书籍,学习更高级的渲染技术,如 PBR 原理、后处理、体积光等。
学习 Shader 是一个漫长但回报丰厚的过程,需要耐心和大量的实践,祝你学习愉快!
