Unity URP Shader 速查表

这篇是给 Unity 程序用的 URP Shader 自查表:少讲玄学,多放结构、宏、模板、常用效果和排错。
写 Shader 时可以先看索引,再复制对应模板改变量名。


快速索引

按任务查

我想做什么 先看
从零写一个 URP Shader 2. 最小 URP Shader 骨架
搞清 ShaderLab 结构 3. ShaderLab 结构速查
查 Tags / Queue / RenderType 4. Tags 与渲染队列
查 Blend / ZWrite / Cull 5. Pass 渲染状态
查 HLSL 类型和语义 6. HLSL 类型、语义与命名
查 URP include 和函数 7. URP 常用 include
做坐标空间转换 8. 坐标空间转换
采样贴图和 Tiling Offset 9. 纹理采样
避免 SRP Batcher 失效 10. 材质属性与 SRP Batcher
写 Unlit / 贴图 / 顶点色 11. Unlit 常用模板
写透明、半透明、裁剪 12. 透明、Alpha Clip 与双面
接主光源 13. 主光源 Lambert 光照
接附加光源 14. 附加光源与 Forward Plus
接收阴影 15. 接收阴影
投射阴影 16. ShadowCaster Pass
法线贴图 17. 法线、切线与 Normal Map
雾效 18. Fog 雾效
深度图和世界坐标重建 19. 深度纹理与屏幕空间
写溶解、边缘光、描边 20. 常见效果片段
C# 控制材质参数 21. C# 传参和 MaterialPropertyBlock
管理关键字和变体 22. Shader Keywords 与变体
优化移动端 Shader 23. 性能优化速查
Shader 变粉、编译报错 24. 常见报错排查
Built-in Shader 改 URP 25. Built-in 到 URP 迁移
提交前检查 26. 写完 Shader 自查清单

必背最短表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
ShaderLab                 # Unity Shader 外层结构:Properties / SubShader / Pass
HLSLPROGRAM # URP 使用 HLSL 代码块
Core.hlsl # URP 基础函数、矩阵、坐标变换
Lighting.hlsl # URP 光照、Light、阴影、Lambert、PBR 相关函数
RenderPipeline # SubShader Tag,URP 写 UniversalPipeline
LightMode # Pass Tag,决定 Pass 在 URP 哪个阶段执行
UnityPerMaterial # 材质属性 CBUFFER,SRP Batcher 关键
Attributes # 顶点输入结构
Varyings # 顶点到片元插值结构
POSITION / NORMAL / TEXCOORD0 # Mesh 顶点输入语义
SV_POSITION # 裁剪空间输出语义
SV_Target # 片元颜色输出语义
TransformObjectToHClip # 物体空间坐标转裁剪空间
GetVertexPositionInputs # 一次拿 WS / VS / CS / NDC 坐标
GetVertexNormalInputs # 一次拿世界空间 normal / tangent / bitangent
TEXTURE2D / SAMPLER # URP 纹理和采样器声明
SAMPLE_TEXTURE2D # URP 纹理采样
TRANSFORM_TEX # 应用 Tiling / Offset
GetMainLight # 获取主光源
GetAdditionalLight # 获取附加光源
LIGHT_LOOP_BEGIN # URP 附加光循环
ComputeScreenPos # 屏幕坐标相关
SampleSceneDepth # 采样相机深度图
ComputeWorldSpacePosition # 从深度和屏幕 UV 重建世界坐标

0. 版本与使用约定

0.1 版本说明

1
2
3
4
主要目标:URP 12+ / 14+ / 16+ / Unity 6 URP
常用项目:Unity 2021 LTS、2022 LTS、2023、Unity 6
写法倾向:ShaderLab + HLSL,不依赖 Shader Graph
光照目标:先能写 Unlit、自定义光照、常见特效,再补 PBR

注意:

1
2
3
4
URP 包版本不同,部分内部函数签名可能变化
基础 include、Pass Tag、SRP Batcher 写法相对稳定
复杂 PBR 建议参考当前项目 PackageCache 里的 Lit.shader
需要长期维护的材质,优先少碰 URP 内部未公开函数

0.2 文件位置

1
2
3
4
Assets/Shaders/xxx.shader              # ShaderLab 文件
Assets/Shaders/HLSL/xxx.hlsl # 可复用 HLSL include
Assets/Materials/xxx.mat # 材质
Assets/Textures/xxx.png # 贴图

0.3 命名约定

1
2
3
4
5
6
7
8
9
10
11
12
positionOS       # Object Space,物体空间
positionWS # World Space,世界空间
positionVS # View Space,观察空间
positionCS # Clip Space,裁剪空间
positionHCS # Homogeneous Clip Space,齐次裁剪空间
positionNDC # Normalized Device Coordinates,归一化设备坐标
normalOS # 物体空间法线
normalWS # 世界空间法线
tangentOS # 物体空间切线
tangentWS # 世界空间切线
viewDirWS # 世界空间视线方向
uv # UV

0.4 float / half / fixed 怎么选

1
2
3
4
float value;        // 高精度,坐标、深度、世界位置、矩阵计算优先用
half value; // 中精度,颜色、法线、光照、移动端常用
int value; // 整数计数、索引
bool value; // 条件开关,注意分支成本

URP 里不要再用 Built-in 时代的 fixed

1
2
3
4
half4 frag(Varyings IN) : SV_Target
{
return half4(1, 1, 1, 1); // URP 常用 half4 输出颜色
}

0.5 写 Shader 的推荐顺序

1
2
3
4
5
6
7
先写最小 Unlit,让材质不粉
加 Properties 和 UnityPerMaterial
加贴图采样和 UV
加渲染状态:Opaque / Transparent / AlphaClip
加光照:主光源 -> 阴影 -> 附加光
加效果:溶解 / Rim / 扰动 / 描边
最后处理性能、关键字、变体和平台差异

1. URP Shader 心智模型

1.1 GPU 管线最短版

1
2
3
4
5
6
Mesh 顶点数据
-> Vertex Shader # 处理每个顶点,输出裁剪空间坐标和插值数据
-> Rasterization # 光栅化,把三角形变成像素片元
-> Fragment Shader # 处理每个像素,输出颜色
-> Depth / Blend / Stencil # 深度、混合、模板测试
-> Frame Buffer # 最终画面

1.2 Unity 程序员关注什么

1
2
3
4
5
顶点阶段:位置变换、顶点动画、传 UV / 法线 / 世界坐标
片元阶段:采贴图、算颜色、算光照、透明裁剪、溶解
Pass 状态:是否写深度、是否透明混合、是否剔除背面
材质属性:Inspector 参数、C# 传参、SRP Batcher 是否兼容
变体关键字:功能开关、光照阴影开关、构建体积

1.3 URP 和 Built-in 最大区别

1
2
3
4
5
6
7
CGPROGRAM -> HLSLPROGRAM
UnityCG.cginc -> Core.hlsl / Lighting.hlsl
UnityObjectToClipPos -> TransformObjectToHClip
appdata / v2f -> Attributes / Varyings
sampler2D / tex2D -> TEXTURE2D / SAMPLE_TEXTURE2D
固定管线光照宏 -> URP ShaderLibrary 函数
材质属性散放 -> UnityPerMaterial CBUFFER

2. 最小 URP Shader 骨架

2.1 最小可显示 Unlit

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
Shader "Xiaoyun/URP/MinimumUnlit"
{
Properties
{
[MainColor] _BaseColor("Base Color", Color) = (1, 1, 1, 1)
}

SubShader
{
Tags
{
"RenderPipeline" = "UniversalPipeline" // 声明这是 URP SubShader
"RenderType" = "Opaque" // 不透明对象
"Queue" = "Geometry" // 不透明队列
}

Pass
{
Name "ForwardUnlit"
Tags { "LightMode" = "UniversalForward" }

HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

struct Attributes
{
float4 positionOS : POSITION; // 模型顶点,物体空间
};

struct Varyings
{
float4 positionCS : SV_POSITION; // 裁剪空间位置,必须输出
};

CBUFFER_START(UnityPerMaterial)
half4 _BaseColor; // 材质颜色,放进 UnityPerMaterial
CBUFFER_END

Varyings vert(Attributes IN)
{
Varyings OUT;
OUT.positionCS = TransformObjectToHClip(IN.positionOS.xyz);
return OUT;
}

half4 frag(Varyings IN) : SV_Target
{
return _BaseColor;
}
ENDHLSL
}
}
}

2.2 最小模板必有项

1
2
3
4
5
6
7
8
9
10
11
12
Shader "路径/名字"                         # Inspector 里选择 Shader 的路径
Properties # 材质面板参数
SubShader # 一套渲染实现
Tags { "RenderPipeline"="UniversalPipeline" } # URP 识别
Pass # 一次渲染 Pass
HLSLPROGRAM / ENDHLSL # HLSL 代码块
#pragma vertex vert # 顶点函数入口
#pragma fragment frag # 片元函数入口
Core.hlsl # 基础 URP 函数
TransformObjectToHClip # 顶点转裁剪空间
SV_POSITION # 顶点输出位置语义
SV_Target # 片元颜色输出语义

2.3 常见漏项现象

1
2
3
4
5
没写 RenderPipeline = UniversalPipeline     # 可能被 URP 跳过或表现异常
没 include Core.hlsl # TransformObjectToHClip 找不到
没输出 SV_POSITION # 顶点阶段输出不合法
材质属性没进 UnityPerMaterial # SRP Batcher 不兼容
Pass Tag 不对 # 阴影、深度、SSAO、Forward 阶段不执行

3. ShaderLab 结构速查

3.1 完整结构

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
Shader "Folder/ShaderName"
{
Properties
{
_BaseColor("Base Color", Color) = (1,1,1,1)
_BaseMap("Base Map", 2D) = "white" {}
}

SubShader
{
Tags { "RenderPipeline"="UniversalPipeline" "RenderType"="Opaque" "Queue"="Geometry" }

Pass
{
Name "PassName"
Tags { "LightMode"="UniversalForward" }

Cull Back
ZWrite On
ZTest LEqual

HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
ENDHLSL
}
}

FallBack Off
}

3.2 Properties 类型

1
2
3
4
5
6
7
_Color("Color", Color) = (1,1,1,1)             // 颜色
_Vector("Vector", Vector) = (0,0,0,0) // 四维向量
_Float("Float", Float) = 1 // 浮点
_Range("Range", Range(0,1)) = 0.5 // 滑条
_Int("Int", Int) = 1 // 整数
_BaseMap("Base Map", 2D) = "white" {} // 2D 贴图
_Cube("Cube Map", Cube) = "" {} // Cubemap

3.3 常用 Inspector 属性装饰

1
2
3
4
5
6
7
[MainTexture] _BaseMap("Base Map", 2D) = "white" {}  // 主贴图
[MainColor] _BaseColor("Base Color", Color) = (1,1,1,1) // 主颜色
[NoScaleOffset] _MaskMap("Mask", 2D) = "white" {} // 不显示 Tiling / Offset
[Normal] _NormalMap("Normal Map", 2D) = "bump" {} // 法线贴图槽
[HDR] _EmissionColor("Emission", Color) = (0,0,0,0) // HDR 颜色
[Toggle] _UseRim("Use Rim", Float) = 0 // 0/1 开关
[Enum(UnityEngine.Rendering.CullMode)] _Cull("Cull", Float) = 2 // 枚举

3.4 属性名建议

1
2
3
4
5
6
7
8
9
10
11
_BaseMap          # 主贴图,URP 常用
_BaseColor # 主颜色,URP 常用
_NormalMap # 法线贴图
_MaskMap # 遮罩图
_EmissionMap # 自发光贴图
_EmissionColor # 自发光颜色
_Cutoff # Alpha Clip 阈值
_Metallic # 金属度
_Smoothness # 光滑度
_RimColor # 边缘光颜色
_RimPower # 边缘光强度

3.5 Shader 名字路径

1
2
3
4
Shader "Xiaoyun/URP/UnlitTexture"             // 自己的 Shader
Shader "Xiaoyun/URP/FX/Dissolve" // 特效类
Shader "Xiaoyun/URP/Character/Toon" // 角色类
Shader "Xiaoyun/URP/UI/CustomImage" // UI 类

4. Tags 与渲染队列

4.1 SubShader Tags

1
2
3
4
5
6
Tags
{
"RenderPipeline" = "UniversalPipeline" // URP 必备
"RenderType" = "Opaque" // 渲染类型,用于替换 Shader、深度等
"Queue" = "Geometry" // 渲染队列
}

4.2 Queue 常用值

Queue 数值 用途
Background 1000 天空、背景
Geometry 2000 默认不透明
AlphaTest 2450 Alpha Clip,例如树叶、草
Transparent 3000 半透明
Overlay 4000 覆盖层
1
2
3
4
5
Tags { "Queue"="Geometry" }                  // 不透明
Tags { "Queue"="AlphaTest" } // 裁剪透明
Tags { "Queue"="Transparent" } // 半透明
Tags { "Queue"="Geometry+10" } // 比普通不透明稍晚
Tags { "Queue"="Transparent-10" } // 比普通透明稍早

4.3 RenderType 常用值

1
2
3
Tags { "RenderType"="Opaque" }               // 不透明
Tags { "RenderType"="TransparentCutout" } // Alpha Clip
Tags { "RenderType"="Transparent" } // 半透明

4.4 Pass LightMode 常用值

LightMode 用途
UniversalForward URP Forward 主渲染 Pass,常用
UniversalForwardOnly 强制走 Forward,Deferred 时也按 Forward 渲染
UniversalGBuffer Deferred GBuffer Pass
ShadowCaster 投射阴影
DepthOnly 写入深度
DepthNormalsOnly 写入深度和法线,SSAO 等会用到
Universal2D URP 2D Renderer
SRPDefaultUnlit 未指定 LightMode 时的默认类型之一
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Pass
{
Name "ForwardLit"
Tags { "LightMode"="UniversalForward" }
}

Pass
{
Name "ShadowCaster"
Tags { "LightMode"="ShadowCaster" }
}

Pass
{
Name "DepthOnly"
Tags { "LightMode"="DepthOnly" }
}

4.5 常见搭配

1
2
3
4
5
6
7
普通不透明:Queue Geometry + RenderType Opaque + ZWrite On + Blend Off
树叶草地:Queue AlphaTest + RenderType TransparentCutout + ZWrite On + clip
半透明特效:Queue Transparent + RenderType Transparent + ZWrite Off + Blend SrcAlpha OneMinusSrcAlpha
加法特效:Queue Transparent + ZWrite Off + Blend One One
描边 Pass:通常 Cull Front,把背面外扩
阴影 Pass:LightMode ShadowCaster
深度 Pass:LightMode DepthOnly + ColorMask 0

5. Pass 渲染状态

5.1 Cull 剔除

1
2
3
Cull Back      // 默认,剔除背面,常用不透明物体
Cull Front // 剔除正面,常用于描边外扩 Pass
Cull Off // 双面渲染,常用于叶子、布料、纸片特效

5.2 ZWrite 深度写入

1
2
ZWrite On      // 写深度,常用不透明和 Alpha Clip
ZWrite Off // 不写深度,常用半透明

5.3 ZTest 深度测试

1
2
3
4
ZTest LEqual   // 默认,小于等于已有深度就通过
ZTest Less // 更严格,只小于才通过
ZTest Greater // 被遮挡部分才显示,常用于 XRay
ZTest Always // 永远通过,常用于屏幕覆盖效果

5.4 Blend 混合

1
2
3
4
5
Blend Off                              // 不混合,不透明
Blend SrcAlpha OneMinusSrcAlpha // 普通透明
Blend One One // 加法发光
Blend One OneMinusSrcAlpha // 预乘 Alpha
Blend DstColor Zero // 乘法

5.5 ColorMask

1
2
3
ColorMask RGB                          // 写 RGB,不写 A
ColorMask A // 只写 Alpha
ColorMask 0 // 不写颜色,常用于 DepthOnly

5.6 Offset

1
2
Offset 1, 1                            // 深度偏移,减少 Z-fighting
Offset -1, -1 // 往镜头方向偏一点,谨慎使用

5.7 常用状态模板

不透明:

1
2
3
4
5
Tags { "Queue"="Geometry" "RenderType"="Opaque" }
Cull Back
ZWrite On
ZTest LEqual
Blend Off

Alpha Clip:

1
2
3
4
5
Tags { "Queue"="AlphaTest" "RenderType"="TransparentCutout" }
Cull Back
ZWrite On
ZTest LEqual
Blend Off

普通透明:

1
2
3
4
5
Tags { "Queue"="Transparent" "RenderType"="Transparent" }
Cull Back
ZWrite Off
ZTest LEqual
Blend SrcAlpha OneMinusSrcAlpha

加法特效:

1
2
3
4
Tags { "Queue"="Transparent" "RenderType"="Transparent" }
Cull Off
ZWrite Off
Blend One One

XRay 被遮挡显示:

1
2
3
4
Tags { "Queue"="Transparent" "RenderType"="Transparent" }
ZWrite Off
ZTest Greater
Blend SrcAlpha OneMinusSrcAlpha

6. HLSL 类型、语义与命名

6.1 常用类型

1
2
3
4
5
6
7
8
9
10
11
float       // 32 位浮点,坐标和矩阵优先
float2 // 二维向量,UV
float3 // 三维向量,位置、法线、方向
float4 // 四维向量,颜色、齐次坐标
half // 半精度,颜色、光照、移动端常用
half2
half3
half4
int
uint
bool

6.2 常用语义

1
2
3
4
5
6
7
8
9
10
POSITION        // 顶点位置输入
NORMAL // 顶点法线输入
TANGENT // 顶点切线输入
COLOR // 顶点色输入
TEXCOORD0 // UV0 或自定义插值
TEXCOORD1 // UV1 或自定义插值
TEXCOORD2 // 自定义插值
SV_POSITION // 顶点 Shader 输出到光栅化的裁剪空间位置
SV_Target // Fragment Shader 输出颜色
SV_Depth // Fragment Shader 输出深度,少用

6.3 Attributes 常用写法

1
2
3
4
5
6
7
8
9
struct Attributes
{
float4 positionOS : POSITION; // 模型顶点
float3 normalOS : NORMAL; // 模型法线
float4 tangentOS : TANGENT; // 模型切线,w 表示副切线方向
float2 uv : TEXCOORD0; // 第一套 UV
float2 uv2 : TEXCOORD1; // 第二套 UV,常给 lightmap
half4 color : COLOR; // 顶点色
};

6.4 Varyings 常用写法

1
2
3
4
5
6
7
8
struct Varyings
{
float4 positionCS : SV_POSITION; // 必须给光栅化
float2 uv : TEXCOORD0; // 传给片元阶段
float3 positionWS : TEXCOORD1; // 世界坐标
half3 normalWS : TEXCOORD2; // 世界法线
half4 color : COLOR; // 顶点色
};

6.5 插值数量注意

1
2
3
4
TEXCOORD 插值越多,带宽越高
移动端少传 float4,多用 half2 / half3
能片元里算的未必都要顶点传,但片元计算次数更高
世界坐标、法线、UV 是最常传的三类

6.6 分支和函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
saturate(x)                 // clamp 到 0~1
lerp(a, b, t) // 插值
step(edge, x) // x >= edge ? 1 : 0
smoothstep(a, b, x) // 平滑过渡
dot(a, b) // 点乘
cross(a, b) // 叉乘
normalize(v) // 单位化
length(v) // 长度
distance(a, b) // 距离
pow(x, p) // 幂,移动端注意成本
abs(x) // 绝对值
frac(x) // 小数部分
floor(x) // 向下取整
ceil(x) // 向上取整

7. URP 常用 include

7.1 Core.hlsl

1
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

常用内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
TransformObjectToHClip
TransformObjectToWorld
TransformWorldToHClip
GetVertexPositionInputs
GetVertexNormalInputs
GetWorldSpaceViewDir
GetWorldSpaceNormalizeViewDir
_Time
_SinTime
_CosTime
_ProjectionParams
_ScreenParams
_ScaledScreenParams

7.2 Lighting.hlsl

1
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

常用内容:

1
2
3
4
5
6
7
8
9
Light
GetMainLight
GetAdditionalLight
GetAdditionalLightsCount
LightingLambert
LightingSpecular
UniversalFragmentPBR
UniversalFragmentBlinnPhong
TransformWorldToShadowCoord

7.3 RealtimeLights.hlsl

1
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/RealtimeLights.hlsl"

常用内容:

1
2
3
4
Forward / Forward+ 附加光循环
GetAdditionalLight
LIGHT_LOOP_BEGIN
LIGHT_LOOP_END

7.4 DeclareDepthTexture.hlsl

1
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"

常用内容:

1
2
SampleSceneDepth              # 采样相机深度图
_CameraDepthTexture # 相机深度纹理

使用前检查:

1
2
3
URP Asset 勾选 Depth Texture
Camera 允许生成 Depth Texture
透明物体通常不会写入常规深度

7.5 DeclareOpaqueTexture.hlsl

1
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareOpaqueTexture.hlsl"

常用内容:

1
2
SampleSceneColor              # 采样不透明物体颜色
_CameraOpaqueTexture # 相机不透明纹理

使用前检查:

1
2
3
URP Asset 勾选 Opaque Texture
透明队列里采样更常见
用来做热扭曲、玻璃、屏幕扰动

7.6 不要乱 include

1
2
3
4
5
Unlit:Core.hlsl 足够
简单光照:Lighting.hlsl
深度图:Core.hlsl + DeclareDepthTexture.hlsl
屏幕颜色:Core.hlsl + DeclareOpaqueTexture.hlsl
附加光 Forward+:RealtimeLights.hlsl 或 Lighting.hlsl 中相关支持

8. 坐标空间转换

8.1 一句话记法

1
2
3
4
5
OS:模型本地
WS:世界
VS:相机观察
CS/HCS:裁剪空间,给 SV_POSITION
NDC:透视除法后的屏幕相关坐标

8.2 常用转换函数

1
2
3
float4 positionCS = TransformObjectToHClip(positionOS.xyz); // OS -> Clip
float3 positionWS = TransformObjectToWorld(positionOS.xyz); // OS -> WS
float4 clipPos = TransformWorldToHClip(positionWS); // WS -> Clip

8.3 一次拿多种位置

1
2
3
4
VertexPositionInputs posInput = GetVertexPositionInputs(IN.positionOS.xyz);

OUT.positionCS = posInput.positionCS; // 裁剪空间
OUT.positionWS = posInput.positionWS; // 世界空间

8.4 一次拿法线、切线、副切线

1
2
3
4
5
VertexNormalInputs normalInput = GetVertexNormalInputs(IN.normalOS, IN.tangentOS);

OUT.normalWS = normalInput.normalWS; // 世界法线
OUT.tangentWS = normalInput.tangentWS; // 世界切线
OUT.bitangentWS = normalInput.bitangentWS; // 世界副切线

8.5 获取视线方向

1
half3 viewDirWS = GetWorldSpaceNormalizeViewDir(positionWS); // 从点指向相机

8.6 屏幕 UV

1
float2 screenUV = IN.positionCS.xy / _ScaledScreenParams.xy; // 当前像素屏幕 UV

_ScaledScreenParams 会考虑动态分辨率,比直接用 _ScreenParams 更适合 URP。

8.7 顶点阶段标准写法

1
2
3
4
5
6
7
8
9
10
11
12
13
Varyings vert(Attributes IN)
{
Varyings OUT;

VertexPositionInputs posInput = GetVertexPositionInputs(IN.positionOS.xyz);
VertexNormalInputs normalInput = GetVertexNormalInputs(IN.normalOS, IN.tangentOS);

OUT.positionCS = posInput.positionCS;
OUT.positionWS = posInput.positionWS;
OUT.normalWS = normalInput.normalWS;

return OUT;
}

9. 纹理采样

9.1 Properties 里声明贴图

1
2
[MainTexture] _BaseMap("Base Map", 2D) = "white" {}
[MainColor] _BaseColor("Base Color", Color) = (1,1,1,1)

9.2 HLSL 里声明贴图和采样器

1
2
TEXTURE2D(_BaseMap);                 // 贴图对象
SAMPLER(sampler_BaseMap); // 采样器

9.3 UnityPerMaterial 里声明 Tiling Offset

1
2
3
4
CBUFFER_START(UnityPerMaterial)
float4 _BaseMap_ST; // xy = tiling,zw = offset
half4 _BaseColor;
CBUFFER_END

9.4 顶点阶段应用 Tiling Offset

1
OUT.uv = TRANSFORM_TEX(IN.uv, _BaseMap); // 等价于 IN.uv * _BaseMap_ST.xy + _BaseMap_ST.zw

9.5 片元阶段采样

1
2
3
half4 baseTex = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv);
half4 color = baseTex * _BaseColor;
return color;

9.6 完整贴图模板

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
Shader "Xiaoyun/URP/UnlitTexture"
{
Properties
{
[MainTexture] _BaseMap("Base Map", 2D) = "white" {}
[MainColor] _BaseColor("Base Color", Color) = (1,1,1,1)
}

SubShader
{
Tags { "RenderPipeline"="UniversalPipeline" "RenderType"="Opaque" "Queue"="Geometry" }

Pass
{
Name "ForwardUnlit"
Tags { "LightMode"="UniversalForward" }

HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);

CBUFFER_START(UnityPerMaterial)
float4 _BaseMap_ST;
half4 _BaseColor;
CBUFFER_END

struct Attributes
{
float4 positionOS : POSITION;
float2 uv : TEXCOORD0;
};

struct Varyings
{
float4 positionCS : SV_POSITION;
float2 uv : TEXCOORD0;
};

Varyings vert(Attributes IN)
{
Varyings OUT;
OUT.positionCS = TransformObjectToHClip(IN.positionOS.xyz);
OUT.uv = TRANSFORM_TEX(IN.uv, _BaseMap);
return OUT;
}

half4 frag(Varyings IN) : SV_Target
{
half4 baseTex = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv);
return baseTex * _BaseColor;
}
ENDHLSL
}
}
}

9.7 采样指定 mip

1
half4 col = SAMPLE_TEXTURE2D_LOD(_BaseMap, sampler_BaseMap, uv, 0); // mip 0

9.8 法线贴图采样

1
2
3
4
5
TEXTURE2D(_NormalMap);
SAMPLER(sampler_NormalMap);

half4 normalSample = SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, uv);
half3 normalTS = UnpackNormal(normalSample); // 切线空间法线

9.9 Mask 图通道约定

1
2
3
4
R:金属度 / 溶解 / 自定义遮罩
G:粗糙度 / AO / 自定义遮罩
B:细节遮罩
A:平滑度 / 透明度

示例:

1
2
3
4
half4 mask = SAMPLE_TEXTURE2D(_MaskMap, sampler_MaskMap, IN.uv);
half dissolveMask = mask.r;
half occlusion = mask.g;
half smoothness = mask.a;

10. 材质属性与 SRP Batcher

10.1 标准写法

1
2
3
4
5
6
7
8
9
10
TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);

CBUFFER_START(UnityPerMaterial)
float4 _BaseMap_ST;
half4 _BaseColor;
half _Cutoff;
half _Metallic;
half _Smoothness;
CBUFFER_END

10.2 什么放 CBUFFER

1
2
3
4
放:float / half / int / vector / color / texture_ST / 参数开关
不放:TEXTURE2D 声明
不放:SAMPLER 声明
不放:Unity 内置矩阵

10.3 多 Pass 必须一致

1
2
3
Forward Pass 里 UnityPerMaterial 有 _BaseColor / _Cutoff
ShadowCaster Pass 里也要同样声明 _BaseColor / _Cutoff
不同 Pass 的 UnityPerMaterial 不一致,容易 SRP Batcher 不兼容

10.4 不推荐写法

1
2
3
half4 _BaseColor;                // 不推荐:散放材质属性
float _Cutoff; // 不推荐:SRP Batcher 可能不兼容
sampler2D _BaseMap; // 不推荐:老 Built-in 写法

10.5 Inspector 检查

1
2
3
4
选中材质
Inspector 查看 SRP Batcher
显示 Compatible 才比较理想
如果 Not compatible,先查 UnityPerMaterial

11. Unlit 常用模板

11.1 纯色

1
2
3
4
half4 frag(Varyings IN) : SV_Target
{
return _BaseColor;
}

11.2 贴图乘颜色

1
2
3
4
5
half4 frag(Varyings IN) : SV_Target
{
half4 tex = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv);
return tex * _BaseColor;
}

11.3 顶点色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct Attributes
{
float4 positionOS : POSITION;
half4 color : COLOR;
};

struct Varyings
{
float4 positionCS : SV_POSITION;
half4 color : COLOR;
};

Varyings vert(Attributes IN)
{
Varyings OUT;
OUT.positionCS = TransformObjectToHClip(IN.positionOS.xyz);
OUT.color = IN.color;
return OUT;
}

half4 frag(Varyings IN) : SV_Target
{
return IN.color;
}

11.4 UV 可视化

1
2
3
4
half4 frag(Varyings IN) : SV_Target
{
return half4(IN.uv, 0, 1); // R=U,G=V
}

11.5 世界坐标可视化

1
2
3
4
5
half4 frag(Varyings IN) : SV_Target
{
half3 color = frac(abs(IN.positionWS) * 0.1);
return half4(color, 1);
}

11.6 法线可视化

1
2
3
4
5
half4 frag(Varyings IN) : SV_Target
{
half3 n = normalize(IN.normalWS);
return half4(n * 0.5 + 0.5, 1);
}

12. 透明、Alpha Clip 与双面

12.1 Alpha Clip

ShaderLab:

1
2
3
4
Tags { "RenderPipeline"="UniversalPipeline" "RenderType"="TransparentCutout" "Queue"="AlphaTest" }
Cull Back
ZWrite On
ZTest LEqual

HLSL:

1
2
3
4
5
6
7
8
9
10
11
12
CBUFFER_START(UnityPerMaterial)
float4 _BaseMap_ST;
half4 _BaseColor;
half _Cutoff;
CBUFFER_END

half4 frag(Varyings IN) : SV_Target
{
half4 col = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv) * _BaseColor;
clip(col.a - _Cutoff); // 小于 0 的像素丢弃
return col;
}

Properties:

1
_Cutoff("Alpha Cutoff", Range(0, 1)) = 0.5

12.2 普通半透明

ShaderLab:

1
2
3
Tags { "RenderPipeline"="UniversalPipeline" "RenderType"="Transparent" "Queue"="Transparent" }
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha

HLSL:

1
2
3
4
5
half4 frag(Varyings IN) : SV_Target
{
half4 col = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv) * _BaseColor;
return col; // alpha 参与 Blend
}

12.3 加法透明

1
2
3
4
Tags { "Queue"="Transparent" "RenderType"="Transparent" }
Cull Off
ZWrite Off
Blend One One

适合:

1
2
3
4
5
火焰
能量
光效
魔法边缘
屏幕闪光

12.4 预乘 Alpha

1
Blend One OneMinusSrcAlpha

片元输出:

1
2
col.rgb *= col.a; // 预乘
return col;

12.5 双面透明

1
2
3
Cull Off
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha

适合:

1
2
3
4
5
草片
树叶
布片
纸张
半透明面片特效

12.6 透明排序常见坑

1
2
3
4
5
透明通常不写深度,所以前后顺序容易错
大面积透明面片容易互相穿插
粒子和透明模型排序会受 Sorting Priority / Queue 影响
能用 Alpha Clip 就不要用半透明
能拆网格就不要靠一个大透明面解决所有层次

13. 主光源 Lambert 光照

13.1 include

1
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

13.2 顶点传世界法线

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
struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
float2 uv : TEXCOORD0;
};

struct Varyings
{
float4 positionCS : SV_POSITION;
float2 uv : TEXCOORD0;
float3 positionWS : TEXCOORD1;
half3 normalWS : TEXCOORD2;
};

Varyings vert(Attributes IN)
{
Varyings OUT;
VertexPositionInputs posInput = GetVertexPositionInputs(IN.positionOS.xyz);
VertexNormalInputs normalInput = GetVertexNormalInputs(IN.normalOS);

OUT.positionCS = posInput.positionCS;
OUT.positionWS = posInput.positionWS;
OUT.normalWS = normalInput.normalWS;
OUT.uv = TRANSFORM_TEX(IN.uv, _BaseMap);
return OUT;
}

13.3 主光源漫反射

1
2
3
4
5
6
7
8
9
10
11
12
half4 frag(Varyings IN) : SV_Target
{
half4 albedo = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv) * _BaseColor;

half3 normalWS = normalize(IN.normalWS);
Light mainLight = GetMainLight();

half NdotL = saturate(dot(normalWS, mainLight.direction));
half3 diffuse = albedo.rgb * mainLight.color * NdotL;

return half4(diffuse, albedo.a);
}

13.4 带环境底色

1
2
3
half3 ambient = albedo.rgb * 0.2;
half3 finalColor = ambient + diffuse;
return half4(finalColor, albedo.a);

13.5 Half Lambert

1
2
half halfLambert = dot(normalWS, mainLight.direction) * 0.5 + 0.5;
half3 diffuse = albedo.rgb * mainLight.color * halfLambert;

13.6 Blinn-Phong 高光

1
2
3
4
5
6
7
half3 viewDirWS = GetWorldSpaceNormalizeViewDir(IN.positionWS);
half3 halfDir = normalize(mainLight.direction + viewDirWS);

half NdotH = saturate(dot(normalWS, halfDir));
half spec = pow(NdotH, _SpecPower) * _SpecIntensity;

half3 finalColor = diffuse + spec * mainLight.color;

Properties:

1
2
_SpecPower("Spec Power", Range(1, 128)) = 32
_SpecIntensity("Spec Intensity", Range(0, 4)) = 1

CBUFFER:

1
2
half _SpecPower;
half _SpecIntensity;

14. 附加光源与 Forward Plus

14.1 关键字

include:

1
2
3
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/RealtimeLights.hlsl"

如果这段 Shader 同时还要用 GetMainLightLightingLambert、阴影等,也可以直接 include:

1
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

关键字:

1
2
#pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
#pragma multi_compile _ _CLUSTER_LIGHT_LOOP // Unity 6 / 新 URP Forward+

14.2 基础附加光循环

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
37
38
39
40
41
half3 ApplySingleLight(half3 normalWS, half3 albedo, Light light)
{
half NdotL = saturate(dot(normalWS, light.direction));
half attenuation = light.distanceAttenuation * light.shadowAttenuation;
return albedo * light.color * NdotL * attenuation;
}

half3 ApplyAdditionalLights(float4 positionCS, float3 positionWS, half3 normalWS, half3 albedo)
{
half3 result = 0;

// Unity 6 / Forward+ 路径下,LIGHT_LOOP_BEGIN 需要当前作用域存在 inputData
InputData inputData = (InputData)0;
inputData.positionWS = positionWS;
inputData.normalWS = normalWS;
inputData.viewDirectionWS = GetWorldSpaceNormalizeViewDir(positionWS);
inputData.normalizedScreenSpaceUV = GetNormalizedScreenSpaceUV(positionCS);

#if defined(_ADDITIONAL_LIGHTS)

// Forward+ 里的非主方向光循环
#if USE_CLUSTER_LIGHT_LOOP
UNITY_LOOP for (uint lightIndex = 0; lightIndex < min(URP_FP_DIRECTIONAL_LIGHTS_COUNT, MAX_VISIBLE_LIGHTS); lightIndex++)
{
Light light = GetAdditionalLight(lightIndex, inputData.positionWS, half4(1, 1, 1, 1));
result += ApplySingleLight(inputData.normalWS, albedo, light);
}
#endif

// Forward 路径里的逐对象附加光循环
uint pixelLightCount = GetAdditionalLightsCount();

LIGHT_LOOP_BEGIN(pixelLightCount)
Light light = GetAdditionalLight(lightIndex, inputData.positionWS, half4(1, 1, 1, 1));
result += ApplySingleLight(inputData.normalWS, albedo, light);
LIGHT_LOOP_END

#endif

return result;
}

14.3 片元里合并主光和附加光

1
2
3
4
5
6
7
8
9
10
11
12
13
half4 frag(Varyings IN) : SV_Target
{
half4 albedo = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv) * _BaseColor;
half3 normalWS = normalize(IN.normalWS);

Light mainLight = GetMainLight();
half mainNdotL = saturate(dot(normalWS, mainLight.direction));
half3 color = albedo.rgb * mainLight.color * mainNdotL;

color += ApplyAdditionalLights(IN.positionCS, IN.positionWS, normalWS, albedo.rgb);

return half4(color, albedo.a);
}

14.4 Forward Plus 注意

1
2
3
4
5
6
7
Forward+ 对附加光的处理和普通 Forward 不完全一样
Unity 6 / 新 URP 使用 _CLUSTER_LIGHT_LOOP 变体
旧 URP 项目如果包内示例使用 _FORWARD_PLUS,就按当前项目包内宏为准
LIGHT_LOOP_BEGIN 所在作用域需要构造 InputData
至少补 positionWS / normalWS / viewDirectionWS / normalizedScreenSpaceUV
Forward+ 下 GetAdditionalLightsCount 可能返回 0,所以要处理 USE_CLUSTER_LIGHT_LOOP 分支
复杂项目里建议直接参考当前 URP 包里的 LitForwardPass.hlsl

14.5 附加光排查

1
2
3
4
5
6
URP Asset 是否开启 Additional Lights
灯光 Render Mode / Layer 是否允许
Shader 是否编译 _ADDITIONAL_LIGHTS
Forward+ 是否编译 _CLUSTER_LIGHT_LOOP 或当前 URP 包对应宏
GetAdditionalLightsCount 是否为 0
移动端附加光数量是否被 URP Asset 限制

15. 接收阴影

15.1 关键字

1
2
3
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN
#pragma multi_compile _ _ADDITIONAL_LIGHT_SHADOWS
#pragma multi_compile _ _SHADOWS_SOFT

15.2 顶点传世界坐标

1
2
3
4
5
6
struct Varyings
{
float4 positionCS : SV_POSITION;
float3 positionWS : TEXCOORD1;
half3 normalWS : TEXCOORD2;
};

15.3 片元计算主光阴影

1
2
3
4
5
6
7
8
9
10
11
12
13
half4 frag(Varyings IN) : SV_Target
{
half3 normalWS = normalize(IN.normalWS);

float4 shadowCoord = TransformWorldToShadowCoord(IN.positionWS);
Light mainLight = GetMainLight(shadowCoord);

half NdotL = saturate(dot(normalWS, mainLight.direction));
half shadow = mainLight.shadowAttenuation;

half3 color = _BaseColor.rgb * mainLight.color * NdotL * shadow;
return half4(color, _BaseColor.a);
}

15.4 阴影值含义

1
2
3
shadowAttenuation = 1      # 完全受光
shadowAttenuation = 0 # 完全阴影
0~1 # 软阴影或过滤后的过渡

15.5 接收阴影不显示排查

1
2
3
4
5
6
7
URP Asset 开了 Main Light Shadows
Light 开了 Cast Shadows
接收物体材质 Shader 编译了主光阴影关键字
投影物体有 ShadowCaster Pass
物体 Renderer 勾选 Cast Shadows
相机距离在 Shadow Distance 内
Layer 没被剔除

16. ShadowCaster Pass

16.1 什么时候需要

1
2
3
自定义 Shader 想投射阴影
Alpha Clip 物体想按透明轮廓投影
顶点动画物体想投影也跟着变形

16.2 简化 ShadowCaster Pass

这是方便理解和改 Alpha Clip 的简化版。正式项目如果遇到阴影偏移、级联阴影边界或平台差异,优先参考当前 URP 包里的 ShadowCasterPass.hlsl

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
Pass
{
Name "ShadowCaster"
Tags { "LightMode"="ShadowCaster" }

ZWrite On
ZTest LEqual
ColorMask 0
Cull Back

HLSLPROGRAM
#pragma vertex ShadowPassVertex
#pragma fragment ShadowPassFragment
#pragma multi_compile_vertex _ _CASTING_PUNCTUAL_LIGHT_SHADOW

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
float2 uv : TEXCOORD0;
};

struct Varyings
{
float4 positionCS : SV_POSITION;
float2 uv : TEXCOORD0;
};

TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);

CBUFFER_START(UnityPerMaterial)
float4 _BaseMap_ST;
half4 _BaseColor;
half _Cutoff;
CBUFFER_END

Varyings ShadowPassVertex(Attributes IN)
{
Varyings OUT;
OUT.positionCS = TransformObjectToHClip(IN.positionOS.xyz);
OUT.uv = TRANSFORM_TEX(IN.uv, _BaseMap);
return OUT;
}

half4 ShadowPassFragment(Varyings IN) : SV_Target
{
half alpha = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv).a * _BaseColor.a;
clip(alpha - _Cutoff); // Alpha Clip 阴影轮廓
return 0;
}
ENDHLSL
}

16.3 顶点动画必须同步到 ShadowCaster

Forward Pass:

1
2
3
4
5
6
float3 ApplyWind(float3 positionOS, float3 normalOS)
{
float wave = sin(_Time.y * _WindSpeed + positionOS.x * _WindFrequency);
positionOS += normalOS * wave * _WindStrength;
return positionOS;
}

ShadowCaster Pass 也要调用:

1
2
float3 animatedPosOS = ApplyWind(IN.positionOS.xyz, IN.normalOS);
OUT.positionCS = TransformObjectToHClip(animatedPosOS);

不然会出现:

1
2
3
模型在动,影子不动
影子轮廓和模型对不上
草和树叶阴影穿帮

17. 法线、切线与 Normal Map

17.1 需要的顶点输入

1
2
3
4
5
6
7
struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
float4 tangentOS : TANGENT;
float2 uv : TEXCOORD0;
};

17.2 顶点输出 TBN

1
2
3
4
5
6
7
8
struct Varyings
{
float4 positionCS : SV_POSITION;
float2 uv : TEXCOORD0;
half3 normalWS : TEXCOORD1;
half3 tangentWS : TEXCOORD2;
half3 bitangentWS : TEXCOORD3;
};

17.3 顶点阶段

1
2
3
4
VertexNormalInputs normalInput = GetVertexNormalInputs(IN.normalOS, IN.tangentOS);
OUT.normalWS = normalInput.normalWS;
OUT.tangentWS = normalInput.tangentWS;
OUT.bitangentWS = normalInput.bitangentWS;

17.4 片元阶段切线空间转世界空间

1
2
3
4
5
6
7
8
9
half3 normalTS = UnpackNormal(SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, IN.uv));

half3x3 tangentToWorld = half3x3(
normalize(IN.tangentWS),
normalize(IN.bitangentWS),
normalize(IN.normalWS)
);

half3 normalWS = normalize(mul(normalTS, tangentToWorld));

17.5 法线贴图强度

1
2
3
half3 normalTS = UnpackNormal(SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, IN.uv));
normalTS.xy *= _NormalScale;
normalTS.z = sqrt(1.0 - saturate(dot(normalTS.xy, normalTS.xy)));

Properties:

1
2
[Normal] _NormalMap("Normal Map", 2D) = "bump" {}
_NormalScale("Normal Scale", Range(0, 2)) = 1

17.6 法线贴图排查

1
2
3
4
5
6
贴图 Texture Type 设置为 Normal Map
模型有 tangent 数据
Shader 输入 TANGENT
TBN 顺序正确
法线最终 normalize
移动端 normal map 过多会增加采样成本

18. Fog 雾效

18.1 顶点输出 fogCoord

1
2
3
4
5
struct Varyings
{
float4 positionCS : SV_POSITION;
float fogCoord : TEXCOORD1;
};

18.2 顶点阶段计算

1
OUT.fogCoord = ComputeFogFactor(OUT.positionCS.z);

18.3 片元阶段混合雾

1
2
3
half3 color = _BaseColor.rgb;
color = MixFog(color, IN.fogCoord);
return half4(color, _BaseColor.a);

18.4 常见忘记项

1
2
3
4
项目 Lighting / Environment 开了 Fog
Shader 里计算 fogCoord
Shader 里调用 MixFog
透明材质雾效根据效果决定是否加

19. 深度纹理与屏幕空间

19.1 采样深度前检查

1
2
3
4
URP Asset -> General -> Depth Texture 开启
Camera 没禁用 Depth Texture
目标物体是否写入深度
透明物体通常不会出现在常规深度里

19.2 include

1
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"

19.3 当前像素屏幕 UV

1
float2 screenUV = IN.positionCS.xy / _ScaledScreenParams.xy;

19.4 采样 Scene Depth

1
real rawDepth = SampleSceneDepth(screenUV);

19.5 平台兼容深度

1
2
3
4
5
#if UNITY_REVERSED_Z
real depth = SampleSceneDepth(screenUV);
#else
real depth = lerp(UNITY_NEAR_CLIP_VALUE, 1, SampleSceneDepth(screenUV));
#endif

19.6 重建世界坐标

1
float3 worldPos = ComputeWorldSpacePosition(screenUV, depth, UNITY_MATRIX_I_VP);

19.7 深度差边缘泡沫

1
2
3
4
5
6
7
8
float2 screenUV = IN.positionCS.xy / _ScaledScreenParams.xy;
real sceneDepth = SampleSceneDepth(screenUV);

float sceneEyeDepth = LinearEyeDepth(sceneDepth, _ZBufferParams);
float thisEyeDepth = IN.positionCS.w;

half depthDiff = saturate((sceneEyeDepth - thisEyeDepth) / _FoamDistance);
half foam = 1 - depthDiff;

用途:

1
2
3
4
水边泡沫
角色脚底接触光
能量罩与场景交界
软粒子

19.8 Opaque Texture 采样

检查:

1
2
URP Asset -> General -> Opaque Texture 开启
通常用于 Transparent Queue

include:

1
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareOpaqueTexture.hlsl"

采样:

1
2
float2 screenUV = IN.positionCS.xy / _ScaledScreenParams.xy;
half3 sceneColor = SampleSceneColor(screenUV);

19.9 屏幕扰动

1
2
3
4
half2 noise = SAMPLE_TEXTURE2D(_NoiseMap, sampler_NoiseMap, IN.uv).rg * 2 - 1;
float2 distortUV = screenUV + noise * _DistortStrength;
half3 sceneColor = SampleSceneColor(distortUV);
return half4(sceneColor, _Alpha);

20. 常见效果片段

20.1 溶解 Dissolve

Properties:

1
2
3
4
_NoiseMap("Noise", 2D) = "white" {}
_Dissolve("Dissolve", Range(0, 1)) = 0
_EdgeWidth("Edge Width", Range(0, 0.2)) = 0.05
[HDR] _EdgeColor("Edge Color", Color) = (1, 0.5, 0, 1)

HLSL:

1
2
3
4
5
6
half noise = SAMPLE_TEXTURE2D(_NoiseMap, sampler_NoiseMap, IN.uv).r;
clip(noise - _Dissolve);

half edge = smoothstep(_Dissolve, _Dissolve + _EdgeWidth, noise);
half3 color = lerp(_EdgeColor.rgb, baseColor.rgb, edge);
return half4(color, baseColor.a);

20.2 边缘光 Rim

1
2
3
4
5
6
half3 viewDirWS = GetWorldSpaceNormalizeViewDir(IN.positionWS);
half rim = 1 - saturate(dot(normalize(IN.normalWS), viewDirWS));
rim = pow(rim, _RimPower) * _RimIntensity;

half3 finalColor = baseColor.rgb + rim * _RimColor.rgb;
return half4(finalColor, baseColor.a);

Properties:

1
2
3
[HDR] _RimColor("Rim Color", Color) = (0, 0.6, 1, 1)
_RimPower("Rim Power", Range(0.5, 8)) = 3
_RimIntensity("Rim Intensity", Range(0, 5)) = 1

20.3 Fresnel

1
2
half fresnel = pow(1 - saturate(dot(normalWS, viewDirWS)), _FresnelPower);
half3 finalColor = lerp(baseColor.rgb, _FresnelColor.rgb, fresnel * _FresnelIntensity);

20.4 UV 滚动

1
2
3
float2 uv = IN.uv;
uv += _ScrollDirection.xy * _ScrollSpeed * _Time.y;
half4 col = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, uv);

Properties:

1
2
_ScrollDirection("Scroll Direction", Vector) = (1, 0, 0, 0)
_ScrollSpeed("Scroll Speed", Float) = 1

20.5 扫光

1
2
3
half band = abs(frac(IN.uv.x + _Time.y * _ScanSpeed) - 0.5) * 2;
half scan = 1 - smoothstep(0, _ScanWidth, band);
half3 finalColor = baseColor.rgb + scan * _ScanColor.rgb * _ScanIntensity;

20.6 Toon 阶梯光

1
2
3
4
5
6
half NdotL = saturate(dot(normalWS, mainLight.direction));
half toon = smoothstep(_ShadowStep, _ShadowStep + _ShadowFeather, NdotL);

half3 shadowColor = baseColor.rgb * _ShadowColor.rgb;
half3 litColor = baseColor.rgb * mainLight.color;
half3 finalColor = lerp(shadowColor, litColor, toon);

Properties:

1
2
3
_ShadowStep("Shadow Step", Range(0, 1)) = 0.5
_ShadowFeather("Shadow Feather", Range(0.001, 0.5)) = 0.05
_ShadowColor("Shadow Color", Color) = (0.5, 0.5, 0.5, 1)

20.7 外描边 Pass

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
37
38
39
40
41
42
43
Pass
{
Name "Outline"
Tags { "LightMode"="SRPDefaultUnlit" }

Cull Front
ZWrite On

HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

CBUFFER_START(UnityPerMaterial)
half4 _OutlineColor;
half _OutlineWidth;
CBUFFER_END

struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
};

struct Varyings
{
float4 positionCS : SV_POSITION;
};

Varyings vert(Attributes IN)
{
Varyings OUT;
float3 posOS = IN.positionOS.xyz + normalize(IN.normalOS) * _OutlineWidth;
OUT.positionCS = TransformObjectToHClip(posOS);
return OUT;
}

half4 frag(Varyings IN) : SV_Target
{
return _OutlineColor;
}
ENDHLSL
}

描边注意:

1
2
3
4
法线外扩描边对硬边模型可能裂
屏幕空间等宽描边更复杂
透明物体描边排序更麻烦
角色描边通常需要专门处理法线

20.8 XRay 透视轮廓

Pass 状态:

1
2
3
ZTest Greater
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha

片元:

1
return half4(_XRayColor.rgb, _XRayAlpha);

20.9 顶点波动

1
2
3
4
float3 posOS = IN.positionOS.xyz;
float wave = sin(_Time.y * _WaveSpeed + posOS.x * _WaveFrequency);
posOS.y += wave * _WaveStrength;
OUT.positionCS = TransformObjectToHClip(posOS);

20.10 草/旗帜简易风

1
2
3
4
5
float heightMask = saturate(IN.positionOS.y); // 越高摆动越大,按模型调整
float wave = sin(_Time.y * _WindSpeed + IN.positionOS.x * _WindFrequency);
float3 posOS = IN.positionOS.xyz;
posOS.xz += wave * _WindStrength * heightMask;
OUT.positionCS = TransformObjectToHClip(posOS);

20.11 闪烁

1
2
half blink = sin(_Time.y * _BlinkSpeed) * 0.5 + 0.5;
half3 finalColor = lerp(baseColor.rgb, _BlinkColor.rgb, blink * _BlinkIntensity);

20.12 网格线

1
2
3
float2 grid = abs(frac(IN.uv * _GridScale) - 0.5);
float line = 1 - smoothstep(_LineWidth, _LineWidth + _LineFeather, min(grid.x, grid.y));
half3 finalColor = lerp(baseColor.rgb, _LineColor.rgb, line);

21. C# 传参和 MaterialPropertyBlock

21.1 直接改材质

1
2
3
4
5
6
7
8
[SerializeField] private Material material;

private static readonly int BaseColorId = Shader.PropertyToID("_BaseColor");

private void SetColor(Color color)
{
material.SetColor(BaseColorId, color); // 会修改这个 Material 资源或实例
}

注意:

1
2
3
改 sharedMaterial 会影响所有共用材质的对象
renderer.material 会实例化材质,可能增加内存
大量对象单独参数优先 MaterialPropertyBlock

21.2 MaterialPropertyBlock

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using UnityEngine;

public sealed class SetShaderColor : MonoBehaviour
{
[SerializeField] private Renderer targetRenderer;
[SerializeField] private Color color = Color.cyan;

private static readonly int BaseColorId = Shader.PropertyToID("_BaseColor");
private MaterialPropertyBlock block;

private void Awake()
{
block = new MaterialPropertyBlock();
}

private void Apply()
{
targetRenderer.GetPropertyBlock(block);
block.SetColor(BaseColorId, color);
targetRenderer.SetPropertyBlock(block);
}
}

21.3 常用 Set 方法

1
2
3
4
block.SetFloat(Shader.PropertyToID("_Dissolve"), value);
block.SetColor(Shader.PropertyToID("_RimColor"), color);
block.SetVector(Shader.PropertyToID("_ScrollDirection"), direction);
block.SetTexture(Shader.PropertyToID("_BaseMap"), texture);

21.4 开关关键字

1
2
material.EnableKeyword("_USE_RIM");
material.DisableKeyword("_USE_RIM");

URP 新项目也可以使用 Local Keyword API:

1
2
var keyword = new UnityEngine.Rendering.LocalKeyword(material.shader, "_USE_RIM");
material.SetKeyword(keyword, true);

21.5 不同对象不同溶解值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public sealed class DissolveSetter : MonoBehaviour
{
[SerializeField] private Renderer target;
[Range(0, 1)] public float dissolve;

private static readonly int DissolveId = Shader.PropertyToID("_Dissolve");
private MaterialPropertyBlock block;

private void Awake()
{
block = new MaterialPropertyBlock();
}

private void LateUpdate()
{
target.GetPropertyBlock(block);
block.SetFloat(DissolveId, dissolve);
target.SetPropertyBlock(block);
}
}

21.6 全局 Shader 参数

1
2
3
Shader.SetGlobalFloat("_GlobalWindStrength", windStrength);
Shader.SetGlobalVector("_GlobalWindDirection", windDirection);
Shader.SetGlobalTexture("_GlobalNoiseTex", noiseTexture);

HLSL:

1
2
3
4
float _GlobalWindStrength;
float4 _GlobalWindDirection;
TEXTURE2D(_GlobalNoiseTex);
SAMPLER(sampler_GlobalNoiseTex);

注意:

1
2
3
全局参数影响所有使用同名变量的 Shader
适合风、天气、时间、全局雾、全局交互点
不要拿全局参数传单个角色的临时状态

22. Shader Keywords 与变体

22.1 shader_feature 和 multi_compile

1
2
#pragma shader_feature_local _USE_RIM          // 材质不用时可剔除变体
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS // 管线功能需要,通常不能随便剔除

22.2 常用选择

需求 推荐
材质功能开关 shader_feature_local
管线光照阴影开关 multi_compile
全局质量等级 multi_compile 或项目自定义剔除
只在当前 Shader 内使用 _local

22.3 代码中使用

1
2
3
#if defined(_USE_RIM)
color += ApplyRim(normalWS, viewDirWS);
#endif

22.4 Properties 里做 Toggle

1
[Toggle(_USE_RIM)] _UseRim("Use Rim", Float) = 0

HLSL:

1
#pragma shader_feature_local _USE_RIM

22.5 常见 URP 光照关键字

1
2
3
4
5
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN
#pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
#pragma multi_compile _ _ADDITIONAL_LIGHT_SHADOWS
#pragma multi_compile _ _SHADOWS_SOFT
#pragma multi_compile _ _CLUSTER_LIGHT_LOOP

22.6 变体爆炸

1
2
3
4
5
2 个开关 = 4 个变体
5 个开关 = 32 个变体
10 个开关 = 1024 个变体
每个 Pass 都会受影响
平台越多,编译越久,包体越大

22.7 减少变体

1
2
3
4
5
6
能用数值分支就别加关键字
同一类效果合并成一个 enum
少在通用 Shader 里塞太多功能
移动端单独做轻量 Shader
URP Asset 关闭不用的光照阴影功能
构建前清理没用材质

23. 性能优化速查

23.1 片元比顶点贵的场景

1
2
3
4
5
6
全屏特效
大面积透明
多层粒子
高分辨率移动端
Overdraw 高的 UI / 特效
复杂光照和多贴图采样

23.2 常见成本排序

1
2
3
4
5
6
7
8
纹理采样多
透明 Overdraw
动态分支
pow / sin / cos 等复杂函数
多光源循环
高精度 float 过多
大量变体
复杂 ShadowCaster

23.3 移动端建议

1
2
3
4
5
6
7
颜色和光照优先 half
少用透明叠很多层
少用实时多光源
法线贴图、Mask 图合并通道
少用高频 Grab/Opaque Texture
少用屏幕空间多次采样
少用复杂噪声实时算,优先噪声贴图

23.4 贴图采样优化

1
2
3
4
5
BaseMap + MaskMap + NormalMap 已经是 3 次采样
Mask 多通道打包,减少贴图数量
同采样器复用
不需要 Tiling Offset 的贴图用 [NoScaleOffset]
特效图尽量压缩、降分辨率

23.5 透明优化

1
2
3
4
5
能 Alpha Clip 就别半透明
粒子贴图留白越少越好
大面片拆小,减少无效透明区域
关闭不必要 ZWrite
需要软粒子才采深度

23.6 分支优化

1
2
3
4
5
6
7
8
// 不一定更快,取决于平台和分支一致性
if (_UseRim > 0.5)
{
color += rim;
}

// 简单开关可用 lerp,避免动态分支
color += rim * _UseRim;

23.7 Debug 工具

1
2
3
4
5
6
Frame Debugger                 # 看 Pass 顺序、SetPass、Batch
RenderDoc # 抓帧看纹理、Shader 输入输出
Profiler Rendering # 看 CPU/GPU 渲染成本
Scene View Overdraw # 看透明重叠
Material Inspector # 看 SRP Batcher compatible
Shader Variant Log # 看变体数量

24. 常见报错排查

24.1 材质变粉

1
2
3
4
5
6
Shader 编译失败
URP 项目用了 Built-in Shader
没写 RenderPipeline UniversalPipeline
include 路径错误
HLSL 函数或变量名不存在
当前 URP 版本不支持某函数签名

优先看:

1
2
3
4
5
Console 第一条 Shader error
报错文件和行号
当前使用的 URP 包版本
Shader 是否真的保存
材质是否引用了正确 Shader

24.2 找不到 Core.hlsl

1
2
3
4
项目没安装 URP
include 路径拼错
包名写错
当前项目不是 URP 管线

正确路径:

1
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

24.3 undeclared identifier TransformObjectToHClip

1
2
3
4
没 include Core.hlsl
include 写在函数后面
拼写错误
用了 CGPROGRAM 而不是 HLSLPROGRAM

24.4 sampler2D / tex2D 老写法问题

推荐替换:

1
2
3
TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);
half4 col = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, uv);

24.5 SRP Batcher Not Compatible

检查:

1
2
3
4
所有材质属性是否在 UnityPerMaterial
多个 Pass 的 UnityPerMaterial 是否一致
是否散放了 _BaseColor / _Cutoff 等属性
是否用了不兼容的 CBUFFER 布局

24.6 贴图 Tiling Offset 不生效

检查:

1
2
3
4
Properties 里贴图没有 [NoScaleOffset]
CBUFFER 有 _BaseMap_ST
顶点阶段用了 TRANSFORM_TEX(IN.uv, _BaseMap)
变量名严格匹配:_BaseMap -> _BaseMap_ST

24.7 透明显示顺序错

1
2
3
4
5
6
半透明不写深度,排序按对象中心和队列
拆分网格
调整 Render Queue
减少透明层叠
改 Alpha Clip
必要时写 Depth Prepass

24.8 阴影没有

1
2
3
4
5
6
7
8
接收阴影:Forward Pass 里算 shadowAttenuation
投射阴影:Shader 有 ShadowCaster Pass
URP Asset 开 Shadows
Light 开 Cast Shadows
Renderer Cast Shadows 打开
物体 Layer 被 Light 和 Camera 渲染
Shadow Distance 足够
Alpha Clip 阴影 Pass 也做 clip

24.9 法线贴图怪

1
2
3
4
5
6
贴图导入类型不是 Normal Map
模型没有 Tangent
TBN 矩阵方向错
normalTS 没 UnpackNormal
normalWS 没 normalize
法线强度过大

24.10 Scene Depth 采样全白或全黑

1
2
3
4
5
URP Asset 没开 Depth Texture
当前 Camera 没生成深度
透明物体不在深度里
深度没处理 UNITY_REVERSED_Z
用了 _ScreenParams 但动态分辨率下 UV 不对

25. Built-in 到 URP 迁移

25.1 关键替换表

Built-in URP
CGPROGRAM HLSLPROGRAM
ENDCG ENDHLSL
UnityCG.cginc Core.hlsl
Lighting.cginc Lighting.hlsl
appdata Attributes
v2f Varyings
UnityObjectToClipPos(v.vertex) TransformObjectToHClip(positionOS.xyz)
sampler2D TEXTURE2D + SAMPLER
tex2D SAMPLE_TEXTURE2D
fixed4 half4
散放材质属性 CBUFFER_START(UnityPerMaterial)

25.2 迁移步骤

1
2
3
4
5
6
7
8
9
改 HLSLPROGRAM / ENDHLSL
include Core.hlsl
SubShader Tags 加 RenderPipeline UniversalPipeline
appdata/v2f 改 Attributes/Varyings
坐标变换改 TransformObjectToHClip
贴图采样改 TEXTURE2D / SAMPLE_TEXTURE2D
材质属性放 UnityPerMaterial
需要光照就 include Lighting.hlsl
需要阴影就补关键字和 ShadowCaster Pass

25.3 Built-in 老代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
CGPROGRAM
#include "UnityCG.cginc"

sampler2D _MainTex;
fixed4 _Color;

v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
return o;
}

fixed4 frag(v2f i) : SV_Target
{
return tex2D(_MainTex, i.uv) * _Color;
}
ENDCG

25.4 URP 新代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
HLSLPROGRAM
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);

CBUFFER_START(UnityPerMaterial)
float4 _BaseMap_ST;
half4 _BaseColor;
CBUFFER_END

Varyings vert(Attributes IN)
{
Varyings OUT;
OUT.positionCS = TransformObjectToHClip(IN.positionOS.xyz);
OUT.uv = TRANSFORM_TEX(IN.uv, _BaseMap);
return OUT;
}

half4 frag(Varyings IN) : SV_Target
{
return SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv) * _BaseColor;
}
ENDHLSL

26. 写完 Shader 自查清单

26.1 基础检查

1
2
3
4
5
6
7
材质不粉
Console 没 Shader error
RenderPipeline Tag 正确
LightMode Tag 正确
Properties 和 CBUFFER 变量名一致
贴图 _ST 和 TRANSFORM_TEX 正确
SRP Batcher Compatible

26.2 渲染检查

1
2
3
4
5
6
7
8
Opaque / Transparent / AlphaTest 队列正确
ZWrite 是否符合预期
Blend 是否符合预期
Cull 是否符合预期
阴影投射是否正常
阴影接收是否正常
Depth Texture / Opaque Texture 依赖是否打开
Forward / Forward+ 都测试过

26.3 平台检查

1
2
3
4
5
Game View 和 Scene View 都看过
目标平台真机跑过
移动端半精度表现正常
OpenGL / Vulkan / D3D 深度方向处理正确
动态分辨率下屏幕 UV 正确

26.4 性能检查

1
2
3
4
5
6
贴图采样次数可接受
透明 Overdraw 可接受
关键字数量可控
Frame Debugger Pass 数正常
Profiler 没异常 GPU 峰值
变体编译时间可接受

26.5 团队维护检查

1
2
3
4
5
6
变量命名符合项目规范
Shader 名字路径清楚
材质参数 Tooltip / 名字能看懂
特效参数范围合理
公共 HLSL include 没乱放业务逻辑
复杂效果有注释

27. 常用完整模板:Alpha Clip Lit

下面是一个偏实用的 Alpha Clip + 主光 + 阴影模板,适合草、树叶、纸片、简单角色局部材质继续改。

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
Shader "Xiaoyun/URP/AlphaClipMainLight"
{
Properties
{
[MainTexture] _BaseMap("Base Map", 2D) = "white" {}
[MainColor] _BaseColor("Base Color", Color) = (1,1,1,1)
_Cutoff("Alpha Cutoff", Range(0, 1)) = 0.5
}

SubShader
{
Tags
{
"RenderPipeline"="UniversalPipeline"
"RenderType"="TransparentCutout"
"Queue"="AlphaTest"
}

Pass
{
Name "ForwardLit"
Tags { "LightMode"="UniversalForward" }

Cull Back
ZWrite On
ZTest LEqual

HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN
#pragma multi_compile _ _SHADOWS_SOFT

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);

CBUFFER_START(UnityPerMaterial)
float4 _BaseMap_ST;
half4 _BaseColor;
half _Cutoff;
CBUFFER_END

struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
float2 uv : TEXCOORD0;
};

struct Varyings
{
float4 positionCS : SV_POSITION;
float2 uv : TEXCOORD0;
float3 positionWS : TEXCOORD1;
half3 normalWS : TEXCOORD2;
};

Varyings vert(Attributes IN)
{
Varyings OUT;
VertexPositionInputs posInput = GetVertexPositionInputs(IN.positionOS.xyz);
VertexNormalInputs normalInput = GetVertexNormalInputs(IN.normalOS);

OUT.positionCS = posInput.positionCS;
OUT.positionWS = posInput.positionWS;
OUT.normalWS = normalInput.normalWS;
OUT.uv = TRANSFORM_TEX(IN.uv, _BaseMap);
return OUT;
}

half4 frag(Varyings IN) : SV_Target
{
half4 baseCol = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv) * _BaseColor;
clip(baseCol.a - _Cutoff);

half3 normalWS = normalize(IN.normalWS);
float4 shadowCoord = TransformWorldToShadowCoord(IN.positionWS);
Light mainLight = GetMainLight(shadowCoord);

half NdotL = saturate(dot(normalWS, mainLight.direction));
half3 ambient = baseCol.rgb * 0.2;
half3 diffuse = baseCol.rgb * mainLight.color * NdotL * mainLight.shadowAttenuation;

return half4(ambient + diffuse, baseCol.a);
}
ENDHLSL
}
}
}

28. 官方参考入口

建议实际项目里同时打开:

1
2
3
4
Packages/com.unity.render-pipelines.universal/Shaders/Lit.shader
Packages/com.unity.render-pipelines.universal/Shaders/LitForwardPass.hlsl
Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl
Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl

学习顺序:

1
2
3
4
5
6
7
先写 Unlit
再写 Texture + Color
再写 Alpha Clip
再写 Main Light
再写 ShadowCaster
再写 Rim / Dissolve / Outline
最后看 Lit.shader 学 PBR 和复杂 Pass