顶点片元着色器(important)
1.需要在Pass渲染通道中编写着色器逻辑
2.可以使用cG或HLSL两种shader语言去编写Shader逻辑
3.代码量较多,灵活性较强,性能消耗更可控,可以实现更多渲染细节
4.适用于光照处理较少,自定义染效果较多时(移动平台首选)
表面着色器是Unity对顶点/片元着色器的封装
可以用更少的代码实现复杂的逻辑
但是性能和可控性较差
它的特点是
1.直接在subshader语句块中书写着色器逻辑
2.我们不需要关心也不需要使用多个Pass,每个Pass如何染,Unity会在内部帮助我们去处理
3.可以使用cG或HLSL两种shader语言去编写shader逻辑
4.代码量较少,可控性较低,性能消耗较高
5.适用于处理需要和各种光源打交道的着色器,但是在移动平台上需要注意性能表现
表面着色器 和顶点/片元着色器这两种unity Shader形式都使用了可编程管线
而对于一些老设备(Dx7.0、0penGL1.5或OpenGLES1.1):它们不支持可编程管线着色器
这时就需要使用固定函数着色器来进行渲染
这些着色器只能实现一些非常简单的效果
它的特点是:
1.需要在Pass染通道中编写着色器逻辑
2.需要使用shaderLab语法中的染设置命令来编写,而非cG和HLSL着色器语言
但是由于这些旧设备目前市面上几乎已经没有了
所以固定函数着色器我们几乎不会再使用
只做了解即可
即使我们现在在unity中使用固定函数着色器来编写Shader,在内部也会被编译为顶点/片元着色器
因此真正意义的固定函数着色器已经不存在了
subshader语句块
渲染标签
通过标签来确定什么时候以及如何对物体进行渲染
渲染队列(Render Queue)优化渲染效率、正确处理透明物体、减少渲染错误
队列名 | 队列值(Queue) | 渲染顺序(从小到大) | 用途 |
---|---|---|---|
Background | 1000 | 最早渲染 | 用于背景物体,通常比不透明物体更早渲染 |
Geometry | 2000 | 渲染大部分不透明物体 | 绝大多数 3D 模型、地面、建筑 |
AlphaTest | 2450 | 用于透明裁剪(如叶子、栅栏) | 用于 clip() 处理的材质,如草丛、栅栏 |
Transparent | 3000 | 后渲染(需要正确混合透明效果) | 玻璃、水、烟雾等需要 Alpha 混合的对象 |
Overlay | 4000 | 最晚渲染 | UI、HUD、特效等最前景的物体 |
(Opaque) → 应该先渲染,因为它们会遮挡后面的物体,减少 GPU 计算量
(Transparent) → 必须后渲染,否则无法正确混合(比如玻璃、水等)透明物体依赖前面已经渲染好的背景
特效、后处理 → 可能需要在所有物体之后渲染。
- 你有 半透明水,希望它在透明物体 之后 渲染,但又不想晚于 UI → 可以设定
"Queue" = "Transparent + 10"
- 你有 特效 Shader,希望它比普通透明物体 稍早渲染,避免遮挡问题 → 可以设定
"Queue" = "Transparent - 1"
RenderType方便 Unity 进行批量优化,
渲染管线(Render Pipeline)或后处理效果(Post Processing)可以识别、管理和优化不同类型的 Shader
一个 SubShader 只能有一个 RenderType
当使用批处理时,模型会被变换到世界空间中,模型空间会被丢弃
这可能会导致某些使用模型空间顶点数据的shader最终无法实现想要的结果
可以通过开启禁用批处理来解决该问题
渲染状态
通过状态来确定渲染时的剔除方式、深度测试方式、混合方式等等内容
Shader 间接控制片元,光栅化阶段(Rasterization) 生成GPU 底层的片元
Shader 控制的片元是 GPU 片元的“表现层”
片元(Fragment)主要由片元着色器(Fragment Shader)或像素着色器(Pixel Shader)控制,表示屏幕上即将被渲染的像素信息(颜色、深度等)
剔除,就是不渲染,背面剔除就是背面不渲染,正面剔除就是正面不渲染,不剔除就是都渲染
Cull Back背面剔除
Cull Front正面剔除
Cull off不剔除
不设置的话,默认为背面剔除
深度缓冲(Depth Buffer):
深度缓冲是一个与屏幕像素对应的缓冲区,用于存储每个像素的深度值(距离相机的距离)。
在渲染场景之前,深度缓冲被初始化为最大深度值,表示所有像素都在相机之外。
最后留在深度缓冲中的信息会被染
ZWrite on写入深度缓冲
Zwrite off不写入深度缓冲
不设置的话,默认为写入
一般情况下,我们在做透明等特殊效果时,会设置为不写入
当前像素的深度值小于深度缓冲中的值,说明当前像素在其他物体之前,它会被绘制,并更新深度缓冲。
当前像素的深度值大于等于深度缓冲中的值,说明当前像素在其他物体之后,它会被丢弃,不会被绘制,并保持深度缓冲不变。
当前深度缓冲中的值
ZTest Less
ZTest Greater
ZTest LEqual 小于等于
ZTest GEqual 大于等于
ZTest Equal
ZTest NotEqual不等于当前深度缓冲中的值,就通过测试,写入到深度缓冲中
ZTestAlways 始终通过深度测试,写入深度缓冲中
不设置的话,默认为LEqual
深度缓冲(Z-Buffer)用于存储每个像素的深度值(Z 值),片元的深度值由 投影变换后的位置 计算得出
混合是在深度测试之后去进行的,混合过后它就会把颜色存入到一个颜色缓冲区当中,颜色缓冲区其实就是一个内存空间,
混合(Blending)决定当前片元的颜色如何与已经存储在颜色缓冲区的颜色进行合成,常用于半透明材质、特效(如火焰、光晕、烟雾)
混合发生在 深度测试(Z-Test)之后,颜色写入(Color Write)之前:先检查 当前片元(Fragment) 是否应该被渲染,如果当前片元被挡住(ZTest 失败) → 直接丢弃,不执行混合
如果通过,获取当前片元的颜色(源颜色,SrcColor),获取颜色缓冲区(Framebuffer)中的颜色(目标颜色,DstColor)
常见的混合模式(Blend Mode)
混合模式 | 公式 | 用途 |
---|---|---|
Alpha 混合 | FinalColor = SrcColor * SrcAlpha + DstColor * (1 - SrcAlpha) | 玻璃、烟雾、透明物体 |
Additive(加法混合) | FinalColor = SrcColor * 1 + DstColor * 1 | 光效、火焰、魔法特效 |
Multiplicative(乘法混合) | FinalColor = SrcColor * DstColor | 阴影、滤镜效果 |
Shader "Custom/LODExample"
{
SubShader
{
LOD 100
Pass { /* 低 LOD 的 Shader 代码 */ }
}SubShader
{
LOD 300
Pass { /* 高 LOD 的 Shader 代码 */ }
}
}
ColorMask 配置 | 用途 |
---|---|
ColorMask RGB | 忽略 Alpha 通道,避免透明度影响 |
ColorMask A | 仅写入 Alpha,用于特效 |
ColorMask 0 | 只影响深度缓冲,不影响颜色(Shadow Map) |
一个Shader设定之后,执行顺序
步骤 | 执行阶段 | Shader 相关代码 | 作用 |
---|---|---|---|
① 解析 Shader 标签 | Shader 解析阶段 | Tags { "Queue"="Geometry" } | 确定渲染顺序、是否透明 |
② 选择 SubShader | 运行时自动匹配 | LOD 300 | 选择适合当前硬件的 Shader |
③ Pass 开始 | GPU 处理 | Pass {} | 进入渲染 Pass |
④ 处理渲染状态 | 设置 GPU 状态 | Cull Back 、ZTest Less | 控制渲染面、深度计算方式 |
⑤ 片元着色器执行 | GPU 计算颜色 | frag() | 计算颜色值 |
⑥ 深度测试 | 片元写入前 | ZTest 、ZWrite | 确保正确的片元渲染 |
⑦ 颜色混合 | 颜色计算 | Blend | 计算透明度混合 |
⑧ 颜色写入 | 最终像素写入 | ColorMask RGB | 控制通道写入 |
在subshader语句块中使用渲染状态会影响之后的所有渲染通道Pass
在Pass语句块中使用渲染状态只会影响当前Pass渲染通道,不会影响其他的Pass
渲染通道
具体实现着色器代码的地方(每个subshader语句块中至少有一个渲染通道,可以有多个)
Unity内部会把Pass名称转换为大写字母
因此在使用usePass命令时必须使用大写形式的名字
在其他shader中复用该Pass代码时
UsePass "TeachShader/Lesson4/MYPAss"
但是subshader语句块中的 渲染标签 不能够在Pass中使用
Pass当中有自己专门的标签
1.Tags{"LightMode”=“标签值"}
主要作用
指定了该Pass应该在哪个阶段执行
可以将着色器代码分配给适当的渲染阶段,以实现所需的效果
Always
始终渲染:不应用光照
ForwardBase
在前向渲染中使用:应用环境光、主方向光、顶点/SH光源和光照贴图
ForwardAdd
在前向染中使用:应用附加的每像素光源(每个光源有一个通道)
Deferred
在延迟渲染中使用;渲染G缓冲区
Shadowcaster
将对象深度染到阴影贴图或深度纹理中
MotionVectors
用于计算每对象运动矢量
PrepassBase
在旧版延迟光照中使用;渲染法线和镜面反射指数
PrepassFinal
在旧版延迟光照中使用:通过组合纹理、光照和反光来渲染最终颜色
Vertex
当对象不进行光照贴图时在旧版顶点光照谊染中使用;应用所有顶点光源
VertexLMRGBM
当对象不进行光照贴图时在旧版顶点光照渲染中使用;在光照贴图为 RGBM 编码的平台上(PC 和游戏主机)
VertexLM
当对象不进行光照贴图时在旧版顶点光照渲染中使用;在光照贴图为双LDR编码的平台上(移动平台)
2.Tags{“RequireOptions”=“标签值”}
主要作用
用于指定当满足某些条件时才渲染该Pass
3.Tags{"PassFlags"="标签值"}
主要作用:
一个染通道Pass可指示一些标志来更改染管线向Pass传递数据的方式
目前unity仅支持
Tags{"PassFlags"="OnlyDirectional"}
意味着非重要光源的数据将不会传递到顶点光源或球谐函数着色器变量
备用shader的语法非常简单
Fallback "shader名"
或者
FallbackOff
它的主要作用就是提供“救命稻草”
我们一般会使用较为低级的效果作为备用shader
确保对象在低端设备上也能够正常渲染
-------------------
Shader的属性主要就是三种类型
数值、颜色和向量、纹理贴图
我们只需要掌握属性声明的基础语法即可
它的主要是:
1.可以在材质面板被编辑
2.可以在后续当作输入变量提供给所有子着色器使用
纹理贴图属性的定义
1.颜色
Name("Display Name",Color) =(number1,number2,number3,number4)
注意:颜色值中的RGBA的取值范围是0~1(映射e~255)
2.向量
Name("Display Name",Vector)=(number1,number2,number3,number4)
注意:向量值中的XYZW的取值范围没有限制
Shader选择的路径
1.直接修改shader文件中Shader后的名字即可
2.Shader的名字决定了在材质面板的选择路径
可以选择对应的图形接口,生成查看(方便根据不同平台的情况去查看Shader生成的对应代码)
PBR 参数(Metallic、Smoothness、Albedo)FX Shader(特效 Shader)
一般的使用流程是
Shader---材质----赋值给GameObject对象上依附的MeshRenderer等相关渲染器组件上进行使用
为什么 Unity 需要 ShaderLab?
ShaderLab 是 Unity 专用的着色器(Shader)编写语言,用于定义 Unity 渲染管线如何处理 材质、光照、特效 等。它的作用类似于 OpenGL 的 GLSL、DirectX 的 HLSL,但 ShaderLab 不是单纯的编程语言,而是一个着色器封装框架,允许你在 Unity 里编写 Shader 代码,同时管理材质参数和渲染状态。
提供了 结构化的写法可以用 HLSL(高阶着色器语言) 来写 Shader
管理 Pass、渲染状态、ShaderLab 里定义 Shader 变量、HLSL 计算最终颜色
Unity 默认的所有 Shader 都是 ShaderLab 结构
Unity 的核心设计理念是 跨平台 + 可扩展、允许开发者自定义 Shader、兼顾不同的渲染后端(如 DirectX、OpenGL、Vulkan、Metal)
Windows(DirectX)、Mac(Metal)、安卓(Vulkan)、WebGL(OpenGL)每个平台的 Shader 语言都不一样
ShaderLab 的核心作用就是对各个平台的 Shader 语言进行封装和统一,让开发者在 Unity 里写 Shader 时不需要直接面对不同的底层 API(DirectX、Metal、Vulkan、OpenGL),而是通过 ShaderLab 提供的统一结构来管理和适配不同平台。
Technical Artist核心技能要求:
Shader & 渲染优化(最核心)【第 1 阶段:3-5 周】
为什么先学 Shader?
Shader 是 TA 的核心技能,它控制了画面的最终呈现,而且学 Shader 能直接出作品,有利于建立信心和作品集。
✔ 学习 Shader 编程
- HLSL(Unity) → Unity Shader Graph + ShaderLab
- GLSL(OpenGL)(如果以后想研究 OpenGL)
- Unreal Shader(USF)(如果想做 Unreal 开发)
- 练习:写几个基础 Shader(溶解、描边、透明效果)
✔ 理解现代渲染管线
- 先学 Unity SRP(URP/HDRP)
- 再学 Unreal 渲染架构(Deferred / Forward)
- 练习:用 Unity URP/HDRP 写一个 PBR Shader
✔ 性能优化
- Draw Call 优化
- GPU Profiler 调试
- LOD、贴图优化
- 练习:优化一个小场景(减少 Draw Call,提升 FPS)
美术工具 & 资产管线【第 2 阶段:3-4 周】
为什么第二步学美术工具?
Shader 只是渲染的一部分,但模型、贴图、动画 都由美术资源决定,你要学会如何处理这些资源,提高美术生产效率。
✔ 熟悉 3D 美术工具
- Maya / Blender(基本建模、绑定)
- Photoshop(贴图处理)
- ZBrush(雕刻,次世代角色管线)
- 练习:制作一个简单的 3D 角色(带 UV 和贴图)
✔ 资产管线(Art Pipeline)
- 如何高效导入游戏资源(FBX、USD、GLTF)
- 练习:导出 FBX,并正确在 Unity / Unreal 里显示
✔ 材质 & 贴图
- UV 贴图、法线贴图、光照贴图(Lightmap)
- 练习:给一个 3D 物体制作完整材质
编程能力(技术美术必备)【第 3 阶段:4-6 周】
为什么第三步学编程?
Shader 和美术技能都有了之后,就需要写工具来提升工作流效率,比如自动化模型导入、Shader 调试工具等。
✔ 掌握一门编程语言
- C++(Unreal Engine)
- C#(Unity,SRP、Editor Tool)
- Python(Maya / Blender 插件,工具开发)
- 练习:用 Python 写一个 Maya 插件(自动导出 FBX)
✔ 图形 API(进阶,选学)
- OpenGL / DirectX / Vulkan(如有更深入需求)
- 练习:用 OpenGL 画一个简单的模型渲染
✔ 工具开发
- 用 Python/C++ 编写插件,提升美术工作效率
- 练习:制作一个 Unity 编辑器工具(批量修改 Shader 参数)
美术基础 & 审美 -----长期积累
为什么美术基础放到第四步?
因为审美是长期积累的东西,你需要在 Shader、美术管线、工具开发的过程中不断锻炼它。
✔ 良好的色彩、光影、构图感
- 练习:调整一个 3D 场景的光照,让它更好看
- 练习:用后处理(Post Processing)做出电影感
✔ 了解动画、绑定
- 角色管线:绑定、蒙皮(Maya / Blender)
- 动画导入导出、IK 处理
- 练习:绑定一个角色,并在游戏中正确播放动画
加分项(可选学习)
这些内容可以在 掌握了基础之后 选择性学习。
✔ VFX(特效)
- Unity VFX Graph、Unreal Niagara
- 粒子系统、流体模拟(Houdini)
- 练习:制作一个魔法特效
✔ 后处理(Post Processing)
- Bloom、DOF、Motion Blur、Color Grading
- 练习:制作一个高质量的后处理特效
✔ 机器学习(次世代游戏优化)
- AI 生成贴图、智能 LOD 优化
- 了解 NVIDIA DLSS、AI Upscaling
- 练习:用 AI 生成贴图并优化资源
30 天计划快速入门:
第 1-2 周(Shader 基础)
✅ 了解 Unity ShaderLab / HLSL
✅ 做一个溶解 Shader、描边 Shader
✅ 了解光照模型(PBR)
第 3-4 周(渲染优化 + 美术管线)
✅ 了解 Unity SRP / HDRP
✅ 了解 Draw Call 优化、GPU Profiler
✅ 掌握 Maya、PS 的基础,能导入 3D 资源
第 5-6 周(编程 + 自动化工具)
✅ 掌握 C++ / C#(Unity / Unreal)
✅ 用 Python 写一个工具,优化美术管线
✅ 用 C# 在 Unity 里开发一个小插件
第 7 周(整合作品)
✅ 制作一个 Shader + VFX 效果
✅ 导出 FBX,优化资源,并展示在游戏中
✅ 用 GPU Profiler 进行优化