1. Unity内置屏幕模糊Shader的实现原理
在Unity中实现屏幕模糊效果是游戏开发中常见的后处理需求。Built-in渲染管线下的屏幕模糊Shader主要依赖于卷积操作中的高斯模糊算法。这种算法的核心思想是通过对图像像素进行加权平均,使每个像素的颜色值受到周围像素的影响,从而产生平滑的视觉效果。
高斯模糊的数学基础是二维高斯函数:
G(x,y) = (1/(2πσ²)) * e^(-(x²+y²)/(2σ²))
其中σ代表标准差,决定了模糊的程度。在实际Shader实现中,我们通常采用分离的高斯模糊(Separable Gaussian Blur),将二维卷积拆分为两个一维卷积(水平+垂直),这样可以大幅减少计算量。对于1920x1080的渲染目标,传统二维模糊需要计算1920x1080xN²次(N为核大小),而分离模糊只需计算1920x1080x2N次。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 构建基础模糊Shader的完整步骤
2.1 创建Shader文件基础结构
首先在Unity中创建新的Shader文件,基本结构应包含:
- Properties块:定义可调节参数
- SubShader块:包含渲染通道
- Pass块:实现具体渲染逻辑
shader复制Shader "Custom/BlurEffect" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_BlurSize ("Blur Size", Range(0,10)) = 1.0
}
SubShader {
// 渲染通道配置
}
}
2.2 实现双Pass模糊渲染
高质量模糊通常需要两个Pass:
- 水平模糊Pass:
shader复制Pass {
CGPROGRAM
#pragma vertex vertBlur
#pragma fragment fragBlurHorizontal
// 顶点着色器
// 片段着色器实现水平模糊
ENDCG
}
- 垂直模糊Pass:
shader复制Pass {
CGPROGRAM
#pragma vertex vertBlur
#pragma fragment fragBlurVertical
// 使用相同的顶点着色器
// 片段着色器实现垂直模糊
ENDCG
}
2.3 核心模糊算法实现
在片段着色器中实现高斯核采样:
c复制fixed4 fragBlurHorizontal(v2f i) : SV_Target {
fixed4 col = 0;
// 定义高斯核权重
float weights[3] = {0.4026, 0.2442, 0.0545};
// 中心像素
col += tex2D(_MainTex, i.uv) * weights[0];
// 左右各两个采样点
for(int j=1; j<3; j++) {
col += tex2D(_MainTex, i.uv + float2(_BlurSize*j/_MainTex_TexelSize.x, 0)) * weights[j];
col += tex2D(_MainTex, i.uv - float2(_BlurSize*j/_MainTex_TexelSize.x, 0)) * weights[j];
}
return col;
}
3. 性能优化关键技巧
3.1 降采样优化策略
高质量模糊对性能影响较大,可采用金字塔式降采样:
- 先将原图降采样到1/2或1/4分辨率
- 在低分辨率下进行模糊处理
- 最后将结果上采样回原分辨率
csharp复制// C#脚本中的降采样实现
RenderTexture rt1 = RenderTexture.GetTemporary(source.width/2, source.height/2);
Graphics.Blit(source, rt1);
RenderTexture rt2 = RenderTexture.GetTemporary(source.width/4, source.height/4);
Graphics.Blit(rt1, rt2);
// 在rt2上执行模糊
3.2 动态模糊强度调节
根据设备性能动态调整模糊质量:
csharp复制void UpdateBlurQuality() {
if(SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLES2) {
blurMaterial.SetFloat("_SampleCount", 3);
} else {
blurMaterial.SetFloat("_SampleCount", 5);
}
}
3.3 模糊区域局部化
只对需要模糊的区域进行处理,减少填充率消耗:
shader复制fixed4 frag(v2f i) : SV_Target {
// 计算模糊区域遮罩
float mask = smoothstep(_BlurArea.x, _BlurArea.y, length(i.uv - 0.5));
fixed4 blurCol = // 模糊结果
fixed4 origCol = tex2D(_MainTex, i.uv);
return lerp(blurCol, origCol, mask);
}
4. 常见问题与调试技巧
4.1 模糊边缘出现锯齿
问题表现:模糊效果的边缘出现明显锯齿或硬边
解决方案:
- 检查纹理导入设置的Wrap Mode是否为Clamp
- 增加模糊采样点的数量
- 在边缘区域使用更大的模糊半径渐变
shader复制float edgeFactor = saturate((distance(i.uv, _BlurCenter) - _BlurRadius) * _EdgeSoftness);
4.2 移动设备性能问题
问题表现:在移动设备上帧率明显下降
优化方案:
- 使用更小的模糊核(3x3或5x5)
- 降低降采样比例
- 使用ES2兼容的Shader语法
shader复制#pragma exclude_renderers d3d11_9x
#pragma target 2.0
4.3 与UI元素的混合问题
问题表现:模糊效果影响了UI元素的显示
解决方案:
- 使用Camera的Layer设置分离场景和UI
- 在Shader中添加UI遮罩支持
csharp复制// 在渲染前设置相机的cullingMask
blurCamera.cullingMask = ~(1 << LayerMask.NameToLayer("UI"));
5. 进阶应用:动态模糊效果
5.1 速度模糊实现
基于物体运动速度的动态模糊:
csharp复制void OnRenderImage(RenderTexture src, RenderTexture dst) {
// 计算当前帧运动向量
Vector2 blurVector = (transform.position - lastPosition) * _VelocityScale;
blurMaterial.SetVector("_BlurVector", blurVector);
Graphics.Blit(src, dst, blurMaterial);
lastPosition = transform.position;
}
5.2 景深模糊模拟
模拟相机镜头的景深效果:
- 生成深度纹理
- 根据深度值调整模糊强度
- 混合不同模糊程度的图像
shader复制float depth = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv_depth));
float blurFactor = saturate((depth - _FocusDistance) / _FocusRange);
5.3 材质表面模糊效果
让特定材质产生模糊反射:
shader复制void surf (Input IN, inout SurfaceOutputStandard o) {
// 常规表面着色
o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;
// 模糊反射
float2 blurUV = IN.worldRefl.xy * _ReflectionBlur;
fixed4 blurRefl = tex2D(_ReflectionTex, blurUV);
o.Emission = blurRefl.rgb * _ReflectionIntensity;
}
6. 与URP/HDRP的兼容性处理
6.1 向URP迁移的注意事项
URP中实现屏幕模糊的差异点:
- 需要使用URP的Renderer Feature系统
- 采样命令从tex2D改为Sample2D
- 需要处理SRP Batcher兼容性
shader复制// URP中的采样示例
TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex);
half4 frag(Varyings input) : SV_Target {
half4 col = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv);
}
6.2 HDRP中的高性能模糊方案
HDRP提供了内置的模糊实现:
- 使用FullScreenPassRendererFeature
- 利用Compute Shader加速
- 支持硬件加速的Bokeh模糊
csharp复制// HDRP中调用内置模糊
var blurSettings = volumeStack.GetComponent<MotionBlur>();
blurSettings.intensity.value = _BlurAmount;
6.3 多平台着色器变体处理
确保Shader在各种平台正常工作:
shader复制#pragma multi_compile __ MOBILE_PLATFORM
#pragma multi_compile_fog
#if defined(MOBILE_PLATFORM)
#define SAMPLE_COUNT 3
#else
#define SAMPLE_COUNT 5
#endif
7. 美术效果控制技巧
7.1 风格化模糊参数调节
创造特殊艺术效果:
- 方向性模糊 - 产生运动轨迹效果
- 径向模糊 - 模拟镜头缩放冲击感
- 不规则模糊 - 使用噪声纹理扰动采样坐标
shader复制// 径向模糊实现
float2 dir = normalize(i.uv - _RadialCenter);
float2 uv = i.uv;
for(int i=0; i<_SampleCount; i++) {
col += tex2D(_MainTex, uv);
uv += dir * _RadialStrength;
}
col /= _SampleCount;
7.2 多层级模糊混合
实现更丰富的景深层次:
- 生成3-4个不同模糊程度的图像
- 根据深度值进行混合
- 添加边缘平滑过渡
shader复制fixed4 nearBlur = tex2D(_NearBlurTex, i.uv);
fixed4 midBlur = tex2D(_MidBlurTex, i.uv);
fixed4 farBlur = tex2D(_FarBlurTex, i.uv);
float depth = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv_depth));
fixed4 finalCol = lerp(nearBlur, midBlur, smoothstep(_NearRange.x, _NearRange.y, depth));
finalCol = lerp(finalCol, farBlur, smoothstep(_MidRange.x, _MidRange.y, depth));
7.3 与后期处理栈的配合
协调模糊与其他效果的关系:
- 处理顺序 - 模糊通常应在Bloom之前应用
- 颜色空间 - 确保在正确的颜色空间(Linear/Gamma)下工作
- 抗锯齿兼容 - 处理TAA等抗锯齿技术的影响
csharp复制// 确保正确的执行顺序
void OnEnable() {
GetComponent<Camera>().GetCommandBuffer(UnityEngine.Rendering.CameraEvent.BeforeImageEffects).AddBlit(...);
}
8. 实战案例:全屏模糊过渡效果
8.1 场景过渡模糊实现
实现场景加载时的平滑过渡:
- 捕获当前屏幕到RenderTexture
- 在新场景加载时保持渲染模糊版本
- 随时间减少模糊强度
csharp复制IEnumerator TransitionCoroutine() {
// 捕获屏幕
RenderTexture rt = new RenderTexture(Screen.width, Screen.height, 0);
camera.targetTexture = rt;
camera.Render();
camera.targetTexture = null;
// 显示模糊版本
blurMaterial.SetTexture("_MainTex", rt);
transitionImage.texture = rt;
// 渐变减少模糊
float t = 1f;
while(t > 0) {
t -= Time.deltaTime / transitionTime;
blurMaterial.SetFloat("_BlurAmount", t * maxBlur);
yield return null;
}
}
8.2 性能敏感型模糊控制
根据帧率动态调整模糊质量:
csharp复制void Update() {
float currentFPS = 1f / Time.unscaledDeltaTime;
if(currentFPS < targetFPS) {
currentQuality = Mathf.Max(minQuality, currentQuality - 0.1f);
} else {
currentQuality = Mathf.Min(maxQuality, currentQuality + 0.05f);
}
blurMaterial.SetFloat("_SampleCount", Mathf.RoundToInt(currentQuality));
}
8.3 模糊效果的调试可视化
开发期辅助调试工具:
- 显示模糊采样点分布
- 可视化模糊强度图
- 性能统计覆盖显示
shader复制// 调试可视化模式
#ifdef DEBUG_VIZ
if(i.uv.x < 0.1 && i.uv.y < 0.1) {
// 在小角落显示采样点
for(int j=0; j<_SampleCount; j++) {
float2 sampleUV = i.uv + _SampleOffsets[j];
if(length(sampleUV - i.uv) < 0.01) {
return float4(1,0,0,1);
}
}
}
#endif
在实现Unity内置屏幕模糊效果时,我发现最关键的平衡点在于视觉效果与性能开销的取舍。对于移动平台,5x5的高斯核已经能提供不错的模糊质量,而PC平台则可以考虑7x7或9x9的核。一个实用的技巧是使用不同模糊强度多次叠加,而不是单纯增大模糊核,这样往往能获得更好的视觉效果和性能表现。另外,在模糊处理前对图像进行适当的降采样(通常降到1/4分辨率),再配合双线性或三线性过滤,可以在几乎不损失视觉质量的情况下显著提升性能。
