Unity Shader 实战屏幕颜色抓取实现径向模糊 (URP)

径向模糊(Radial Blur)是一种经典的后处理效果,它能够创建出动态的速度感或聚焦效果。在游戏开发中,径向模糊常用于:

  • 高速运动的速度线效果
  • 聚焦中心的视觉引导
  • 爆炸或冲击波的视觉表现
  • 特殊技能释放的炫酷效果

本教程将带你一步步在Unity URP(Universal Render Pipeline)环境下,通过屏幕颜色抓取技术实现径向模糊效果。

💡 核心概念

什么是屏幕颜色抓取?

屏幕颜色抓取(Screen Color Grab)是一种后处理技术,它允许Shader访问当前渲染的屏幕内容,并将其作为纹理进行处理。在Unity中,这通过_CameraColorTexture或自定义的Render Texture来实现。

什么是径向模糊?

径向模糊是一种从中心点向外辐射的模糊效果。不同于高斯模糊的全方向模糊,径向模糊只沿着从中心到边缘的径向方向进行采样和混合,创造出动态的视觉冲击。

完整的径向模糊Shader

以下是一个完整的URP径向模糊Shader实现,包含详细的注释:

cs 复制代码
Shader "Hidden/RadialBlur"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }

    HLSLINCLUDE
    #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
    #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/PostProcessing.hlsl"

    struct Attributes
    {
        float4 positionOS : POSITION;
        float2 uv : TEXCOORD0;
    };

    struct Varyings
    {
        float4 positionCS : SV_POSITION;
        float2 uv : TEXCOORD0;
    };

    TEXTURE2D(_MainTex);
    SAMPLER(sampler_MainTex);

    // 模糊参数
    float _BlurStrength;    // 模糊强度 0.0-1.0
    float _BlurRadius;     // 模糊半径 0.0-0.5
    float2 _Center;        // 模糊中心 (屏幕UV坐标)
    float _SampleCount;    // 采样次数 1-32

    // 顶点着色器
    Varyings Vert(Attributes input)
    {
        Varyings output;
        output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
        output.uv = input.uv;
        return output;
    }

    // 片段着色器 - 径向模糊核心算法
    half4 Frag(Varyings input) : SV_Target
    {
        float2 uv = input.uv;
        float2 center = _Center;
        float2 dir = uv - center;  // 计算从中心到当前像素的方向
        float dist = length(dir);   // 计算距离中心的距离

        // 如果在中心点附近,直接返回原样,避免不必要的计算
        if (dist < 0.001)
        {
            return SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv);
        }

        dir = normalize(dir);  // 归一化方向向量

        half4 finalColor = half4(0, 0, 0, 0);
        float totalWeight = 0;

        // 沿径向方向多次采样
        for (int i = 0; i < (int)_SampleCount; i++)
        {
            // 计算采样偏移量 (0到1之间均匀分布)
            float t = (float)i / (_SampleCount - 1);
            
            // 沿径向方向的偏移
            float2 offset = dir * t * _BlurRadius * _BlurStrength;
            
            // 从中心向外采样
            half4 sampleColor = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv - offset);

            // 距离越远,权重越小 (可选的加权策略)
            float weight = 1.0 - t * 0.5;
            finalColor += sampleColor * weight;
            totalWeight += weight;
        }

        // 归一化颜色
        finalColor /= totalWeight;
        return finalColor;
    }

    ENDHLSL

    SubShader
    {
        Tags 
        { 
            "RenderType" = "Opaque" 
            "RenderPipeline" = "UniversalPipeline" 
        }
        
        Cull Off
        ZWrite Off
        ZTest Always

        Pass
        {
            HLSLPROGRAM
            #pragma vertex Vert
            #pragma fragment Frag
            ENDHLSL
        }
    }
}
💡 Shader代码要点解析
  • 方向计算: 使用 `normalize(uv - center)` 计算径向方向
  • 多次采样: 通过循环沿径向方向采样不同位置的颜色
  • 权重混合: 使用权重因子 `1.0 - t * 0.5` 让靠近中心的采样点权重更高
  • 性能优化: 在中心点附近直接返回,避免不必要的计算

创建渲染特效脚本

创建RadialBlurEffect.cs文件,用于控制Shader的参数和渲染流程:

cs 复制代码
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

namespace RenderPipelineEffects
{
    /// 

    /// URP径向模糊效果 - Renderer Feature
    /// 通过屏幕颜色抓取实现径向模糊效果
    /// 

    public class RadialBlurEffect : ScriptableRendererFeature
    {
        [System.Serializable]
        public class Settings
        {
            [Header("基础设置")]
            public Shader shader;
            public RenderPassEvent renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing;
            
            [Header("模糊参数")]
            [Tooltip("模糊强度 (0.0 - 1.0)")]
            [Range(0f, 1f)]
            public float blurStrength = 0.5f;
            
            [Tooltip("模糊半径 (0.0 - 0.5)")]
            [Range(0f, 0.5f)]
            public float blurRadius = 0.2f;
            
            [Header("质量设置")]
            [Tooltip("采样次数 (1 - 32)")]
            [Range(1f, 32f)]
            public float sampleCount = 16f;
            
            [Header("中心点设置")]
            [Tooltip("模糊中心X坐标 (屏幕UV空间 0.0 - 1.0)")]
            [Range(0f, 1f)]
            public float centerX = 0.5f;
            
            [Tooltip("模糊中心Y坐标 (屏幕UV空间 0.0 - 1.0)")]
            [Range(0f, 1f)]
            public float centerY = 0.5f;
            
            [Header("控制")]
            [Tooltip("启用/禁用效果")]
            public bool enableEffect = true;
        }

        public Settings settings = new Settings();
        private RadialBlurPass m_ScriptablePass;

        /// 

        /// 创建渲染通道
        /// 

        public override void Create()
        {
            m_ScriptablePass = new RadialBlurPass(settings);
            m_ScriptablePass.renderPassEvent = settings.renderPassEvent;
        }

        /// 

        /// 将渲染通道添加到渲染器
        /// 

        public override void AddRenderPasses(
            ScriptableRenderer renderer, 
            ref RenderingData renderingData)
        {
            if (settings.shader == null || !settings.enableEffect)
                return;

            renderer.EnqueuePass(m_ScriptablePass);
        }

        /// 

        /// 径向模糊渲染通道
        /// 

        class RadialBlurPass : ScriptableRenderPass
        {
            private Settings settings;
            private Material m_Material;
            private RTHandle m_TemporaryColorTexture;

            public RadialBlurPass(Settings settings)
            {
                this.settings = settings;
                if (settings.shader != null)
                {
                    m_Material = new Material(settings.shader);
                    m_Material.hideFlags = HideFlags.HideAndDontSave;
                }
            }

            /// 

            /// 摄像机设置 - 分配临时渲染纹理
            /// 

            public override void OnCameraSetup(
                CommandBuffer cmd, 
                ref RenderingData renderingData)
            {
                var desc = renderingData.cameraData.cameraTargetDescriptor;
                desc.depthBufferBits = 0; // 不需要深度缓冲
                
                RenderingUtils.ReAllocateIfNeeded(
                    ref m_TemporaryColorTexture,
                    desc,
                    FilterMode.Bilinear,
                    TextureWrapMode.Clamp,
                    name: "_RadialBlurTemp"
                );
            }

            /// 

            /// 执行渲染通道 - 应用径向模糊效果
            /// 

            public override void Execute(
                CommandBuffer cmd, 
                ref RenderingData renderingData)
            {
                if (m_Material == null)
                    return;

                // 获取摄像机颜色目标
                var source = renderingData.cameraData.renderer.cameraColorTargetHandle;

                // 设置Shader参数
                m_Material.SetFloat("_BlurStrength", settings.blurStrength);
                m_Material.SetFloat("_BlurRadius", settings.blurRadius);
                m_Material.SetFloat("_SampleCount", settings.sampleCount);
                m_Material.SetVector(
                    "_Center", 
                    new Vector2(settings.centerX, settings.centerY));

                // 第一次Blit: 应用模糊效果到临时纹理
                Blitter.BlitCameraTexture(
                    cmd,
                    source,
                    m_TemporaryColorTexture,
                    m_Material,
                    0
                );

                // 第二次Blit: 将结果拷贝回源纹理
                Blitter.BlitCameraTexture(
                    cmd,
                    m_TemporaryColorTexture,
                    source
                );
            }
        }
    }
}

参数详解

参数 类型 范围 作用 默认值
blurStrength float 0.0 ~ 1.0 控制模糊的整体强度 0.5
blurRadius float 0.0 ~ 0.5 控制采样的最大半径 0.2
sampleCount float 1 ~ 32 采样次数,影响质量与性能 16
centerX float 0.0 ~ 1.0 模糊中心的X坐标(屏幕空间) 0.5
centerY float 0.0 ~ 1.0 模糊中心的Y坐标(屏幕空间) 0.5

使用步骤

  • 步骤1: 导入Shader

    将RadialBlurShader.shader文件拖入Unity项目,Unity会自动编译并生成Shader资源

  • 步骤2: 添加Renderer Feature

    在Project窗口找到URP Renderer Data资源,点击Add Renderer Feature,选择RadialBlurEffect

  • 步骤3: 配置参数

    在Inspector面板中设置Shader引用,调整模糊强度、半径和中心点位置

  • 步骤4: 测试效果

    运行场景,观察径向模糊效果。如果需要动态调整,可以编写C#脚本来控制参数

💡 性能优化建议
  • 在移动平台上,建议将sampleCount控制在8-12之间
  • 可以使用mipmap降低采样成本
  • 考虑使用half精度而非full精度
  • 在不显示效果时设置enableEffect为false
⚠️ 常见问题
  • 效果不显示:检查URP Renderer Data是否正确添加了Renderer Feature
  • 性能下降明显:降低sampleCount或blurRadius
  • 中心点不对:确保centerX和centerY在0-1范围内
  • Shader编译错误:确认URP版本兼容性,使用正确的include路径

📚 总结

本教程详细介绍了在Unity URP环境下实现径向模糊效果的完整流程,包括:

  • ✅ 屏幕颜色抓取的基本原理
  • ✅ 径向模糊的算法实现
  • ✅ URP Renderer Feature的使用
  • ✅ 性能优化技巧
  • ✅ 动态效果实现方法
相关推荐
林枫依依6 小时前
Unity2017 项目源码打开即崩溃,无法打开的解决办法
unity
wearegogog1236 小时前
ESP32迷你无人机开发代码详解
游戏引擎·无人机·cocos2d
心前阳光8 小时前
Unity使用豆包语音模型
unity·游戏引擎
张老师带你学8 小时前
unity资源:星际飞船 陨石 虫族 星球
科技·游戏·unity·模型·游戏美术
心前阳光8 小时前
Unity使用豆包语言模型
unity·语言模型
魔士于安8 小时前
unity宇宙飞船
游戏·unity·游戏引擎·贴图·模型
RReality8 小时前
【Unity Shader】高级光照与阴影总结:渲染路径、多光源、透明阴影
unity·游戏引擎
浪客川8 小时前
godot-rust入门案例
rust·游戏引擎·godot
RPGMZ9 小时前
RPGMakerMZ游戏引擎 地图角色顶部显示称号
javascript·游戏引擎·rpgmz·rpgmakermz