一.简介

 
今天来学习一下后处理中比较常用的一种效果,屏幕模糊效果。模糊效果,在图像处理中经常用到,Photoshop中也有类似的滤镜。我们在游戏中也会经常用到。因为屏幕模糊效果是一些高级后处理效果的基础,比如景深等效果都需要用屏幕模糊效果来实现,所以我们首先看一下屏幕模糊效果,然后通过屏幕模糊,进一步学习景深效果与运动模糊效果的实现。
所谓模糊,也就是不清楚,清晰的图片,各个像素之间会有明显的过渡,而如果各个像素之间的差距不是很大,那么图像就会模糊了,极端一点的情况,当一张图片所有的像素之间颜色都差不多时,那么这张图片也就是一个纯色的图片了。模糊操作就是让像素间的颜色差距变小,比如A点是红色,A点周围的点是绿色,模糊就像用一把刷子,将A点和周围的点的颜色混合起来,变成最终的颜色。而怎样混合,按照不同的权值进行混合,就可以达到不同的效果了。比如均值模糊,以及著名的高斯模糊。
影响模糊程度的重要因素是模糊半径,模糊半径越大,模糊程度越大,模糊半径越小,模糊程度越小。那么,模糊半径是什么?所谓模糊半径,也就是我们采样的一个范围,比如我们模糊的半径很小,只是把像素和它周围的一圈定点混合,那么模糊的程度就很小,而如果我们加大模糊半径,极端情况是每个顶点都取了周围所有点,也就是整张图的像素平均值,那么这张图的颜色就会偏向一种颜色。
 

二.均值模糊

 
最简单的,我们看一下简单的模糊,直接用周围像素求和平均,我们混合的最终图像,在某一点的权重仅仅跟模糊半径有关。换句话说,比如模糊半径为1,那么,我们取一个像素点,以及他周围的一圈像素点,一共九个点,直接取平均,那么每个点的权重设置为1/9。这也就是所谓的均值模糊。我们看一下均值模糊的例子。
shader部分:
  1. Shader "Custom/SimpleBlurEffect"
  2. {
  3. Properties
  4. {
  5. _MainTex("Base (RGB)", 2D) = "white" {}
  6. }
  7. //通过CGINCLUDE我们可以预定义一些下面在Pass中用到的struct以及函数,
  8. //这样在pass中只需要设置渲染状态以及调用函数,shader更加简洁明了
  9. CGINCLUDE
  10. //cg文件,包含了unity内置的一些cg函数
  11. #include "UnityCG.cginc"
  12. //blur结构体,从blur的vert函数传递到frag函数的参数
  13. struct v2f_blur
  14. {
  15. float4 pos : SV_POSITION; //顶点位置
  16. float2 uv  : TEXCOORD0;   //纹理坐标
  17. float2 uv1 : TEXCOORD1;  //周围纹理1
  18. float2 uv2 : TEXCOORD2;  //周围纹理2
  19. float2 uv3 : TEXCOORD3;  //周围纹理3
  20. float2 uv4 : TEXCOORD4;  //周围纹理4
  21. };
  22. //用到的变量
  23. sampler2D _MainTex;
  24. //XX_TexelSize,XX纹理的像素相关大小width,height对应纹理的分辨率,x = 1/width, y = 1/height, z = width, w = height
  25. float4 _MainTex_TexelSize;
  26. //模糊半径
  27. float _BlurRadius;
  28. //vertex shader
  29. v2f_blur vert_blur(appdata_img v)
  30. {
  31. v2f_blur o;
  32. o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
  33. o.uv = v.texcoord.xy;
  34. //计算uv上下左右四个点对于blur半径下的uv坐标
  35. o.uv1 = v.texcoord.xy + _BlurRadius * _MainTex_TexelSize * float2( 1,  1);
  36. o.uv2 = v.texcoord.xy + _BlurRadius * _MainTex_TexelSize * float2(-1,  1);
  37. o.uv3 = v.texcoord.xy + _BlurRadius * _MainTex_TexelSize * float2(-1, -1);
  38. o.uv4 = v.texcoord.xy + _BlurRadius * _MainTex_TexelSize * float2( 1, -1);
  39. return o;
  40. }
  41. //fragment shader
  42. fixed4 frag_blur(v2f_blur i) : SV_Target
  43. {
  44. fixed4 color = fixed4(0,0,0,0);
  45. color += tex2D(_MainTex, i.uv );
  46. color += tex2D(_MainTex, i.uv1);
  47. color += tex2D(_MainTex, i.uv2);
  48. color += tex2D(_MainTex, i.uv3);
  49. color += tex2D(_MainTex, i.uv4);
  50. //相加取平均,据说shader中乘法比较快
  51. return color * 0.2;
  52. }
  53. ENDCG
  54. //子着色器
  55. SubShader
  56. {
  57. //pass 0: blur effect
  58. Pass
  59. {
  60. ZTest Always
  61. Cull Off
  62. ZWrite Off
  63. Fog{ Mode Off }
  64. //直接调用vert_blur和frag_blur
  65. CGPROGRAM
  66. #pragma vertex vert_blur
  67. #pragma fragment frag_blur
  68. ENDCG
  69. }
  70. }
  71. }
C#脚本部分:
  1. using UnityEngine;
  2. using System.Collections;
  3. //编辑状态下也运行
  4. [ExecuteInEditMode]
  5. //继承自PostEffectBase
  6. public class SimpleBlurEffect : PostEffectBase
  7. {
  8. //模糊半径
  9. public float BlurRadius = 1.0f;
  10. void OnRenderImage(RenderTexture source, RenderTexture destination)
  11. {
  12. if (_Material)
  13. {
  14. //blur
  15. _Material.SetFloat("_BlurRadius", BlurRadius);
  16. Graphics.Blit(source, destination, _Material);
  17. }
  18. }
  19. }

注意,此处的PostEffectBase为各种后处理效果的基类,在上一篇文章:Unity Shader-后处理:简单的颜色调整(亮度,饱和度,对比度)中有该类的完整实现,此处不予贴出代码。

 
效果如下图所示:
 
原图效果
 
blurRadius = 1
 
blurRadius = 5
 
从上面的模糊效果我们看到,模糊半径越大,模糊的效果越明显。但是!这种效果看起来一点都不舒服,有种近视的赶脚,完全不是平滑的模糊效果,就更不要说进一步的毛玻璃之类的全模糊效果了。

三.均值模糊的改进

 
既然,一次模糊我们感觉效果不是很尽人意,那么,我们可以尝试迭代模糊,也就是用上一次模糊的输出作为下一次模糊的输入,迭代之后的模糊效果更加明显。先看一下代码,这次,我们的shader代码和上面的一样,没有变动,仅仅是修改了脚本,增加了降分辨率和迭代的两个操作。
  1. using UnityEngine;
  2. using System.Collections;
  3. //编辑状态下也运行
  4. [ExecuteInEditMode]
  5. //继承自PostEffectBase
  6. public class SimpleBlurEffect : PostEffectBase
  7. {
  8. //模糊半径
  9. public float BlurRadius = 1.0f;
  10. //降分辨率
  11. public int downSample = 2;
  12. //迭代次数
  13. public int iteration = 3;
  14. void OnRenderImage(RenderTexture source, RenderTexture destination)
  15. {
  16. if (_Material)
  17. {
  18. //申请RenderTexture,RT的分辨率按照downSample降低
  19. RenderTexture rt1 = RenderTexture.GetTemporary(source.width >> downSample, source.height >> downSample, 0, source.format);
  20. RenderTexture rt2 = RenderTexture.GetTemporary(source.width >> downSample, source.height >> downSample, 0, source.format);
  21. //直接将原图拷贝到降分辨率的RT上
  22. Graphics.Blit(source, rt1);
  23. //进行迭代,一次迭代进行了两次模糊操作,使用两张RT交叉处理
  24. for(int i = 0; i < iteration; i++)
  25. {
  26. //用降过分辨率的RT进行模糊处理
  27. _Material.SetFloat("_BlurRadius", BlurRadius);
  28. Graphics.Blit(rt1, rt2, _Material);
  29. Graphics.Blit(rt2, rt1, _Material);
  30. }
  31. //将结果拷贝到目标RT
  32. Graphics.Blit(rt1, destination);
  33. //释放申请的两块RenderBuffer内容
  34. RenderTexture.ReleaseTemporary(rt1);
  35. RenderTexture.ReleaseTemporary(rt2);
  36. }
  37. }
  38. }
结果如下:
 
blurRadius = 1, downSample = 2,  iteration = 3 
 
blurRadius = 1, downSample = 2, iteration = 5
 
我们看到,通过迭代以及降低分辨率,我们的模糊效果更加明显了,当迭代次数较大时,会有一种毛玻璃的效果。这里,虽然迭代次数增加了,会耗费更多的性能,但是相应地,我们也降低了分辨率,也减少了采样等计算操作的消耗。
 

四.RenderTexture介绍

 

这里,我们通过多次处理,包括降分辨率以及迭代,完成了模糊操作,这里我们需要临时存储上一次处理过的中间输出,所以就需要用渲染中常用的一个概念RenderTexture。

 
关于RenderTexture,简要介绍一下,我们在渲染场景时,一般都是直接输出到帧缓存,然后输出到屏幕上,然而有的时候,我们并不想直接输出结果,而是需要对这个渲染的结果进行处理,所以,我们就将渲染的结果输出到了一张纹理上,也就是RenderTexture,这也是所有后处理的基础。Unity的RenderTexture还是很好用的,我们不仅仅可以在后处理时使用,还可以通过把摄像机的输出设置到某个RT上,然后用这张RT作为一些类似镜子的物体上,就可以实现镜面效果或者屏幕效果。
 
不过RenderTexture还是很耗费资源的,一张大的RenderTexture是屏幕分辨率大小的一张图片,而且是完全不能够压缩的,所以当后处理中RenderTexture用得多时,内存消耗很大,在手机,尤其是大屏手机,内存比较小的情况下,后处理叠加时很可能会由于内存耗尽而崩溃。所以,我们在使用RenderTexture时需要慎重考虑。如果效果可以接受,我们就可以考虑降低RenderTexture的分辨率,这样,输出的画面效果可能会打一些折扣,但是性能会有很大的提高。而我们的模糊效果,本身降低分辨率就会导致画面比较模糊,所以在这里,我们完全可以放心大胆地降低RT的分辨率,既可以提升效果,又可以大大地减少开销。
 
这里还有一点,由于OnRenderImage函数每帧在渲染之前都会调用,之前曾经担心会不会每一帧在这里申请RT,然后释放,会不会有很高的GC?经过查找了一些资料,发现Unity这里是进行过处理的,RenderTexture是之前申请好的一块内存区域,我们可以直接使用,而不需要考虑GC的问题,正如这两个函数的名字一样,RenderTexture.GetTemporary和RenderTexture.ReleaseTemporary一样。并且,本人亲测,使用Profile挂了一下这个脚本,发现的确没有GC:
 

 

五.总结

 
本篇文章介绍了模糊的原理以及RenderTexture的概念。给出了一版简单的均值模糊效果,并通过降低分辨率增加迭代次数优化了模糊的表现效果。但是这种模糊效果还是不尽人意,不进行迭代时效果很差,迭代次数大时虽然可以得到一些好的效果,但是相应地,性能耗费也是极大的。所以是时候学习一下更先进的模糊效果了,下一篇就是传说中的《高斯模糊》~哇咔咔

Unity Shader-后处理:简单均值模糊的更多相关文章

  1. Unity Shader后处理-搜索灰度效果

    如U3D中Hierarchy面板下的搜索效果: 讲解分析: 1.这种PostEffect效果其实就是指Unity shader的后处理,即游戏中实现屏幕特效的常见方法.顾名思义屏幕后处理就是指在渲染完 ...

  2. Unity Shader实现描边效果

    http://gad.qq.com/article/detail/28346 描边效果是游戏里面非常常用的一种效果,一般是为了凸显游戏中的某个对象,会给对象增加一个描边效果.本篇文章和大家介绍下利用S ...

  3. Unity shader学习之屏幕后期处理效果之均值模糊

    均值模糊,也使用卷积来实现,之不过卷积中每个值均相等,且相加等于1. 代码如下, 子类: using UnityEngine; public class MeanBlurRenderer : Post ...

  4. Unity Shader入门精要学习笔记 - 第12章 屏幕后处理效果

    建立一个基本的屏幕后处理脚本系统 屏幕后处理,顾名思义,通常指的是在渲染完整个场景得到屏幕图像后,再对这个图像进行一系列操作,实现各种屏幕特效.使用这种技术,可以为游戏画面添加更多艺术效果,例如景深. ...

  5. 【我的书】Unity Shader的书 — 文件夹(2015.12.21更新)

    写在前面 感谢全部点进来看的朋友.没错.我眼下打算写一本关于Unity Shader的书. 出书的目的有以下几个: 总结我接触Unity Shader以来的历程,给其它人一个借鉴.我非常明确学Shad ...

  6. 【我的书】Unity Shader的书 — 目录(2016.5.19最后一次更新)

    写在前面 感谢所有点进来看的朋友.没错,我目前打算写一本关于Unity Shader的书. 出书的目的有下面几个: 总结我接触Unity Shader以来的历程,给其他人一个借鉴.我非常明白学Shad ...

  7. 【转】《Unity Shader入门精要》冯乐乐著 书中彩图

    为方便个人手机学习时候查阅,从网上转来这些彩图. 如属过当行为,联系本人删除. 勘错表 http://candycat1992.github.io/unity_shaders_book/unity_s ...

  8. Unity Shader入门精要学习笔记 - 第13章 使用深度和法线纹理

    线纹理的代码非常简单,但是我们有必要在这之前首先了解它们背后的实现原理. 深度纹理实际上就是一张渲染纹理,只不过它里面存储的像素值不是颜色值而是一个高精度的深度值.由于被存储在一张纹理中,深度纹理里的 ...

  9. Unity Shader入门精要学习笔记 - 第10章 高级纹理

    转载自 冯乐乐的 <Unity Shader入门精要> 立方体纹理 在图形学中,立方体纹理是环境映射的一种实现方法.环境映射可以模拟物体周围的环境,而使用了环境映射的物体可以看起来像镀了层 ...

随机推荐

  1. Lodash JavaScript 实用工具库

    地址:https://www.lodashjs.com/ Lodash 是一个一致性.模块化.高性能的 JavaScript 实用工具库.

  2. ERP退货系统管理(四十五)

    添加的存储过程:(添加退货申请信息的存储过程) CREATE PROCEDURE [dbo].[BioBackSendGoods_ADD] @SendBackID INT OUTPUT, ), @Ap ...

  3. [转] nginx配置HTTPS

    使用ssl模块配置同时支持http和https并存 一,生成证书 # 1.首先,进入你想创建证书和私钥的目录,例如: cd /etc/nginx/ # 2.创建服务器私钥,命令会让你输入一个口令: o ...

  4. day8--socketserver回顾

    sockeserver主要实现多并发的情况,我们知道,socket只能一对一用户进行交互,如何实现一对多交互,socketserver就是用来解决这个问题的. socketserver--共有这么几种 ...

  5. 【Java】 剑指offer(68) 树中两个结点的最低公共祖先

      本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 输入两个树结点,求它们的最低公共祖先. 思路 该题首先要和面试 ...

  6. 063 日志分析(pv  uv  登录人数  游客人数  平均访问时间  二跳率  独立IP)

    1.需求分析 分析指标 pv uv 登录人数 游客人数 平均访问时间 二跳率 独立IP 2.使用的日志(一号店),会话信息 3.创建数据库 4.创建源表,存储源数据 5.创建我们需要的use表 6.创 ...

  7. ubuntu16系统中pycharm下使用git将代码提交到github仓库

    1 在系统中安装git,在terminal中输入以下命令 sudo apt-get update sudo apt-get install git 2 对git进行配置,在terminal中输入以下命 ...

  8. 浮点数在计算机中的表示(IEEE浮点数标准)

    转载自:https://wdxtub.com/2016/04/16/thin-csapp-1/

  9. laravel5 项目上线后务必将开发环境更改为生产环境

    如果以开发环境上线,出错信息将全通过json暴露出来了,屏蔽方式如下: .env 文件设置如下APP_ENV=productionAPP_DEBUG=false 改完设置后把缓存清理一遍 如果更改后清 ...

  10. kali上部署dvwa漏洞测试平台

    kali上部署dvwa漏洞测试平台 一.获取dvwa安装包并解压 二.赋予dvwa文件夹相应权限 三.配置Mysql数据库 四.启动apache2和mysql服务 五.在网页配置dvwa 六.登陆到D ...