Unity Shader编程完全入门指南:从零到实战 C# 实战案例
Unity Shader编程完全入门指南:从零到实战 C#
提示:内容纯个人编写,欢迎评论点赞。
文章目录
- Unity Shader编程完全入门指南:从零到实战 C#
- 1. Shader基础概念
- 1.1 什么是Shader?
- 1.2 Shader在游戏开发中的作用
- 1.3 Unity中的Shader类型
- 2. ShaderLab语法入门
- 2.1 基本结构
- 2.2 Properties属性块
- 2.3 SubShader与Pass
- 2.4 常用渲染指令
- 3. 表面着色器(Surface Shader)实战
- 3.1 表面着色器结构
- 3.2 实现漫反射光照
- 3.3 添加纹理贴图
- 4. 顶点/片元着色器(Vertex/Fragment Shader)实战
- 4.1 基本结构
- 4.2 实现顶点动画
- 4.3 片元着色特效
- 5. 常见问题与解决方案
- 5.1 性能优化
- 5.2 平台兼容性问题
- 5.3 调试技巧
- 6. 实战案例:水波特效
- 6.1 效果分析
- 6.2 数学原理
- 6.3 完整代码实现
- 7. 实战案例:卡通渲染
- 7.1 效果分析
- 7.2 边缘检测实现
- 7.3 色块化处理
- 8. 进阶学习资源
- 8.1 官方文档
- 8.2 推荐书目
- 8.3 社区资源
1. Shader基础概念
1.1 什么是Shader?
Shader(着色器)是一类运行在GPU上的特殊程序,用于控制图形渲染管线的各个阶段。它决定了模型的顶点如何变换到屏幕空间,以及每个像素如何着色
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 基本结构
Shader "Custom/ExampleShader"
{Properties{// 属性声明}SubShader{Tags { "RenderType"="Opaque" }Pass{// 渲染指令CGPROGRAM#pragma vertex vert#pragma fragment frag// CG代码ENDCG}}FallBack "Diffuse"
}
2.2 Properties属性块
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 常用渲染指令
Cull Back // 背面剔除
ZWrite On // 深度写入
Blend SrcAlpha OneMinusSrcAlpha // 透明混合
LOD 200 // 细节级别
3. 表面着色器(Surface Shader)实战
3.1 表面着色器结构
Shader "Custom/SurfaceExample"
{Properties { /* 属性声明 */ }SubShader{Tags { "RenderType"="Opaque" }CGPROGRAM#pragma surface surf Standardstruct Input{float2 uv_MainTex;};void surf (Input IN, inout SurfaceOutputStandard o){// 表面着色逻辑}ENDCG}
}
3.2 实现漫反射光照
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 添加纹理贴图
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 基本结构
Shader "Custom/VFExample"
{Properties { _Color("Color", Color) = (1,0,0,1) }SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment fragstruct 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 实现顶点动画
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;
}
初始状态: 波动状态:--- /\/\/\--- /\/\/\/\--- /\/\/\/\/\
4.3 片元着色特效
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)
- 纹理采样优化:
// 避免多次采样相同纹理
fixed4 col = tex2D(_MainTex, uv);
fixed4 normal = UnpackNormal(tex2D(_NormalMap, uv));// 使用纹理合并(RGB存储不同数据)
- 条件语句优化:
// 避免分支语句
float value = a > b ? 1.0 : 0.0; // 不推荐
float value = saturate(sign(a - b)); // 推荐`在这里插入代码片`
5.2 平台兼容性问题
// 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 调试技巧
- 颜色输出调试法:
// 可视化法线方向
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 数学原理
水波函数:
y = A \times sin(\frac{2\pi}{T} \times t + \frac{2\pi}{\lambda} \times x)
其中:
A = 振幅
T = 周期
λ = 波长
6.3 完整代码实现
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 300GrabPass { "_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"
}
水波特效效果:
初始水面: 波动效果: 折射效果:~~~~ ~~~~~~ ~~~~~~~~~~ ~/\/\/~ ~/~~\/~~~~~ /~~~~~~ /~~~~~~
7. 实战案例:卡通渲染
7.1 效果分析
- 硬朗的明暗分界:离散化光照计算
- 黑色描边:边缘检测或背面挤出
- 色块化着色:减少颜色过渡
7.2 边缘检测实现
// 背面挤出法实现描边
Pass
{Cull Front // 渲染背面CGPROGRAM#pragma vertex vert#pragma fragment fragstruct 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 色块化处理
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开发中更加得心应手!如果有任何问题,请在评论区留言讨论。
- 点赞收藏加关注哦~ 蟹蟹