【Unity3D】高斯模糊特效

news/2025/2/16 3:22:34/

1 高斯模糊原理

        边缘检测特效中使用了卷积运算进行了边缘检测,本文实现的高斯模糊特效同样使用了卷积运算,关于卷积核和卷积运算的概念,读者可以参考边缘检测特效。

        本文完整资源见→Unity3D高斯模糊特效。

        我们将用于模糊处理的卷积核称为模糊算子,它一般满足以下条件:

  • 卷积核中权值上下对称、左右对称;
  • 卷积核中每个权值大于或等于 0,小于 1;
  • 卷积核中所有权值之和为 1。

        我们将所有权值都为 1 / n(n 为权值个数)的卷积核称为平均模糊算子;将权值随位置变化且符合高斯分布(或正太分布)的卷积核称为高斯模糊算子(或高斯核),它较好地模拟了邻域中每个像素对当前处理像素的影响程度(距离越近,影响越大)。高斯方程如下:

         σ 是标准差,其值越大,高斯分布函数的图像越矮胖,一般取值为 1,x、y 为当前位置到卷积核中心的整数距离。要构建一个高斯核,我们只需要计算高斯核中各个位置对应的高斯值。为了保证模糊处理后的图像不会变暗,我们需要对高斯核中的元素进行归一化,即将所有元素都除以它们的权值和,从而保证归一化后的权值和为 1。因此,高斯函数中 e 前面的系数不会对高斯核产生任何影响,在计算高斯核的过程中可以省去。

        高斯核的维数越高,模糊程度越大。使用一个 n * n 的卷积核,需要进行 n * n * w * h 次纹理采样(w、h 分别为图像的宽高),为节省性能,我们将二维高斯核拆分为 2 个一维高斯核,采样次数只需要 2 * n * w * h 次。进一步观察到,2 个高斯核中包含了很多重复权重。对于一个大小为 5 的一维高斯核,实际只需记录 3 个权值即可。

2 代码实现

        GaussianBlur.cs

using UnityEngine;[ExecuteInEditMode] // 编辑态可以查看脚本运行效果
[RequireComponent(typeof(Camera))] // 需要相机组件
public class GaussianBlur : MonoBehaviour {[Range(0, 4)]public int iterations = 3; // 高斯模糊迭代次数[Range(0.2f, 3.0f)]public float blurSpread = 0.6f; // 每次迭代纹理坐标偏移的速度[Range(1, 8)]public int downSample = 2; // 降采样比率private Material material = null; // 材质private void Start() {material = new Material(Shader.Find("MyShader/GaussianBlur"));material.hideFlags = HideFlags.DontSave;}void OnRenderImage (RenderTexture src, RenderTexture dest) {if (material != null) {int rtW = src.width / downSample; // 降采样的纹理宽度int rtH = src.height / downSample; // 降采样的纹理高度RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, 0);buffer0.filterMode = FilterMode.Bilinear; // 滤波模式设置为双线性Graphics.Blit(src, buffer0);for (int i = 0; i < iterations; i++) {material.SetFloat("_BlurSize", 1.0f + i * blurSpread); // 设置模糊尺寸(纹理坐标的偏移量)RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);Graphics.Blit(buffer0, buffer1, material, 0); // 渲染垂直的PassRenderTexture.ReleaseTemporary(buffer0);buffer0 = buffer1;buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);Graphics.Blit(buffer0, buffer1, material, 1); // 渲染水平的PassRenderTexture.ReleaseTemporary(buffer0);buffer0 = buffer1;}Graphics.Blit(buffer0, dest);RenderTexture.ReleaseTemporary(buffer0);} else {Graphics.Blit(src, dest);}}
}

        GaussianBlur.shader

Shader "MyShader/GaussianBlur" { // 高斯模糊Properties {_MainTex ("Base (RGB)", 2D) = "white" {} // 主纹理_BlurSize ("Blur Size", Float) = 1.0 // 模糊尺寸(纹理坐标的偏移量)}SubShader {CGINCLUDE#include "UnityCG.cginc"sampler2D _MainTex; // 主纹理half4 _MainTex_TexelSize; // _MainTex的像素尺寸大小, float4(1/width, 1/height, width, height)float _BlurSize; // 模糊尺寸(纹理坐标的偏移量)struct v2f {float4 pos : SV_POSITION; // 模型空间顶点坐标half2 uv[5]: TEXCOORD0; // 5个邻域的纹理坐标};v2f vertBlurVertical(appdata_img v) { // 垂直模糊顶点着色器v2f o;o.pos = UnityObjectToClipPos(v.vertex); // 模型空间顶点坐标变换到裁剪空间, 等价于: mul(UNITY_MATRIX_MVP, v.vertex)half2 uv = v.texcoord;o.uv[0] = uv;o.uv[1] = uv + float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;o.uv[2] = uv - float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;o.uv[3] = uv + float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;o.uv[4] = uv - float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;		 return o;}v2f vertBlurHorizontal(appdata_img v) { // 水平模糊顶点着色器v2f o;o.pos = UnityObjectToClipPos(v.vertex); // 模型空间顶点坐标变换到裁剪空间, 等价于: mul(UNITY_MATRIX_MVP, v.vertex)half2 uv = v.texcoord;o.uv[0] = uv;o.uv[1] = uv + float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;o.uv[2] = uv - float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;o.uv[3] = uv + float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;o.uv[4] = uv - float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;		 return o;}fixed4 fragBlur(v2f i) : SV_Target {float weight[3] = {0.4026, 0.2442, 0.0545}; // 大小为5的一维高斯核,实际只需记录3个权值fixed3 sum = tex2D(_MainTex, i.uv[0]).rgb * weight[0];for (int j = 1; j < 3; j++) {sum += tex2D(_MainTex, i.uv[j * 2 - 1]).rgb * weight[j]; // 中心右侧或下侧的纹理*权值sum += tex2D(_MainTex, i.uv[j * 2]).rgb * weight[j]; // 中心左侧或上侧的纹理*权值}return fixed4(sum, 1.0);}ENDCGZTest Always Cull Off ZWrite OffPass {NAME "GAUSSIAN_BLUR_VERTICAL"CGPROGRAM#pragma vertex vertBlurVertical  #pragma fragment fragBlurENDCG  }Pass {  NAME "GAUSSIAN_BLUR_HORIZONTAL"CGPROGRAM  #pragma vertex vertBlurHorizontal  #pragma fragment fragBlurENDCG}}FallBack "Diffuse"
}

3 运行效果

        1)原图

        2)模糊处理

        调整模糊迭代次数 iterations 由 0 ~ 4 变化,效果如下:


http://www.ppmy.cn/news/189865.html

相关文章

scrcpy: 在电脑上使用Android手机

文章目录 1. 目的2. 配置2.0 配置思路2.1 安装 scrcpy 2.02.2 添加 udev 规则2.3 手机设置权限2.4 开启 scrcpy 1. 目的 打算在电脑上使用手机上的 App&#xff0c;如京东读书&#xff0c;或QQ。不用电脑版对应软件的原因是&#xff0c;电脑上需要重新登录&#xff0c;有些软件…

代码随想录算法训练营第三天 | 203.移除链表元素,707.设计链表,206.反转链表

203. 移除链表元素 正确运行的代码如下&#xff1a; truct ListNode* removeElements(struct ListNode* head, int val){while (head && head->val val){head head->next;}struct ListNode * cur head;while (cur && cur->next){// printf("…

dhcp服务器自动分配地址思科,思科服务器配置dhcp服务器方法

因为现在生活中网络的流行以及广泛的使用&#xff0c;但企业和学校都会配备思科服务器&#xff0c;因为在配备了服务器之后&#xff0c;可以让不论在什么地方的电脑&#xff0c;只要连接上局域网之后&#xff0c;就可以通过自动获取Ip地址的方式获取Ip地址网关&#xff0c;这样…

思科FTP服务器基本配置

思科FTP服务器基本配置 参赛话题&#xff1a;学习笔记https://activity.csdn.net/creatActivity?id10213 ​ 目录 一、准备TFTP服务器 二、备份IOS 三、从TFTP升级IOS 四、备份配置文件到TFTP服务器 五、从TFTP下载配置文件 一、准备TFTP服务器 Router# ping tftp_addres…

思科服务器dns配置文件,思科完成dns服务器配置

思科完成dns服务器配置 内容精选 换一换 云速建站支持以下三种情况的域名解析:您在华为云购买域名,使用华为云解析服务。您在第三方购买域名,使用华为云解析服务。您可以通过云速建站域名配置助手或者手动操作完成域名的解析和绑定,推荐使用云速建站域名配置助手,由云速建…

cisco在服务器编辑首页信息,cisco设置

cisco路由向来以难配置而绝不天下。那么cisco怎么设置vlan ip?秋天网 Qiutian.ZqNF.Com小编整理了相关资料&#xff0c;给大家介绍。有需要学习的同学可参考参考。 cisco设置vlan ip步骤 1.注意事项 1.1.交换机启动需要大约4-5分钟; 1.2.网线插入交换机接口从黄变为绿需要大约…

cisco服务器设置步骤_设置您自己的邮件服务器的10个步骤

cisco服务器设置步骤 A simple guide to setup your own mail server using MailCow, DigitalOcean and Docker 使用MailCow,DigitalOcean和Docker设置自己的邮件服务器的简单指南 Setting a mail server is easy and hard at the same time. What an irony! Let’s begin wi…

思科高计算性能服务器,全面解析:思科UCS统一计算刀片服务器

【IT168 评论】思科统一计算系统是下一代数据中心平台,在一个紧密结合的系统中整合了计算、网络、存储接入与虚拟化功能,旨在降低总体拥有成本(TCO),同时提高业务灵活性。该系统包含一个低延时无丢包万兆以太网统一网络阵列,以及多台企业级x86架构服务器。它是一个集成的可…