Unity Shader编程进阶:掌握高阶渲染技术 C# 实战案例

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


本文将深入探讨Unity Shader编程的高级技术,包括自定义光照模型、后处理效果、GPU实例化、表面着色器深度应用等,帮助开发者提升渲染效果与性能优化能力。
提示:内容纯个人编写,欢迎评论点赞。

文章目录

  • Unity Shader编程完全入门指南:从零到实战 C#
  • [1. 高级光照模型](#1. 高级光照模型)
      • [1.1 光照模型理论基础](#1.1 光照模型理论基础)
      • [1.2 实现PBR光照](#1.2 实现PBR光照)
      • [1.3 自定义卡通光照](#1.3 自定义卡通光照)
  • [2. 后处理效果处理](#2. 后处理效果处理)
      • [2.1 后处理原理](#2.1 后处理原理)
      • [2.2 Bloom效果实现](#2.2 Bloom效果实现)
      • [2.3 屏幕空间环境光遮蔽(SSAO)](#2.3 屏幕空间环境光遮蔽(SSAO))
  • [3. 表面着色器深度应用](#3. 表面着色器深度应用)
      • [3.1 表面函数高级用法](#3.1 表面函数高级用法)
      • [3.2 自定义顶点修改](#3.2 自定义顶点修改)
      • [3.3 多光源支持](#3.3 多光源支持)
  • [4. GPU实例化优化](#4. GPU实例化优化)
      • [4.1 实例化原理](#4.1 实例化原理)
      • [4.2 静态批处理 vs GPU实例化](#4.2 静态批处理 vs GPU实例化)
      • [4.3 实例化实战:草地渲染](#4.3 实例化实战:草地渲染)
  • [5. Shader变体管理](#5. Shader变体管理)
      • [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 Shader性能分析](#8.1 Shader性能分析)
      • [8.2 带宽优化](#8.2 带宽优化)
      • [8.3 跨平台适配](#8.3 跨平台适配)
  • [9. 总结与资源](#9. 总结与资源)

1. 高级光照模型

1.1 光照模型理论基础

光照模型描述了光与物体表面相互作用的数学表示:

  • Phong模型:环境光+漫反射+高光反射
csharp 复制代码
I = I_ambient + I_diffuse + I_specular
  • BRDF(双向反射分布函数):更精确的物理模型

1.2 实现PBR光照

基于物理的渲染(PBR)核心公式:

csharp 复制代码
// Unity Standard BRDF
half4 BRDF_Unity_PBS(
    half3 diffColor, half3 specColor, half oneMinusReflectivity, half smoothness,
    float3 normal, float3 viewDir,
    Light light, InputData data
) {
    // ... PBR计算过程
}

完整PBR表面着色器示例:

csharp 复制代码
Shader "Custom/PBRShader"
{
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _Metallic ("Metallic", Range(0,1)) = 0.0
        _Smoothness ("Smoothness", Range(0,1)) = 0.5
        _NormalMap ("Normal Map", 2D) = "bump" {}
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        
        CGPROGRAM
        #pragma surface surf Standard fullforwardshadows
        
        struct Input {
            float2 uv_NormalMap;
        };
        
        half _Metallic;
        half _Smoothness;
        half4 _Color;
        sampler2D _NormalMap;
        
        void surf (Input IN, inout SurfaceOutputStandard o) {
            o.Albedo = _Color.rgb;
            o.Metallic = _Metallic;
            o.Smoothness = _Smoothness;
            o.Normal = UnpackNormal(tex2D(_NormalMap, IN.uv_NormalMap));
        }
        ENDCG
    }
    FallBack "Diffuse"
}

1.3 自定义卡通光照

csharp 复制代码
// 卡通光照函数
void LightingRamp_GI (
    SurfaceOutput s,
    UnityGIInput data,
    inout UnityGI gi
) {
    // ... 全局光照设置
}

void LightingRamp_CS (
    SurfaceOutput s,
    half3 viewDir,
    UnityGI gi,
    out half4 finalColor
) {
    // 离散化处理
    half NdotL = dot(s.Normal, gi.light.dir);
    half ramp = ceil(NdotL * _RampSteps) / _RampSteps;
    
    // 最终颜色
    finalColor.rgb = s.Albedo * gi.light.color * ramp;
    finalColor.a = s.Alpha;
}

2. 后处理效果处理

2.1 后处理原理

后处理在渲染完成后对屏幕图像进行处理:

csharp 复制代码
渲染场景 -> 获取屏幕图像 -> 应用后处理Shader -> 输出最终图像

2.2 Bloom效果实现

csharp 复制代码
// Bloom效果Shader核心
half4 frag_bloom (v2f i) : SV_Target
{
    // 1. 亮度提取
    half4 col = tex2D(_MainTex, i.uv);
    half brightness = dot(col.rgb, float3(0.2126, 0.7152, 0.0722));
    if(brightness < _BloomThreshold)
        return 0;
    
    // 2. 高斯模糊
    half4 blur = 0;
    for(int i = 0; i < KERNEL_SIZE; i++) {
        blur += _BloomKernel[i] * tex2D(_MainTex, i.uv + _BloomOffsets[i]);
    }
    
    // 3. 混合原始图像与模糊图像
    return col + blur * _BloomIntensity;
}

2.3 屏幕空间环境光遮蔽(SSAO)

csharp 复制代码
// SSAO核心计算
float ComputeAO(float2 uv, float3 viewNormal)
{
    float ao = 0.0;
    for(int i = 0; i < SAMPLE_COUNT; i++) {
        float3 samplePos = GetSamplePosition(uv, i);
        float sample = depthCompare(samplePos);
        ao += ComputeAOContribution(viewNormal, samplePos, sample);
    }
    return 1.0 - (ao / SAMPLE_COUNT) * _AOIntensity;
}

3. 表面着色器深度应用

3.1 表面函数高级用法

csharp 复制代码
void surf (Input IN, inout SurfaceOutputStandard o) 
{
    // 世界坐标计算
    float3 worldPos = IN.worldPos;
    
    // 三平面贴图混合
    float3 triblend = pow(abs(IN.worldNormal), _BlendSharpness);
    triblend /= dot(triblend, float3(1,1,1));
    
    half4 colX = tex2D(_MainTex, IN.worldPos.yz);
    half4 colY = tex2D(_MainTex, IN.worldPos.xz);
    half4 colZ = tex2D(_MainTex, IN.worldPos.xy);
    
    o.Albedo = colX * triblend.x + colY * triblend.y + colZ * triblend.z;
}

3.2 自定义顶点修改

csharp 复制代码
#pragma surface surf Lambert vertex:vert

void vert (inout appdata_full v, out Input o) 
{
    UNITY_INITIALIZE_OUTPUT(Input, o);
    
    // 顶点波浪动画
    float wave = sin(_Time.y + v.vertex.x * _WaveFreq);
    v.vertex.y += wave * _WaveAmp;
    
    // 传递世界坐标
    o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
}

3.3 多光源支持

csharp 复制代码
#pragma surface surf Standard fullforwardshadows
#pragma multi_compile_fwdadd

void surf (Input IN, inout SurfaceOutputStandard o) 
{
    // 表面函数
}

// 额外光源处理
#ifdef _ADDITIONAL_LIGHTS
    uint pixelLightCount = GetAdditionalLightsCount();
    for (uint lightIndex = 0; lightIndex < pixelLightCount; lightIndex++) {
        Light light = GetAdditionalLight(lightIndex, IN.worldPos);
        // 光源贡献计算
    }
#endif

4. GPU实例化优化

4.1 实例化原理

GPU实例化允许一次性绘制多个相同网格,减少Draw Call:

csharp 复制代码
常规绘制: Draw Call 1 -> 网格1
          Draw Call 2 -> 网格1
          ...
实例化:   Draw Call 1 -> 网格1 (实例1, 实例2, ...)

4.2 静态批处理 vs GPU实例化

4.3 实例化实战:草地渲染

csharp 复制代码
Shader "Custom/InstancedGrass"
{
    Properties { /* 属性 */ }
    SubShader
    {
        Pass
        {
            Tags { "LightMode"="ForwardBase" }
            
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_instancing
            
            UNITY_INSTANCING_BUFFER_START(Props)
                UNITY_DEFINE_INSTANCED_PROP(float4, _Color)
                UNITY_DEFINE_INSTANCED_PROP(float, _Height)
            UNITY_INSTANCING_BUFFER_END(Props)
            
            struct appdata
            {
                float4 vertex : POSITION;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };
            
            struct v2f
            {
                float4 pos : SV_POSITION;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };
            
            v2f vert (appdata v)
            {
                v2f o;
                UNITY_SETUP_INSTANCE_ID(v);
                UNITY_TRANSFER_INSTANCE_ID(v, o);
                
                float height = UNITY_ACCESS_INSTANCED_PROP(Props, _Height);
                v.vertex.y += height;
                
                o.pos = UnityObjectToClipPos(v.vertex);
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                UNITY_SETUP_INSTANCE_ID(i);
                return UNITY_ACCESS_INSTANCED_PROP(Props, _Color);
            }
            ENDCG
        }
    }
}

5. Shader变体管理

5.1 变体概念

Shader变体由预编译指令组合生成:

csharp 复制代码
#pragma multi_compile _ A B
#pragma multi_compile C D

5.2 变体控制技巧

  1. 精确控制:
csharp 复制代码
#pragma shader_feature _ENABLE_FEATURE
  1. 跳过变体:
csharp 复制代码
#pragma skip_variants POINT SPOT

5.3 变体优化策略

  • 避免不必要的multi_compile
  • 使用shader_feature替代multi_compile
  • 拆分变体过多的Shader
  • 使用变体集合(Variant Collections)

6. 计算着色器入门

6.1 计算着色器基础

csharp 复制代码
#pragma kernel CSMain

RWTexture2D<float4> Result;
[numthreads(8,8,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
    // 并行计算
    Result[id.xy] = float4(id.x & id.y, (id.x & 15)/15.0, (id.y & 15)/15.0, 0.0);
}

6.2 粒子系统优化

csharp 复制代码
// 粒子更新计算着色器
RWStructuredBuffer<Particle> ParticleBuffer;

[numthreads(64,1,1)]
void UpdateParticles (uint id : SV_DispatchThreadID)
{
    Particle p = ParticleBuffer[id];
    
    // 更新位置
    p.velocity += float3(0, -9.8, 0) * dt;
    p.position += p.velocity * dt;
    
    // 碰撞检测
    if(p.position.y < 0) {
        p.position.y = 0;
        p.velocity.y *= -0.8;
    }
    
    ParticleBuffer[id] = p;
}

6.3 通用计算应用

  • 网格变形
  • 物理模拟
  • 纹理生成
  • 光线追踪

7. 实战案例:天气系统

7.1 雨滴效果

csharp 复制代码
// 雨滴表面着色器
void surf (Input IN, inout SurfaceOutput o) 
{
    // 雨滴法线贴图
    float2 rainUV = IN.worldPos.xz * _RainScale + float2 _Time.y * _RainSpeed;
    half3 rainNormal = UnpackNormal(tex2D(_RainNormalMap, rainUV));
    
    // 混合基础法线
    o.Normal = BlendNormals(o.Normal, rainNormal);
    
    // 湿润效果
    half wetFactor = saturate(_RainAmount * 2 - 1);
    o.Albedo = lerp(_DryColor, _WetColor, wetFactor);
}

7.2 雪地脚印

csharp 复制代码
// 脚印渲染
float3 worldPos = IN.worldPos;
float2 footprintUV = worldPos.xz - _PlayerPos.xz;
half footprint = tex2D(_FootprintMask, footprintUV).r;

// 混合雪地材质
o.Albedo = lerp(_SnowColor, _GroundColor, footprint);
o.Smoothness = lerp = lerp(_SnowSmoothness, 0.2, footprint);

7.3 动态潮湿效果

csharp 复制代码
// 潮湿贴图随时间变化
float2 wetUV = IN.worldPos.xz * _WetScale;
half wetPattern = tex2D(_WetPatternMap, wetUV).r;

// 随时间增加的潮湿程度
half wetFactor = saturate(_Wetness * 2 - wetPattern);
o.Metallic = lerp(0.0, 0.8, wetFactor);
o.Smoothness = lerp(0.1, 0.9, wetFactor);

8. 性能调优与调试

8.1 Shader性能分析

GPU性能分析:

  • Unity Frame Debugger
  • RenderDoc
  • XCode GPU Debugger

关键指标:

  • Fill Rate(填充率)
  • Overdraw(过度绘制)
  • Shader复杂度

8.2 带宽优化

  • 压缩纹理格式(ASTC, ETC2)
  • Mipmap优化
  • 减少纹理采样次数
  • 使用纹理图集

8.3 跨平台适配

csharp 复制代码
// 平台相关宏
#if defined(SHADER_API_MOBILE)
    // 移动端简化版
#elif defined(SHADER_API_D3D11)
    // PC高级版
#endif

// 精度调整
#if defined(SHADER_API_GLES)
    precision mediump float;
#else
    precision highp float;
#endif

9. 总结与资源

核心进阶技术:

  • 物理光照模型实现
  • 后处理特效开发
  • GPU实例化优化
  • 计算着色器应用

继续学习资源: