Unity自定义后处理——模糊效果

这篇具有很好参考价值的文章主要介绍了Unity自定义后处理——模糊效果。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

  大家好,我是阿赵。
  继续介绍后处理的做法,这一期介绍的是模糊效果的做法。

一、模糊效果的原理

我们还是用这个角色作为背景来实现模糊效果
unity图片模糊效果,Unity屏幕后处理,unity,游戏引擎,模糊,后处理

unity图片模糊效果,Unity屏幕后处理,unity,游戏引擎,模糊,后处理

这是模糊后的效果
unity图片模糊效果,Unity屏幕后处理,unity,游戏引擎,模糊,后处理

根据不同的参数,可以调整不同的模糊程度。

  在介绍做法之前,首先要明确一个基本的认知,模糊效果是非常消耗性能的一种处理。正常我们显示一张图片,每个像素根据UV坐标采样一次,得到颜色。而模糊处理,是每个像素点,除了采样自己,还要采样像素点周围的多个像素点,然后把采样得到的颜色值做不同的求平均值算法计算,而得到的模糊。
  概括的说,这个采样周围多个点的做法,就是定义卷积核。下面介绍的三种不同的模糊算法,区别就在于卷积核的计算方式不同。
  除了采样周围的多个像素点,我们还有可能需要多次进行采样,因为单次卷积核的采样计算,可能达不到我们想要的效果。
  所以说,模糊效果是一种消耗非常大的处理方式。我们经常说Bloom辉光效果性能消耗非常大,其实也是因为,Bloom效果也是建立在模糊处理的效果上再做叠加的,性能消耗其实还是出在模糊上。

二、几种不同的模糊效果实现

1、均值模糊(BoxBlur)

  均值模糊是采样当前像素点附近的9个像素颜色(包括自己),然后把所有颜色相加,最后除以9。因为是把9个像素的颜色直接取平均值的,所以成为均值模糊。
unity图片模糊效果,Unity屏幕后处理,unity,游戏引擎,模糊,后处理

  放大模糊后的画面,可以看到,单次的均值模糊,其实效果并不是很好,会出现一些方形的像素效果,模糊效果不是很平均。所以均值模糊也成为盒状模糊。
  做模糊效果,是需要在C#脚本里面做多次Graphics.Blit的,不过这里先跳过这一步,先看看Shader怎样写,最后再去看C#的实现。

Shader "Hidden/AzhaoBoxBlur"
{
	CGINCLUDE
		#include "UnityCG.cginc"


		sampler2D _MainTex;
		float4 _MainTex_TexelSize;
		float _BlurOffset;

		fixed4 fragBoxBlur(v2f_img i) : SV_Target
		{
			half4 col = tex2D(_MainTex, i.uv);
			//均值模糊
			float4 colBlur = 0;
			float4 blurUV = _MainTex_TexelSize.xyxy * float4(1, 1, -1, -1)*_BlurOffset;
			colBlur += tex2D(_MainTex, i.uv + blurUV.xy);//1,1
			colBlur += tex2D(_MainTex, i.uv + blurUV.xw);//1,-1
			colBlur += tex2D(_MainTex, i.uv + blurUV.zy);//-1,1
			colBlur += tex2D(_MainTex, i.uv + blurUV.zw);//-1,-1
			col.rgb = colBlur.rgb / 4;
			return col;
		}
	ENDCG

    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
		_BlurOffset("BlurOffset",Float) = 1
    }
    SubShader
    {
        // No culling or depth
        Cull Off ZWrite Off ZTest Always

        Pass
        {
            CGPROGRAM
            #pragma vertex vert_img
            #pragma fragment fragBoxBlur

            ENDCG
        }
		Pass
		{
			CGPROGRAM
			#pragma vertex vert_img
			#pragma fragment fragBoxBlur

			ENDCG
		}
    }
}

2、高斯模糊(GaussianBlur)

  高斯模糊应该是很常用的一种模糊手段,他的卷积核并不是附近9格,而是5X5范围,所以它的采样次数比均值模糊是要多很多的。并且,高斯模糊采样25个像素点,并不是直接求平均值的,而是有一个从中间往外减少的权重值,这个处理可以减轻均值模糊的盒状格子效果。
unity图片模糊效果,Unity屏幕后处理,unity,游戏引擎,模糊,后处理

不过如果只是单次采样,其实这个效果还是不算很好。
下面是Shader

Shader "Hidden/AzhaoGaussianBlur"
{
	CGINCLUDE
	#include "UnityCG.cginc"
	sampler2D _MainTex;
	float4 _MainTex_TexelSize;
	float _BlurOffset;
		//高斯模糊横向
	half4 frag_HorizontalBlur(v2f_img i) : SV_Target
	{
		half2 uv1 = i.uv + _MainTex_TexelSize.xy* half2(1, 0)*_BlurOffset * -2.0;
		half2 uv2 = i.uv + _MainTex_TexelSize.xy* half2(1, 0)*_BlurOffset * -1.0;
		half2 uv3 = i.uv;
		half2 uv4 = i.uv + _MainTex_TexelSize.xy* half2(1, 0)*_BlurOffset * 1.0;
		half2 uv5 = i.uv + _MainTex_TexelSize.xy* half2(1, 0)*_BlurOffset * 2.0;

		half4 s = 0;
		s += tex2D(_MainTex, uv1) * 0.05;
		s += tex2D(_MainTex, uv2) * 0.25;
		s += tex2D(_MainTex, uv3) * 0.40;
		s += tex2D(_MainTex, uv4) * 0.25;
		s += tex2D(_MainTex, uv5) * 0.05;
		return s;
	}
		//高斯模糊纵向
	half4 frag_VerticalBlur(v2f_img i) : SV_Target
	{
		half2 uv1 = i.uv + _MainTex_TexelSize.xy* half2(0, 1)*_BlurOffset * -2.0;
		half2 uv2 = i.uv + _MainTex_TexelSize.xy* half2(0, 1)*_BlurOffset * -1.0;
		half2 uv3 = i.uv;
		half2 uv4 = i.uv + _MainTex_TexelSize.xy* half2(0, 1)*_BlurOffset * 1.0;
		half2 uv5 = i.uv + _MainTex_TexelSize.xy* half2(0, 1)*_BlurOffset * 2.0;

		half4 s = 0;
		s += tex2D(_MainTex, uv1) * 0.05;
		s += tex2D(_MainTex, uv2) * 0.25;
		s += tex2D(_MainTex, uv3) * 0.40;
		s += tex2D(_MainTex, uv4) * 0.25;
		s += tex2D(_MainTex, uv5) * 0.05;
		return s;
	}
	ENDCG
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
		_BlurOffset("BlurOffset", Float) = 1
    }
    SubShader
    {
        // No culling or depth
        Cull Off ZWrite Off ZTest Always

        Pass
        {
            CGPROGRAM
            #pragma vertex vert_img
            #pragma fragment frag_HorizontalBlur

            ENDCG
        }
		Pass
		{
			CGPROGRAM
			#pragma vertex vert_img
			#pragma fragment frag_VerticalBlur

			ENDCG
		}
    }
}

3、Kawas模糊

  这个算法是我从网上学习的,根据介绍Kawase 模糊的思路是对距离当前像素越来越远的地方对四个角进行采样,且在两个大小相等的纹理之间进行乒乓式的blit,创新点在于,采用了随迭代次数移动的blur kernel,而不是类似高斯模糊,或均值模糊一样从头到尾固定的卷积核。
unity图片模糊效果,Unity屏幕后处理,unity,游戏引擎,模糊,后处理

  可以看到,这种模糊方式比上面两种效果是要好很多,那些马赛克条纹一样的东西基本看不到了。

Shader "Hidden/AzhaoKawaseBlur"
{
	CGINCLUDE
#include "UnityCG.cginc"
		uniform sampler2D _MainTex;
	uniform float4 _MainTex_TexelSize;
	uniform half _BlurOffset;

	struct v2f_DownSample
	{
		float4 pos: SV_POSITION;
		float2 uv: TEXCOORD1;
		float4 uv01: TEXCOORD2;
		float4 uv23: TEXCOORD3;
	};


	struct v2f_UpSample
	{
		float4 pos: SV_POSITION;
		float4 uv01: TEXCOORD1;
		float4 uv23: TEXCOORD2;
		float4 uv45: TEXCOORD3;
		float4 uv67: TEXCOORD4;
	};


	v2f_DownSample Vert_DownSample(appdata_img v)
	{
		v2f_DownSample o;
		o.pos = UnityObjectToClipPos(v.vertex);

		_MainTex_TexelSize = 0.5 * _MainTex_TexelSize;
		float2 uv = v.texcoord;
		o.uv = uv;
		o.uv01.xy = uv - _MainTex_TexelSize * float2(1 + _BlurOffset, 1 + _BlurOffset);//top right
		o.uv01.zw = uv + _MainTex_TexelSize * float2(1 + _BlurOffset, 1 + _BlurOffset);//bottom left
		o.uv23.xy = uv - float2(_MainTex_TexelSize.x, -_MainTex_TexelSize.y) * float2(1 + _BlurOffset, 1 + _BlurOffset);//top left
		o.uv23.zw = uv + float2(_MainTex_TexelSize.x, -_MainTex_TexelSize.y) * float2(1 + _BlurOffset, 1 + _BlurOffset);//bottom right

		return o;
	}

	half4 Frag_DownSample(v2f_DownSample i) : SV_Target
	{
		half4 sum = tex2D(_MainTex, i.uv) * 4;
		sum += tex2D(_MainTex, i.uv01.xy);
		sum += tex2D(_MainTex, i.uv01.zw);
		sum += tex2D(_MainTex, i.uv23.xy);
		sum += tex2D(_MainTex, i.uv23.zw);

		return sum * 0.125;
	}


		v2f_UpSample Vert_UpSample(appdata_img v)
	{
		v2f_UpSample o;
		o.pos = UnityObjectToClipPos(v.vertex);

		float2 uv = v.texcoord;

		_MainTex_TexelSize = 0.5 * _MainTex_TexelSize;
		_BlurOffset = float2(1 + _BlurOffset, 1 + _BlurOffset);

		o.uv01.xy = uv + float2(-_MainTex_TexelSize.x * 2, 0) * _BlurOffset;
		o.uv01.zw = uv + float2(-_MainTex_TexelSize.x, _MainTex_TexelSize.y) * _BlurOffset;
		o.uv23.xy = uv + float2(0, _MainTex_TexelSize.y * 2) * _BlurOffset;
		o.uv23.zw = uv + _MainTex_TexelSize * _BlurOffset;
		o.uv45.xy = uv + float2(_MainTex_TexelSize.x * 2, 0) * _BlurOffset;
		o.uv45.zw = uv + float2(_MainTex_TexelSize.x, -_MainTex_TexelSize.y) * _BlurOffset;
		o.uv67.xy = uv + float2(0, -_MainTex_TexelSize.y * 2) * _BlurOffset;
		o.uv67.zw = uv - _MainTex_TexelSize * _BlurOffset;

		return o;
	}

	half4 Frag_UpSample(v2f_UpSample i) : SV_Target
	{
		half4 sum = 0;
		sum += tex2D(_MainTex, i.uv01.xy);
		sum += tex2D(_MainTex, i.uv01.zw) * 2;
		sum += tex2D(_MainTex, i.uv23.xy);
		sum += tex2D(_MainTex, i.uv23.zw) * 2;
		sum += tex2D(_MainTex, i.uv45.xy);
		sum += tex2D(_MainTex, i.uv45.zw) * 2;
		sum += tex2D(_MainTex, i.uv67.xy);
		sum += tex2D(_MainTex, i.uv67.zw) * 2;

		return sum * 0.0833;
	}

		ENDCG
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
		_BlurOffset("BlurOffset", Float) = 1
    }
	SubShader
	{
		Cull Off ZWrite Off ZTest Always

		Pass
		{
			CGPROGRAM
			#pragma vertex Vert_DownSample
			#pragma fragment Frag_DownSample	
			ENDCG
		}

		Pass
		{
			CGPROGRAM
			#pragma vertex Vert_UpSample
			#pragma fragment Frag_UpSample
			ENDCG

		}
	}
}

4、双重模糊

  这里对比一下均值模糊和高斯模糊。
  高斯模糊的效果比均值模糊要好一些,但马赛克条纹的情况还是存在,而且性能消耗巨大。
为了解决这个问题,我们可以进行双重模糊的操作。
具体的做法是,在C#做Graphics.Blit的时候,开2个循环。
第一个循环里面,每次对RenderTexture的宽高除以2,做降采样。
第二个循环里面,每次对RenderTexture的宽高乘以2,做升采样。
经过了RenderTexture的宽高改变之后,原来的马赛克条纹效果,基本上就可以消除。
  双重采样在同样的参数设置下,效果是比高斯模糊更好,更平滑的。不过这个做法要注意的一点,是因为创建了多个RenderTexture,必须记得做对应的释放操作。

三、C#代码

  下面的代码,是C#端兼容了上面说的三种不同的模糊算法,然后做了双重采样之后的结果。文章来源地址https://www.toymoban.com/news/detail-778229.html

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BlurCtrl : MonoBehaviour
{
    private Material blurMat;
    public bool isBlur = false;
    [Range(0, 4)]
    public float blurSize = 0;
    [Range(-3, 3)]
    public float blurOffset = 1;
    [Range(1, 3)]
    public int blurType = 3;
    void Start()
    {

    }


    void Update()
    {

    }

    private Material GetBlurMat(int bType)
    {
        if (bType == 1)
        {
            return new Material(Shader.Find("Hidden/AzhaoBoxBlur"));
        }
        else if (bType == 2)
        {
            return new Material(Shader.Find("Hidden/AzhaoGaussianBlur"));
        }
        else if (bType == 3)
        {
            return new Material(Shader.Find("Hidden/AzhaoKawaseBlur"));
        }
        else
        {
            return null;
        }
    }

    private void ReleaseRT(RenderTexture rt)
    {
        if (rt != null)
        {
            RenderTexture.ReleaseTemporary(rt);
        }
    }

    private bool CheckNeedCreateBlurMat(Material mat, int bType)
    {
        if (mat == null)
        {
            return true;
        }
        if (mat.shader == null)
        {
            return true;
        }
        if (bType == 1)
        {
            if (mat.shader.name != "Hidden/AzhaoBoxBlur")
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        else if (bType == 2)
        {
            if (mat.shader.name != "Hidden/AzhaoGaussianBlur")
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        else if (bType == 3)
        {
            if (mat.shader.name != "Hidden/AzhaoKawaseBlur")
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        else
        {
            return false;
        }
    }
    private void BlurFun(RenderTexture source, RenderTexture destination, float blurTime, int bType, float offset)
    {
        if (CheckNeedCreateBlurMat(blurMat, bType) == true)
        {
            blurMat = GetBlurMat(bType);
        }
        if (blurMat == null || blurMat.shader == null || blurMat.shader.isSupported == false)
        {
            return;
        }
        blurMat.SetFloat("_BlurOffset", offset);
        float width = source.width;
        float height = source.height;
        int w = Mathf.FloorToInt(width);
        int h = Mathf.FloorToInt(height);
        RenderTexture rt1 = RenderTexture.GetTemporary(w, h);
        RenderTexture rt2 = RenderTexture.GetTemporary(w, h);
        Graphics.Blit(source, rt1);
//降采样
        for (int i = 0; i < blurTime; i++)
        {
            ReleaseRT(rt2);
            width = width / 2;
            height = height / 2;
            w = Mathf.FloorToInt(width);
            h = Mathf.FloorToInt(height);
            rt2 = RenderTexture.GetTemporary(w, h);
            Graphics.Blit(rt1, rt2, blurMat, 0);
            width = width / 2;
            height = height / 2;
            w = Mathf.FloorToInt(width);
            h = Mathf.FloorToInt(height);
            ReleaseRT(rt1);
            rt1 = RenderTexture.GetTemporary(w, h);
            Graphics.Blit(rt2, rt1, blurMat, 1);
        }
//升采样
        for (int i = 0; i < blurTime; i++)
        {
            ReleaseRT(rt2);
            width = width * 2;
            height = height * 2;
            w = Mathf.FloorToInt(width);
            h = Mathf.FloorToInt(height);
            rt2 = RenderTexture.GetTemporary(w, h);
            Graphics.Blit(rt1, rt2, blurMat, 0);
            width = width * 2;
            height = height * 2;
            w = Mathf.FloorToInt(width);
            h = Mathf.FloorToInt(height);
            ReleaseRT(rt1);
            rt1 = RenderTexture.GetTemporary(w, h);
            Graphics.Blit(rt2, rt1, blurMat, 1);
        }
        Graphics.Blit(rt1, destination);
        ReleaseRT(rt1);
        rt1 = null;
        ReleaseRT(rt2);
        rt2 = null;
        return;
    }
    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if (isBlur == true)
        {
            if (blurSize > 0)
            {
                BlurFun(source, source, blurSize, blurType, blurOffset);
            }
            Graphics.Blit(source, destination);

        }
        else
        {
            Graphics.Blit(source, destination);
        }
    }
}

到了这里,关于Unity自定义后处理——模糊效果的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Unity 引擎做残影效果——2、屏幕后处理方式

    Unity实现残影效果   大家好,我是阿赵。   这里继续介绍Unity里面做残影的方法。之前介绍了BakeMesh的方法做残影,这一期介绍的是用屏幕后处理的方法做残影。   之前的BakeMesh方法,是真的生成了很多个网格模型在场景里面。如果用后处理做,就没有这个过程。   

    2024年01月17日
    浏览(31)
  • unity webgl网页运行后屏幕模糊,UI无响应问题解决

    【记一个莫名其妙的问题】 工具:Unity 2019.4.40f1c1 先前Unity打包apk,设置了最大帧率15 在Project Settings -Quality中设置了Other-VSync Count:Don’t Sync 运行后,帧率稳定在100上下,呵呵 后来在代码中加了一行: 问题解决 今天,准备再打一个webgl包 打包运行后,打开网页,画面停留在

    2024年02月06日
    浏览(29)
  • Unity实现屏幕淡入淡出效果

    预期效果:为实现在进入新场景时的淡入淡出效果 编译器版本:2021.3.19f1c1 使用组件:UI -- RawImage 在Unity场景切换的时候,使用RawImage制作场景的淡入淡出效果。 添加RawImage,覆盖全屏,将脚本添加上去

    2024年02月12日
    浏览(78)
  • Unity背景模糊图片高斯模糊高性能的实现方案

    环境: unity2021.3.x 效果: 模糊前: 模糊后: 模糊前: 模糊后: 实现核心思路(shader): github地址:高斯模糊 Github地址

    2024年04月26日
    浏览(26)
  • Unity中Shader抓取屏幕并实现扭曲效果(优化)

    对上一篇中实现的shader进行优化 1、定义结构体用于传入顶点坐标系 struct appdata { float4 vertex : POSITION; //从应用程序阶段的输入,多加一个uv,用于对扭曲纹理的采样 float2 uv : TEXCOORD; }; 2、因为UnityObjectToClipPos是从本地空间转换到裁剪空间,但是没有进行透视除法,所以需要对其

    2024年02月03日
    浏览(39)
  • 【游戏开发小技】Unity通过UI全屏图来模糊场景画面(Shader | 模糊 | 滤镜 | Blur)

    一、前言 嗨,大家好,我是新发。 以前我写文章都是很长很长,接下来我会尝试用新的方式来写博客,尽量简短,以实用为主。同时也是作为自己零碎的一些记录,方便查阅。 本文我要说的是在 Unity 中通过 UI 全屏图来模糊场景画面的效果。 二、效果演示 这是没用模糊效果

    2024年02月05日
    浏览(32)
  • Unity数字孪生UI设计——Text字体模糊处理

    问题描述: 使用Text控件之后,看上去感觉没啥问题,但是一旦运行项目就变得非常模糊,想截个高清的场景图都不行 解决方法: 1、修改text对应的width和Height,使得他变成原来的2倍 2、修改scale,改成原来的0.5倍,这样一来一去就使得原来的大小起始并没有改变 3、修改字体

    2024年02月12日
    浏览(31)
  • 【Unity的 Built-in 渲染管线下实现好用的GUI模糊效果_Blur_案例分享(内附源码)】

    其他的模糊效果,在这一篇。 效果如图: 新建一个C#文件,命名为\\\"CommandBlur\\\",打开C#,删除内容,复制粘贴下面的代码:

    2024年02月07日
    浏览(29)
  • unity 2D中,实现点击按钮可以游戏暂停的效果

    在Unity 2D中,可以通过以下步骤和代码来设置一个按钮,实现游戏暂停的效果: 创建一个空对象,命名为\\\"GameManager\\\",用于管理游戏状态。 在GameManager对象上添加一个脚本,命名为\\\"PauseManager\\\"。 在脚本中添加以下代码: 在场景中创建一个Canvas对象,用于放置按钮。 在Canvas下创

    2024年02月03日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包