Unity Shader编程完全入门指南:从零到实战 C# 实战案例

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 性能优化

  1. 减少数学运算:
  • 避免使用sin、cos等复杂函数
  • 使用mad指令优化计算(a*b+c)
  1. 纹理采样优化:
csharp 复制代码
// 避免多次采样相同纹理
fixed4 col = tex2D(_MainTex, uv);
fixed4 normal = UnpackNormal(tex2D(_NormalMap, uv));

// 使用纹理合并(RGB存储不同数据)
  1. 条件语句优化:
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 调试技巧

  1. 颜色输出调试法:
csharp 复制代码
// 可视化法线方向
return float4(i.normal * 0.5 + 0.5, 1.0);

// 显示UV坐标
return float4(i.uv, 0, 1);
  1. Unity帧调试器:
  • Window > Analysis > Frame Debugger
  • 逐步查看渲染过程
  1. 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开发中更加得心应手!如果有任何问题,请在评论区留言讨论。
  • 点赞收藏加关注哦~ 蟹蟹
相关推荐
LZQqqqqo1 小时前
C# 事件Event
开发语言·c#
时光追逐者1 小时前
C#/.NET/.NET Core优秀项目和框架2025年7月简报
c#·.net·.netcore
Edision_li2 小时前
基于Mediapipe_Unity_Plugin实现手势识别
unity·游戏引擎·unity 手势识别
谷宇.2 小时前
【Unity3D实例-功能-镜头】第三人称视觉
游戏·unity·unity3d·游戏开发·游戏编程·steam
两水先木示2 小时前
【Unity3D】Shader圆形弧度裁剪
unity·shader·圆形裁剪·弧度裁剪
R-G-B3 小时前
【08】大恒相机SDK C#发开 —— 多相机采集
c#·大恒相机c#发开·大恒多相机采集
一个会的不多的人5 小时前
C# NX二次开发:超级点控件使用详解
开发语言·c#
LZQqqqqo11 小时前
C# 中生成随机数的常用方法
java·算法·c#
weixin_4569042715 小时前
C#泛型委托讲解
开发语言·c#