专栏/URP | 后处理-径向模糊

URP | 后处理-径向模糊

2022年11月15日 04:38--浏览 · --点赞 · --评论
粉丝:1077文章:112


效果

目的

  • 学习径向模糊的制作方法?

  • 怎么使用径向模糊,

  • 有那些需要注意的内容。

向模糊 | Radial Blur

上面一篇介绍到基础模糊效果,这次学习经向模糊,

经向模糊大部分使用到BOSS战斗或者飞行,赛车表现效果。

径向模糊,是一种从中心向外呈幅射状的逐渐模糊的效果

制作思路

由此我们可以大概确定下径向模糊实现的流程:

  • 第一步:确定径向模糊的中心点,通常取图像的正中心点、

  • 第二步:计算采样像素与中心点的距离,根据距离确定偏移程度,即离中心点越远,偏移量越大。

  • 第三步:将采样点的颜色值做加权求和,本例使用平均求和。

实现过程

准备径向模糊Shader

径向模糊的特点是从某个像素为中心向外辐射状扩散,因此需要采样的像素在原像素和中间点像素的连线上,不同连线上的点不会相互影响.

Shader

shader我们准备两个Pass 一个处理模糊方向,一个混合模糊和原画

已中心点为起点,当前像素为终点,进行

Shader "URP/4_RadialBlur"
{
    Properties
    {
        _MainTex ("_MainTex", 2D) = "white" {}
        _Blur("_Blur",Float) = 0
        [int]_Loop("_Loop",range(1,10)) = 1
        _X("_X",Float) = 0.5
        _Y("_Y",Float) = 0.5
        _Instensity("_Instensity",Float) = 0
    }
    SubShader
    {
        Tags { "RenderPipeline"="UniversalPipeline" }


        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment radialfrag

            #include "Assets/Post/04_RadialBliur/RadialBlur.hlsl"           //函数库


            ENDHLSL
        }
    }
}
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"



CBUFFER_START(UnityPerMaterial)
float _Blur;
float _Loop;
float _Y;
float _X;
CBUFFER_END


TEXTURE2D(_MainTex);             SAMPLER(sampler_MainTex);
TEXTURE2D(_SourceTex);             SAMPLER(sampler_SourceTex);

struct appdata
{
    float4 positionOS : POSITION;
    float2 texcoord : TEXCOORD0;
};

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

};


v2f vert(appdata v)
{
    v2f o;
    o.vertex = TransformObjectToHClip(v.positionOS.xyz);
    o.uv = v.texcoord;
    return o;
}

half4 radialfrag(v2f i) : SV_Target
{

    float4 col = 0;
    float2 dir = (float2(_X,_Y) - i.uv) * _Blur * 0.01;
    for(int t = 0; t < _Loop; t++)
    {
        col += SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv + dir * t)/ _Loop;
    }
    return col;
}
  • 测试一下是否起作用。

    增加一个图片,调整数值, X = 0.5, Y = 0.5 是中心点,测试效果是否正确。

这个是在单材质上测试。

Volume

定义几个属性

采样中心点,迭代次数  模糊采样距离    降采样   模糊强度等。

using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;



public class RadialBlurVolume : VolumeComponent, IPostProcessComponent
{
    [Tooltip("模糊中心点")]
    public FloatParameter X = new FloatParameter(0.5f);
    public FloatParameter Y = new FloatParameter(0.5f);

    [Range(0f, 10f), Tooltip("模糊的迭代次数")]
    public IntParameter BlurTimes = new ClampedIntParameter(1, 1, 10);
    [Range(0f, 10f), Tooltip("模糊半径")]
    public FloatParameter BlurRange = new ClampedFloatParameter(1.0f, 0.0f, 10.0f);
    [Range(0f, 10f), Tooltip("降采样次数")]
    public IntParameter RTDownSampling = new ClampedIntParameter(1, 1, 10);

    public bool IsActive() => RTDownSampling.value > 0f;

    public bool IsTileCompatible() => false;

}
  • 效果

RendererFeature 类

后处理组件也设置完成,那我们开始正式编辑渲染逻辑。

前设置模板

using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;



public class RadialBlurRenderFeature : ScriptableRendererFeature
{
    [System.Serializable]
    public class Settings
    {
        public RenderPassEvent renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing;
        public Shader shader;
    }
    public Settings settings = new Settings();

    RadialBlurPass radialBlurPass;           // 定义我们创建出Pass


    public override void Create()
    {
        this.name = "RadialBlur";    // 模糊渲染的名字
        radialBlurPass = new RadialBlurPass(RenderPassEvent.BeforeRenderingPostProcessing, settings.shader);    // 初始化 我们的渲染层级和Shader

    }
    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        renderer.EnqueuePass(radialBlurPass);
    }
}


public class RadialBlurPass : ScriptableRenderPass
{
    static readonly string RenderTag = " Blur Effects";                         // 设置渲染标签

    RadialBlurVolume radialBlur;                                                  // 定义组件类型

    Material Radialmaterial;                                                      // 后处理材质
                                                                                  //RenderTargetIdentifier renderTargetIdentifier;                                  // 设置当前渲染目标
    RenderTargetIdentifier BlurTex;
    RenderTargetIdentifier Temp1;

    public RadialBlurPass(RenderPassEvent evt, Shader blurshader)
    {
        renderPassEvent = evt;
        var shader = blurshader;

        if (shader == null)
        {
            Debug.LogError("没有指定Shader");
            return;
        }
        Radialmaterial = CoreUtils.CreateEngineMaterial(blurshader);
    }

    public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    {
        if (Radialmaterial == null)
        {
            Debug.LogError("材质初始化失败");
            return;
        }

        if (!renderingData.cameraData.postProcessEnabled)
        {
            Debug.LogError("");
            return;
        }

        var stack = VolumeManager.instance.stack;                                    // 传入 volume
        radialBlur = stack.GetComponent<RadialBlurVolume>();                     // 获取到后处理组件

        if (radialBlur == null)
        {
            Debug.LogError("获取组件失败");
            return;
        }

        var cmd = CommandBufferPool.Get(RenderTag);    // 渲染标签

        Render(cmd, ref renderingData);                 // 调用渲染函数

        context.ExecuteCommandBuffer(cmd);              // 执行函数,回收。
        CommandBufferPool.Release(cmd);

    }

    void Render(CommandBuffer cmd, ref RenderingData renderingData)
    {
        RenderTargetIdentifier sourceRT = renderingData.cameraData.renderer.cameraColorTarget;                 // 定义RT
        RenderTextureDescriptor inRTDesc = renderingData.cameraData.cameraTargetDescriptor;
        inRTDesc.depthBufferBits = 0;                                                                          // 清除深度

        // 定义屏幕尺寸
        var width = (int)(inRTDesc.width / radialBlur.RTDownSampling.value);
        var height = (int)(inRTDesc.height / radialBlur.RTDownSampling.value);


    }
}

开始处理模糊

绑定后处理组件的参数

Radialmaterial.SetFloat("_Loop", radialBlur.BlurTimes.value);             // Shader变量  和 Volume 组件属性 绑定
Radialmaterial.SetFloat("_X", radialBlur.X.value);                         // Shader变量  和 Volume 组件属性 绑定
Radialmaterial.SetFloat("_Y", radialBlur.Y.value);                         // Shader变量  和 Volume 组件属性 绑定
Radialmaterial.SetFloat("_Blur", radialBlur.BlurRange.value);             // Shader变量  和 Volume 组件属性 绑定

创建临时RT

int TempID1 = Shader.PropertyToID("Temp1");
int BlurTexID = Shader.PropertyToID("_BlurTex");              // 临时
// 获取一张临时RT
cmd.GetTemporaryRT(TempID1, width, height, 0, FilterMode.Bilinear, RenderTextureFormat.DefaultHDR); //申请一个临时图像,并设置相机rt的参数进去
cmd.GetTemporaryRT(BlurTexID, inRTDesc);   // 模糊图

前把原图传临时RT,RT和模糊储存到 BlurTex 里,

BlurTex = new RenderTargetIdentifier(BlurTexID);
        Temp1 = new RenderTargetIdentifier(TempID1);

        cmd.Blit(sourceRT, Temp1);                              // 摄像机渲染的图储存到 Temp1
        cmd.Blit(Temp1, BlurTex, Radialmaterial, 0);            // 临时图像 进行径向模糊
        cmd.Blit(BlurTex, sourceRT);            // 模糊 和原图混合

模糊结果输出到画面。

  • 完成

扩展 : 可以调整模糊半径

有时候我们不希望模糊中间部分,我们希望中间部分没有模糊。

所以在Shader路我们设置一个半径

half4 radialfrag(v2f i) : SV_Target
{

    float4 col = 0;
    float2 dir = (float2(_X,_Y) - i.uv) * _Blur * 0.01;
    float blurParams = saturate(distance(i.uv,float2(_X,_Y)) / _BufferRadius);   // 控制不模糊的半径

    for(int t = 0; t < _Loop; t++)
    {
        col += SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv + dir * t * blurParams)/ _Loop;
    }
    return col;
}
  • 我们在后处理组件中增加控制

using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;



public class RadialBlurVolume : VolumeComponent, IPostProcessComponent
{
    [Tooltip("模糊中心点")]
    public FloatParameter X = new FloatParameter(0.5f);
    public FloatParameter Y = new FloatParameter(0.5f);

    [Range(0f, 10f), Tooltip("模糊的迭代次数")]
    public IntParameter BlurTimes = new ClampedIntParameter(1, 1, 10);
    [Range(0f, 10f), Tooltip("模糊半径")]
    public FloatParameter BlurRange = new ClampedFloatParameter(1.0f, 0.0f, 10.0f);
    [Range(0f, 10f), Tooltip("降采样次数")]
    public IntParameter RTDownSampling = new ClampedIntParameter(1, 1, 10);
    [Range(0f, 10f), Tooltip("模糊半径")]
    public FloatParameter BufferRadius = new ClampedFloatParameter(1.0f, 0.0f, 5.0f);
    public bool IsActive() => RTDownSampling.value > 0f;

    public bool IsTileCompatible() => false;

}
  • 在脚本里绑定属性

Radialmaterial.SetFloat("_BufferRadius", radialBlur.BufferRadius.value);             // Shader变量  和 Volume 组件属性 绑定
  • 效果对比

    全模糊效果

全模糊效果

中间没有模糊

中间没有模糊

全代码

using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;



public class RadialBlurRenderFeature : ScriptableRendererFeature
{
    [System.Serializable]
    public class Settings
    {
        public RenderPassEvent renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing;
        public Shader shader;
    }
    public Settings settings = new Settings();

    RadialBlurPass radialBlurPass;           // 定义我们创建出Pass


    public override void Create()
    {
        this.name = "RadialBlur";    // 模糊渲染的名字
        radialBlurPass = new RadialBlurPass(RenderPassEvent.BeforeRenderingPostProcessing, settings.shader);    // 初始化 我们的渲染层级和Shader

    }
    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        renderer.EnqueuePass(radialBlurPass);
    }
}


public class RadialBlurPass : ScriptableRenderPass
{
    static readonly string RenderTag = " Blur Effects";                         // 设置渲染标签

    RadialBlurVolume radialBlur;                                                  // 定义组件类型

    Material Radialmaterial;                                                      // 后处理材质
                                                                                  //RenderTargetIdentifier renderTargetIdentifier;                                  // 设置当前渲染目标
    RenderTargetIdentifier BlurTex;
    RenderTargetIdentifier Temp1;

    public RadialBlurPass(RenderPassEvent evt, Shader blurshader)
    {
        renderPassEvent = evt;
        var shader = blurshader;

        if (shader == null)
        {
            Debug.LogError("没有指定Shader");
            return;
        }
        Radialmaterial = CoreUtils.CreateEngineMaterial(blurshader);
    }

    public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    {
        if (Radialmaterial == null)
        {
            Debug.LogError("材质初始化失败");
            return;
        }

        if (!renderingData.cameraData.postProcessEnabled)
        {
            Debug.LogError("");
            return;
        }

        var stack = VolumeManager.instance.stack;                                    // 传入 volume
        radialBlur = stack.GetComponent<RadialBlurVolume>();                     // 获取到后处理组件

        if (radialBlur == null)
        {
            Debug.LogError("获取组件失败");
            return;
        }

        var cmd = CommandBufferPool.Get(RenderTag);    // 渲染标签

        Render(cmd, ref renderingData);                 // 调用渲染函数

        context.ExecuteCommandBuffer(cmd);              // 执行函数,回收。
        CommandBufferPool.Release(cmd);

    }

    void Render(CommandBuffer cmd, ref RenderingData renderingData)
    {
        RenderTargetIdentifier sourceRT = renderingData.cameraData.renderer.cameraColorTarget;                 // 定义RT
        RenderTextureDescriptor inRTDesc = renderingData.cameraData.cameraTargetDescriptor;
        inRTDesc.depthBufferBits = 0;                                                                          // 清除深度


        // 定义屏幕尺寸
        var width = (int)(inRTDesc.width / radialBlur.RTDownSampling.value);
        var height = (int)(inRTDesc.height / radialBlur.RTDownSampling.value);

        Radialmaterial.SetFloat("_Loop", radialBlur.BlurTimes.value);             // Shader变量  和 Volume 组件属性 绑定
        Radialmaterial.SetFloat("_X", radialBlur.X.value);                         // Shader变量  和 Volume 组件属性 绑定
        Radialmaterial.SetFloat("_Y", radialBlur.Y.value);                         // Shader变量  和 Volume 组件属性 绑定
        Radialmaterial.SetFloat("_Blur", radialBlur.BlurRange.value);             // Shader变量  和 Volume 组件属性 绑定

        Radialmaterial.SetFloat("_BufferRadius", radialBlur.BufferRadius.value);             // Shader变量  和 Volume 组件属性 绑定

        int TempID1 = Shader.PropertyToID("Temp1");
        int BlurTexID = Shader.PropertyToID("_BlurTex");              // 临时
        // 获取一张临时RT
        cmd.GetTemporaryRT(TempID1, width, height, 0, FilterMode.Bilinear, RenderTextureFormat.DefaultHDR); //申请一个临时图像,并设置相机rt的参数进去
        cmd.GetTemporaryRT(BlurTexID, inRTDesc);   // 模糊图

        BlurTex = new RenderTargetIdentifier(BlurTexID);
        Temp1 = new RenderTargetIdentifier(TempID1);

        cmd.Blit(sourceRT, Temp1);                              // 摄像机渲染的图储存到 Temp1
        cmd.Blit(Temp1, BlurTex, Radialmaterial, 0);            // 临时图像 进行径向模糊
        cmd.Blit(BlurTex, sourceRT);            // 模糊 和原图混合
    }
}


投诉或建议