UnityShader实例13:屏幕特效之均值模糊(Box Blur)

2024-05-28 23:32

本文主要是介绍UnityShader实例13:屏幕特效之均值模糊(Box Blur),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

均值模糊(Box Blur)



概述



由于公司手游项目需求,需要一个适合手机平台的模糊效果,同时需要开放一个参数便于调节模糊值。我首先想到的就是ps里面的均值模糊。查资料可以知道均值模糊是一种快速的图像模糊技术,相比与传统的卷积模糊(如高斯模糊),均值模糊可以更加有效率的完成对图像模糊。在unity官方自带imageeffect包也有一个blur的屏幕特效,用的就是均值模糊算法,只不过他只采样了离原像素上下左右模糊半径(Blur Spread)距离的四个像素进行平均处理。然后做迭代(Iterations)处理,即将模糊后的图像再次采样模糊,迭代次数越高越模糊,同时也会产生更多的drawcall,本文的效果就是从这个例子简化而来,使之适合手游项目,并且傻瓜式方便的调节参数。



原理


均值模糊的原理是通过图形滤波器来把一个像素和周围的像素一起求平均值,比如一个三阶的图像滤波器构造其实就是一个3*3的数组(n阶数组中的元素成为滤波器的系数和滤波器的权重,n称为滤波器的阶),相当于把一个像素和周围8个像素相加在一起再除以9求平均值,等于把一个像素和周围的像素搅拌在一起,自然就模糊了。均值滤波器与高斯模糊的滤波器不同的地方,就是采样的像素权重是相等的,因此效果相对而言要比高斯模糊差,但速度却要快一些。本例其实只采样了四个像素求和做平均,然后通过迭代多次采样,实现比较好的模糊效果。下面是一个三阶的均值滤波器:

      [1,1,1]
1/9 [1,1,1]
[1,1,1]

本例采用的滤波器:

         [0,1,0]
 1/4  [1,0,1]
[0,1,0]





Shader代码实现



shader代码部分相对比较简单,首先需要定义两个内部变量,_MainTex_TexelSize和_BlurOffsets,这两个变量都属于unity的黑魔法,官方文档并没有详细说明,_MainTex_TexelSize的解释可以参考这里_BlurOffsets这个参数是用来接收从C#脚本里面穿过来的参数,即模糊半径。


		half4 _MainTex_TexelSize;half4 _BlurOffsets;


在顶点输入结构体里面定义一个一维四阶数组用来存储uv坐标:



		struct v2f {float4 vertex : SV_POSITION;half2 texcoord : TEXCOORD0;half2 taps[4] : TEXCOORD1; };


在vert函数里面将uv坐标进行偏移获得原像素上下左右偏移_BlurOffsets像素,并存储在taps[]数组里面,传给frag函数




		v2f vert (appdata_t v){v2f o;o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);o.texcoord = v.texcoord - _BlurOffsets.xy * _MainTex_TexelSize.xy;//将C#脚本传过来的图像偏移回原位置o.taps[0] = o.texcoord + _MainTex_TexelSize * _BlurOffsets.xy;o.taps[1] = o.texcoord - _MainTex_TexelSize * _BlurOffsets.xy;o.taps[2] = o.texcoord + _MainTex_TexelSize * _BlurOffsets.xy * half2(1,-1);o.taps[3] = o.texcoord - _MainTex_TexelSize * _BlurOffsets.xy * half2(1,-1);return o;}


在frag函数使用vert函数传过来的uv坐标数组采样图像然后进行叠加后平均化,以达到模糊效果:



		fixed4 frag (v2f i) : SV_Target{half4 color = tex2D(_MainTex, i.taps[0]);color += tex2D(_MainTex, i.taps[1]);color += tex2D(_MainTex, i.taps[2]);color += tex2D(_MainTex, i.taps[3]); return color * 0.25;}


Shader完整代码


VF版本代码01


Shader "PengLu/ImageEffect/Unlit/BlurBox" {Properties {_MainTex ("Base (RGB)", 2D) = "white" {}
}SubShader {	Pass {ZTest Always  	ZWrite OffCGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata_t {float4 vertex : POSITION;float2 texcoord : TEXCOORD0;};struct v2f {float4 vertex : SV_POSITION;half2 texcoord : TEXCOORD0;half2 taps[4] : TEXCOORD1; };sampler2D _MainTex;half4 _MainTex_TexelSize;half4 _BlurOffsets;v2f vert (appdata_t v){v2f o;o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);o.texcoord = v.texcoord - _BlurOffsets.xy * _MainTex_TexelSize.xy;o.taps[0] = o.texcoord + _MainTex_TexelSize * _BlurOffsets.xy;o.taps[1] = o.texcoord - _MainTex_TexelSize * _BlurOffsets.xy;o.taps[2] = o.texcoord + _MainTex_TexelSize * _BlurOffsets.xy * half2(1,-1);o.taps[3] = o.texcoord - _MainTex_TexelSize * _BlurOffsets.xy * half2(1,-1);return o;}fixed4 frag (v2f i) : SV_Target{half4 color = tex2D(_MainTex, i.taps[0]);color += tex2D(_MainTex, i.taps[1]);color += tex2D(_MainTex, i.taps[2]);color += tex2D(_MainTex, i.taps[3]); return color * 0.25;}ENDCG}
}
Fallback off
}



C#脚本代码




C#脚本相对也比较简单,这个脚本主要负责把抓取屏幕并传递参数给shader进行模糊处理,这个脚本是从官方的简化而来,将迭代的次数固定为2,只留下模糊半径一个参数调节。关键的两个函数需要注意下:



            Graphics.BlitMultiTap (source, dest, material,new Vector2(-off, -off),new Vector2(-off,  off),new Vector2( off,  off),new Vector2( off, -off));


RenderTexture buffer = RenderTexture.GetTemporary(rtW, rtH, 0);



Graphics.BlitMultiTap函数官方解释在这里 大概意思是传递多个位图块给shader进行处理,每个位图块有各自的偏移(由vector[]数组决定)。但实际上我测试发现,这个函数传递的位图块是配合固定管线编程使用的,在5.0以下的blur特效shader代码里有以下代码:上面那个函数生成的四个纹理块一次对应下面四个_MainTex,shader代码里面并没有偏移,都是从Graphics.BlitMultiTap传递过来。

SubShader {Pass {ZTest Always Cull Off ZWrite Off Fog { Mode Off }SetTexture [_MainTex] {constantColor (0,0,0,0.25) combine texture * constant alpha}SetTexture [_MainTex] {constantColor (0,0,0,0.25) combine texture * constant + previous}SetTexture [_MainTex] {constantColor (0,0,0,0.25) combine texture * constant + previous}SetTexture [_MainTex] {constantColor (0,0,0,0.25) combine texture * constant + previous}}}




而在unity5.0的官方blur shader里面已经将固定管线的相关代码删除,而对vert&frag shader编程,它实际上传递了一个uv坐标偏移值为(off,off)的位图块给shader,并将偏移值传给内置变量_BlurOffsets,我们在相关代码里要重新做偏移,才会有偏移效果。因此Graphics.BlitMultiTap我们可以改成:




            Graphics.BlitMultiTap (source, dest, material,new Vector2(off, off) );



RenderTexture.GetTemporary这个函数在这里主要是用来重新设定抓取的屏幕图像的长宽,在本例中我们将长宽设置成原来的8分之1后然后再传递给shader处理,可以是采样计算消耗降低到原来的64分之1,只是因此多消耗一个drawcall。由于还迭代了两次,最后这个屏幕特效需要消耗4个drawcall,当然由于做了个判断,当Blur Size为0时,只消耗1个drallcall,不做模糊处理。



完整C#脚本如下:

using UnityEngine;
using System.Collections;
using System;[ExecuteInEditMode]
[AddComponentMenu ("PengLu/ImageEffect/Blurbox")]
public class ImageEffect_BlurBox : MonoBehaviour {#region Variablespublic Shader BlurBoxShader = null;private Material BlurBoxMaterial = null;[Range(0.0f, 1.0f)]public float BlurSize = 0.5f;#endregion#region PropertiesMaterial material{get{if(BlurBoxMaterial == null){BlurBoxMaterial = new Material(BlurBoxShader);BlurBoxMaterial.hideFlags = HideFlags.HideAndDontSave;	}return BlurBoxMaterial;}}#endregion// Use this for initializationvoid Start () {BlurBoxShader = Shader.Find("PengLu/ImageEffect/Unlit/BlurBox");// Disable if we don't support image effectsif (!SystemInfo.supportsImageEffects){enabled = false;return;}// Disable the image effect if the shader can't// run on the users graphics cardif (!BlurBoxShader || !BlurBoxShader.isSupported)enabled = false;return;}public void FourTapCone (RenderTexture source, RenderTexture dest,int iteration){float off = BlurSize*iteration+0.5f;Graphics.BlitMultiTap (source, dest, material,new Vector2(-off, -off),new Vector2(-off,  off),new Vector2( off,  off),new Vector2( off, -off));}private void DownSample4x (RenderTexture source, RenderTexture dest){float off = 1.0f;// Graphics.Blit(source, dest, material);Graphics.BlitMultiTap (source, dest, material,new Vector2(off, off),new Vector2(-off,  off),new Vector2( off,  off),new Vector2( off, -off));}void OnRenderImage (RenderTexture sourceTexture, RenderTexture destTexture){	if(BlurSize != 0 && BlurBoxShader != null){int rtW = sourceTexture.width/8;int rtH = sourceTexture.height/8;RenderTexture buffer = RenderTexture.GetTemporary(rtW, rtH, 0);DownSample4x (sourceTexture, buffer);for(int i = 0; i < 2; i++){RenderTexture buffer2 = RenderTexture.GetTemporary(rtW, rtH, 0);FourTapCone (buffer, buffer2,i);RenderTexture.ReleaseTemporary(buffer);buffer = buffer2;}Graphics.Blit(buffer, destTexture);RenderTexture.ReleaseTemporary(buffer);}else{Graphics.Blit(sourceTexture, destTexture);}}// Update is called once per framevoid Update () {#if UNITY_EDITORif (Application.isPlaying!=true){BlurBoxShader = Shader.Find("PengLu/ImageEffect/Unlit/BlurBox");}#endif}public void OnDisable () {if (BlurBoxMaterial)DestroyImmediate (BlurBoxMaterial);}
}


最终模糊特效效果:





这篇关于UnityShader实例13:屏幕特效之均值模糊(Box Blur)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1012001

相关文章

swiper实例

大家好,我是燐子,今天给大家带来swiper实例   微信小程序中的 swiper 组件是一种用于创建滑动视图的容器组件,常用于实现图片轮播、广告展示等效果。它通过一系列的子组件 swiper-item 来定义滑动视图的每一个页面。 基本用法   以下是一个简单的 swiper 示例代码:   WXML(页面结构) <swiper autoplay="true" interval="3

Java面试题:通过实例说明内连接、左外连接和右外连接的区别

在 SQL 中,连接(JOIN)用于在多个表之间组合行。最常用的连接类型是内连接(INNER JOIN)、左外连接(LEFT OUTER JOIN)和右外连接(RIGHT OUTER JOIN)。它们的主要区别在于它们如何处理表之间的匹配和不匹配行。下面是每种连接的详细说明和示例。 表示例 假设有两个表:Customers 和 Orders。 Customers CustomerIDCus

笔记本电脑屏幕模糊?6招恢复屏幕清晰!

在数字化时代的浪潮中,笔记本电脑已成为我们生活、学习和工作中不可或缺的一部分。然而,当那曾经清晰明亮的屏幕逐渐变得模糊不清时,无疑给我们的使用体验蒙上了一层阴影。屏幕模糊不仅影响视觉舒适度,更可能对我们的工作效率和眼睛健康构成威胁。 遇到笔记本电脑屏幕模糊的情况时我们应该如何解决?本文将与大家分享6个简单易懂的解决方法。 方法一:调整Windows分辨率 电脑屏幕模糊显示不清晰怎

如何实现一台机器上运行多个MySQL实例?

在一台机器上一个MySQL服务器运行多个MySQL实例有什么好处?这里我先入为主给大家介绍这样做至少存在两个好处(看完这篇文章后理解会更透彻): (1)减轻服务器链接负担 (2)为不同的用户提供不同的mysqld服务器的访问权限以方便这些用户进行自我管理。   下面我介绍具体的实现过程: 一、准备工作     台式机一台、Windows系统、MySQL服务器(我安装的版本是MySQL

(13)DroneCAN 适配器节点(一)

文章目录 前言 1 特点 2 固件  3 ArduPilot固件DroneCAN设置 4 DroneCAN适配器节点 前言 这些节点允许现有的 ArduPilot 支持的外围设备作为 DroneCAN 或 MSP 设备适应 CAN 总线。这也允许扩展自动驾驶仪硬件的功能。如允许 I2C 设备(如罗盘或空速)距离自动驾驶仪 1m 以上,并实现多达 32 个伺服输出通道。

Docker Compose--安装Nginx--方法/实例

原文网址:Docker Compose--安装Nginx--方法/实例_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍Docker Compose如何安装Nginx。 目录结构 ├── config│   ├── cert│   │   ├── xxx_bundle.pem│   │   └── xxx.key│   ├── conf.d│   └── nginx.co

算法13—Bit Map算法简介

1. Bit Map算法简介          来自于《编程珠玑》。所谓的Bit-map就是用一个bit位来标记某个元素对应的Value, 而Key即是该元素。由于采用了Bit为单位来存储数据,因此在存储空间方面,可以大大节省。 2、 Bit Map的基本思想         我们先来看一个具体的例子,假设我们要对0-7内的5个元素(4,7,2,5,3)排

【大数据 复习】第11,12,13,14章

Web应用与流数据 1.在Web应用、网络监控、传感监测等领域,兴起了一种新的数据密集型应用——静态数据,即数据以大量、快速、时变的流形式持续到达。( )    正确答案: 错误 错误在静态数据,这里应该叫非静态数据之类的,虽然没有这个名词。 2.流数据适合采用批量计算,因为流数据适合用传统的关系模型建模。( )    正确答案: 错误 传统的关系模型一般是用于静态数据的存储和分析,例如 S

让IE8支持CSS3属性(border-radius、box-shadow、linear-gradient)

下载 PIE-1.0.0.zip解压后,将文件夹重命名为PIE,放到项目目录下在CSS3文件中添加一行代码 behavior: url(PIE/PIE.htc); 例如: .form__input{border-radius: 0.3em;behavior: url(PIE/PIE.htc);} 参考: TYStudio-专注WEB前端开发 css3pie

【计算机网络篇】数据链路层(13)共享式以太网与交换式以太网的对比

文章目录 🍔共享式以太网与交换式以太网的对比🔎主机发送单播帧的情况🔎主机发送广播帧的情况🔎多对主机同时通信 🛸使用集线器和交换机扩展共享式以太网的区别 🍔共享式以太网与交换式以太网的对比 下图是使用集线器将4台主机互联而成的小型共享式以太网 下图是使用交换机将4台主机互联而成的小型交换式以太网 我们假设交换机的转发表已经学习到了所有主机与自己各接口的对应