You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity移动端基于CG语言实现快速平滑模糊着色器的最佳标准方案咨询

嘿,正好在移动设备上实现高效又平滑的模糊是Unity开发者常遇到的需求,而《盗贼之王》的方案确实是行业内经过验证的标杆级实践。我来给你拆解下移动平台的最佳标准方案,附上手写的CG着色器代码:

Unity移动设备快速平滑CG模糊着色器最佳实践

核心思路:分离式高斯模糊(双Pass)

移动GPU的计算带宽有限,直接做二维高斯模糊的开销会随着模糊半径呈平方增长,完全不适用。分离式高斯模糊是行业标准解决方案——把二维模糊拆成水平模糊+垂直模糊两个一维Pass,计算量从O(n²)降到O(2n),这也是《盗贼之王》采用的核心基础逻辑。

移动设备专属优化要点

这些细节是保证速度与画质平衡的关键,也是《盗贼之王》的优化精髓:

  • 先降采样再模糊:在模糊前把渲染纹理缩小到原分辨率的1/2或1/4,大幅减少后续采样次数,这是移动平台性价比最高的性能优化,《盗贼之王》会根据设备性能动态调整降采样比例。
  • 硬件加速线性采样:Unity移动平台的tex2D默认用硬件线性采样,比手动插值快数倍,无需额外计算就能获得平滑的采样过渡。
  • 低精度计算:在CG代码中用half代替float,减少内存带宽占用和计算量,移动GPU对低精度数据的处理效率更高。
  • 预计算采样UV:在顶点着色器中提前计算好模糊所需的采样坐标,避免在片元着色器中重复计算,进一步降低开销。

完整CG着色器实现代码

这个实现整合了降采样+双Pass模糊,完全适配移动平台:

Shader "Custom/FastMobileBlur" {
    Properties {
        _MainTex ("Source Texture", 2D) = "white" {}
        _BlurRadius ("Blur Radius", Range(0, 10)) = 2.5
        _DownsampleFactor ("Downsample Scale", Range(1, 4)) = 2.0
    }
    SubShader {
        Tags { "RenderType"="Transparent" "Queue"="PostProcessing" "RenderPipeline"="UniversalPipeline" }
        LOD 100

        // 降采样Pass:先缩小纹理,减少后续模糊计算量
        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct appdata {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float _DownsampleFactor;

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

            fixed4 frag (v2f i) : SV_Target {
                float2 texelSize = 1.0 / (_ScreenParams.xy / _DownsampleFactor);
                // 4邻域采样平均,快速降采样
                fixed4 col = 0;
                col += tex2D(_MainTex, i.uv);
                col += tex2D(_MainTex, i.uv + float2(texelSize.x, 0));
                col += tex2D(_MainTex, i.uv + float2(0, texelSize.y));
                col += tex2D(_MainTex, i.uv + texelSize);
                return col * 0.25;
            }
            ENDCG
        }

        // 水平模糊Pass
        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct appdata {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float2 blurUV[5] : TEXCOORD1; // 预计算采样UV
            };

            sampler2D _MainTex;
            float _BlurRadius;

            v2f vert (appdata v) {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                
                float2 step = float2(_BlurRadius / _ScreenParams.x, 0);
                // 预计算5个采样点的UV,减少片元计算
                o.blurUV[0] = o.uv - 2.0 * step;
                o.blurUV[1] = o.uv - 1.0 * step;
                o.blurUV[2] = o.uv;
                o.blurUV[3] = o.uv + 1.0 * step;
                o.blurUV[4] = o.uv + 2.0 * step;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target {
                // 近似高斯权重,避免指数计算开销
                fixed weights[5] = {0.12, 0.22, 0.32, 0.22, 0.12};
                fixed4 col = 0;
                for(int j=0; j<5; j++){
                    col += tex2D(_MainTex, i.blurUV[j]) * weights[j];
                }
                return col;
            }
            ENDCG
        }

        // 垂直模糊Pass
        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct appdata {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float2 blurUV[5] : TEXCOORD1;
            };

            sampler2D _MainTex;
            float _BlurRadius;

            v2f vert (appdata v) {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                
                float2 step = float2(0, _BlurRadius / _ScreenParams.y);
                o.blurUV[0] = o.uv - 2.0 * step;
                o.blurUV[1] = o.uv - 1.0 * step;
                o.blurUV[2] = o.uv;
                o.blurUV[3] = o.uv + 1.0 * step;
                o.blurUV[4] = o.uv + 2.0 * step;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target {
                fixed weights[5] = {0.12, 0.22, 0.32, 0.22, 0.12};
                fixed4 col = 0;
                for(int j=0; j<5; j++){
                    col += tex2D(_MainTex, i.blurUV[j]) * weights[j];
                }
                return col;
            }
            ENDCG
        }
    }
    FallBack "Hidden/Universal Render Pipeline/FallbackError"
}

《盗贼之王》的进阶优化细节

为了在移动平台达到极致的体验,他们还做了这些额外优化:

  • 动态分辨率适配:通过SystemInfo.graphicsDeviceType判断设备性能,中低端设备自动切换到1/4降采样,高端设备用1/2,平衡帧率与画质。
  • 轻量边缘保留:在模糊后加入简易Sobel边缘检测,对高对比度边缘做保留处理,避免模糊掉角色、UI等核心元素。
  • SRP管线集成:将模糊效果作为Post Process Volume的一部分,利用Unity SRP的批处理和渲染优化,减少额外Draw Call开销。

内容的提问来源于stack exchange,提问作者Mahdi GB

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 07:08:35