Unity Shader编程完全入门指南:从零到实战 C#
提示:内容纯个人编写,欢迎评论点赞。
文章目录
- Unity Shader编程完全入门指南:从零到实战 C#
- [1. Shader基础概念](#1. Shader基础概念)
-
-
- [1.1 什么是Shader?](#1.1 什么是Shader?)
- [1.2 Shader在游戏开发中的作用](#1.2 Shader在游戏开发中的作用)
- [1.3 Unity中的Shader类型](#1.3 Unity中的Shader类型)
-
- [2. ShaderLab语法入门](#2. ShaderLab语法入门)
-
-
- [2.1 基本结构](#2.1 基本结构)
- [2.2 Properties属性块](#2.2 Properties属性块)
- [2.3 SubShader与Pass](#2.3 SubShader与Pass)
- [2.4 常用渲染指令](#2.4 常用渲染指令)
-
- [3. 表面着色器(Surface Shader)实战](#3. 表面着色器(Surface Shader)实战)
-
-
- [3.1 表面着色器结构](#3.1 表面着色器结构)
- [3.2 实现漫反射光照](#3.2 实现漫反射光照)
- [3.3 添加纹理贴图](#3.3 添加纹理贴图)
-
- [4. 顶点/片元着色器(Vertex/Fragment Shader)实战](#4. 顶点/片元着色器(Vertex/Fragment Shader)实战)
-
-
- [4.1 基本结构](#4.1 基本结构)
- [4.2 实现顶点动画](#4.2 实现顶点动画)
- [4.3 片元着色特效](#4.3 片元着色特效)
-
- [5. 常见问题与解决方案](#5. 常见问题与解决方案)
-
-
- [5.1 性能优化](#5.1 性能优化)
- [5.2 平台兼容性问题](#5.2 平台兼容性问题)
- [5.3 调试技巧](#5.3 调试技巧)
-
- [6. 实战案例:水波特效](#6. 实战案例:水波特效)
-
-
- [6.1 效果分析](#6.1 效果分析)
- [6.2 数学原理](#6.2 数学原理)
- [6.3 完整代码实现](#6.3 完整代码实现)
-
- [7. 实战案例:卡通渲染](#7. 实战案例:卡通渲染)
-
-
- [7.1 效果分析](#7.1 效果分析)
- [7.2 边缘检测实现](#7.2 边缘检测实现)
- [7.3 色块化处理](#7.3 色块化处理)
-
- [8. 进阶学习资源](#8. 进阶学习资源)
-
-
- [8.1 官方文档](#8.1 官方文档)
- [8.2 推荐书目](#8.2 推荐书目)
- [8.3 社区资源](#8.3 社区资源)
-
1. Shader基础概念
1.1 什么是Shader?
Shader(着色器)是一类运行在GPU上的特殊程序,用于控制图形渲染管线的各个阶段。它决定了模型的顶点如何变换到屏幕空间,以及每个像素如何着色
csharp
graph LR
A[顶点数据] --> B[顶点着色器]
B --> C[图元装配]
C --> D[几何着色器]
D --> E[光栅化]
E --> F[片元着色器]
F --> G[帧缓冲输出]
1.2 Shader在游戏开发中的作用
- 材质外观控制:金属、木质、皮肤等材质的视觉效果
- 特效实现:水波、火焰、全息投影等特殊效果
- 性能优化:通过减少draw call提升渲染效率
- 艺术风格:卡通渲染、像素风等独特视觉风格
1.3 Unity中的Shader类型

2. ShaderLab语法入门
2.1 基本结构
csharp
Shader "Custom/ExampleShader"
{
Properties
{
// 属性声明
}
SubShader
{
Tags { "RenderType"="Opaque" }
Pass
{
// 渲染指令
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// CG代码
ENDCG
}
}
FallBack "Diffuse"
}
2.2 Properties属性块
csharp
Properties
{
_Color ("Main Color", Color) = (1,1,1,1)
_MainTex ("Base (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
_BumpMap ("Normal Map", 2D) = "bump" {}
}
2.3 SubShader与Pass
- SubShader:针对不同显卡配置的备选方案
- Pass:一次完整的渲染流程,一个SubShader可包含多个Pass
2.4 常用渲染指令
csharp
Cull Back // 背面剔除
ZWrite On // 深度写入
Blend SrcAlpha OneMinusSrcAlpha // 透明混合
LOD 200 // 细节级别
3. 表面着色器(Surface Shader)实战
3.1 表面着色器结构
csharp
Shader "Custom/SurfaceExample"
{
Properties { /* 属性声明 */ }
SubShader
{
Tags { "RenderType"="Opaque" }
CGPROGRAM
#pragma surface surf Standard
struct Input
{
float2 uv_MainTex;
};
void surf (Input IN, inout SurfaceOutputStandard o)
{
// 表面着色逻辑
}
ENDCG
}
}
3.2 实现漫反射光照
csharp
void surf (Input IN, inout SurfaceOutputStandard o)
{
fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
o.Alpha = c.a;
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
}
3.3 添加纹理贴图
csharp
Properties {
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_NormalMap ("Normal Map", 2D) = "bump" {}
}
struct Input {
float2 uv_MainTex;
float2 uv_NormalMap;
};
void surf (Input IN, inout SurfaceOutputStandard o) {
fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Normal = UnpackNormal(tex2D(_NormalMap, IN.uv_NormalMap));
}
4. 顶点/片元着色器(Vertex/Fragment Shader)实战
4.1 基本结构
csharp
Shader "Custom/VFExample"
{
Properties { _Color("Color", Color) = (1,0,0,1) }
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct appdata
{
float4 vertex : POSITION;
};
struct v2f
{
float4 pos : SV_POSITION;
};
v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
return _Color;
}
ENDCG
}
}
}
4.2 实现顶点动画
csharp
v2f vert (appdata v)
{
v2f o;
// 正弦波动效果
float wave = sin(_Time.y * 5 + v.vertex.x * 10) * 0.1;
v.vertex.y += wave;
o.pos = UnityObjectToClipPos(v.vertex);
return o;
}
csharp
初始状态: 波动状态:
--- /\/\/\
--- /\/\/\/\
--- /\/\/\/\/\
4.3 片元着色特效
csharp
fixed4 frag (v2f i) : SV_Target
{
// 创建UV动画
float2 uv = i.uv + float2(_Time.x * 0.1, _Time.y * 0.2);
// 生成噪点效果
float noise = frac(sin(dot(uv, float2(12.9898,78.233))) * 43758.5453);
// 混合颜色
fixed4 col = tex2D(_MainTex, uv);
col.rgb += noise * 0.1;
// 添加边缘光
float rim = 1.0 - saturate(dot(i.normal, i.viewDir));
col.rgb += pow(rim, 5) * _RimColor;
return col;
}
5. 常见问题与解决方案
5.1 性能优化
- 减少数学运算:
- 避免使用sin、cos等复杂函数
- 使用mad指令优化计算(a*b+c)
- 纹理采样优化:
csharp
// 避免多次采样相同纹理
fixed4 col = tex2D(_MainTex, uv);
fixed4 normal = UnpackNormal(tex2D(_NormalMap, uv));
// 使用纹理合并(RGB存储不同数据)
- 条件语句优化:
csharp
// 避免分支语句
float value = a > b ? 1.0 : 0.0; // 不推荐
float value = saturate(sign(a - b)); // 推荐`在这里插入代码片`
5.2 平台兼容性问题
csharp
// 1. 精度问题
#ifdef GL_ES
precision mediump float;
#endif
// 2. 坐标系差异
float4 clipPos = mul(UNITY_MATRIX_VP, mul(UNITY_MATRIX_M, v.vertex));
// 3. 特性支持检查
#if defined(SHADER_API_D3D11) || defined(SHADER_API_GLES3)
// 使用高级特性
#else
// 回退方案
#endif
5.3 调试技巧
- 颜色输出调试法:
csharp
// 可视化法线方向
return float4(i.normal * 0.5 + 0.5, 1.0);
// 显示UV坐标
return float4(i.uv, 0, 1);
- Unity帧调试器:
- Window > Analysis > Frame Debugger
- 逐步查看渲染过程
- RenderDoc工具:
- 捕获帧数据
- 分析渲染管线每个阶段的状态
6. 实战案例:水波特效
6.1 效果分析
- 水面波动:正弦波叠加
- 折射效果:法线扰动+屏幕纹理采样
- 高光反射:菲涅尔反射
6.2 数学原理
水波函数:
csharp
y = A \times sin(\frac{2\pi}{T} \times t + \frac{2\pi}{\lambda} \times x)
其中:
A = 振幅
T = 周期
λ = 波长
6.3 完整代码实现
csharp
Shader "Custom/WaterShader"
{
Properties
{
_Color ("Water Color", Color) = (0.2, 0.6, 1, 0.8)
_MainTex ("Base (RGB)", 2D) = "white" {}
_NormalMap ("Normal Map", 2D) = "bump" {}
_WaveSpeed ("Wave Speed", Float) = 1.0
_WaveHeight ("Wave Height", Float) = 0.1
_WaveFrequency ("Wave Frequency", Float) = 1.0
_RefractionIntensity ("Refraction", Range(0,1)) = 0.1
_Specular ("Specular", Range(0,1)) = 0.5
}
SubShader
{
Tags { "Queue"="Transparent" "RenderType"="Transparent" }
LOD 300
GrabPass { "_RefractionTex" }
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 pos : SV_POSITION;
float4 grabPos : TEXCOORD0;
float2 uv : TEXCOORD1;
float3 viewDir : TEXCOORD2;
float3 normal : NORMAL;
};
sampler2D _MainTex;
sampler2D _NormalMap;
sampler2D _RefractionTex;
float4 _Color;
float _WaveSpeed;
float _WaveHeight;
float _WaveFrequency;
float _RefractionIntensity;
float _Specular;
v2f vert (appdata v)
{
v2f o;
// 顶点动画 - 正弦波
float wave = sin(_Time.y * _WaveSpeed + v.vertex.x * _WaveFrequency);
v.vertex.y += wave * _WaveHeight;
o.pos = UnityObjectToClipPos(v.vertex);
o.grabPos = ComputeGrabScreenPos(o.pos);
o.uv = v.uv;
o.viewDir = normalize(WorldSpaceViewDir(v.vertex));
o.normal = UnityObjectToWorldNormal(v.normal);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// 法线贴图扰动
float3 normal = UnpackNormal(tex2D(_NormalMap, i.uv));
// 折射效果
float2 refraction = normal.xy * _RefractionIntensity;
i.grabPos.xy += refraction;
fixed4 refrCol = tex2Dproj(_RefractionTex, i.grabPos);
// 菲涅尔反射
float fresnel = 1.0 - saturate(dot(i.normal, i.viewDir));
float specular = SPECULAR_STRENGTH * pow(fresnel, _Specular);
// 最终颜色混合
fixed4 baseCol = tex2D(_MainTex, i.uv) * _Color;
fixed4 finalCol = lerp(refrCol, baseCol, 0.7);
finalCol.rgb += specular;
return finalCol;
}
ENDCG
}
}
FallBack "Transparent/Diffuse"
}
水波特效效果:
csharp
初始水面: 波动效果: 折射效果:
~~~~ ~~~~~~ ~~~~~~
~~~~ ~/\/\/~ ~/~~\/~
~~~~ /~~~~~~ /~~~~~~
7. 实战案例:卡通渲染
7.1 效果分析
- 硬朗的明暗分界:离散化光照计算
- 黑色描边:边缘检测或背面挤出
- 色块化着色:减少颜色过渡
7.2 边缘检测实现
csharp
// 背面挤出法实现描边
Pass
{
Cull Front // 渲染背面
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f
{
float4 pos : SV_POSITION;
};
float _OutlineWidth;
v2f vert (appdata v)
{
v2f o;
// 沿法线方向挤出
float3 normal = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal);
float2 offset = TransformViewToProjection(normal.xy);
o.pos = UnityObjectToClipPos(v.vertex);
o.pos.xy += offset * _OutlineWidth;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
return fixed4(0,0,0,1); // 黑色描边
}
ENDCG
}
7.3 色块化处理
csharp
fixed4 frag (v2f i) : SV_Target
{
// 计算光照
float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
float diffuse = max(0, dot(i.normal, lightDir));
// 离散化处理
float ramp = floor(diffuse * _RampSteps) / _RampSteps;
// 采样色块纹理
fixed3 rampCol = tex2D(_RampTex, float2(ramp, 0.5)).rgb;
// 添加高光
float3 viewDir = normalize(i.viewDir);
float3 halfDir = normalize(lightDir + viewDir);
float specular = pow(max(0, dot(i.normal, halfDir)), _SpecularPower);
specular = step(_SpecularThreshold, specular);
fixed4 col = tex2D(_MainTex, i.uv);
col.rgb *= rampCol + specular * _SpecularColor;
return col;
}
8. 进阶学习资源
8.1 官方文档
Unity Shader Reference
Surface Shader Examples
Shader Variants
8.2 推荐书目
- 《Unity Shader入门精要》- 冯乐乐
- 《Real-Time Rendering》- Tomas Akenine-Möller
- 《GPU Gems》系列 - NVIDIA
8.3 社区资源
Unity官方论坛Shader版块
CG/CGIN标准函数库
- 希望本文能帮助你在Unity开发中更加得心应手!如果有任何问题,请在评论区留言讨论。
- 点赞收藏加关注哦~ 蟹蟹