一.简介

 
今天来学习一下后处理中比较常用的一种效果,屏幕模糊效果。模糊效果,在图像处理中经常用到,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. jquery幻灯片插件之owl.carousel.js

    官网地址:http://owlcarousel2.github.io/OwlCarousel2/ 这个插件兼容各种浏览器,以及移动端 使用方法: 1.下载文件,解压以后,把dist里面的文件放到项目中 ...

  2. mybatis学习笔记--常见的错误

    原文来自:<mybatis学习笔记--常见的错误> 昨天刚学了下mybatis,用的是3.2.2的版本,在使用过程中遇到了些小问题,现总结如下,会不断更新. 1.没有在configurat ...

  3. SPLAY,LCT学习笔记(五)

    这一篇重点探讨LCT的应用 例:bzoj 2631 tree2(国家集训队) LCT模板操作之一,利用SPLAY可以进行区间操作这一性质对维护懒惰标记,注意标记下传顺序和如何下传 #include & ...

  4. C#中的预处理指令详解

    这篇文章主要介绍了C#中的预处理指令详解,本文讲解了#define 和 #undef.#if.#elif.#else和#endif.#warning和#error.#region和#endregion ...

  5. bootstrap之表格和按钮

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. ERP产品销售发货--发货管理(四十一)

    发货详细信息的业务实体视图: CREATE VIEW [dbo].[View_BioSendAppInfo] AS SELECT SendId, BillNo, Subject, DepartMent ...

  7. Ubuntu 里面 apt-get 三个有关更新的命令的区别

    apt-get update 更新软件源中的所有软件列表. apt-get upgrade 更新软件. apt-get dist-upgrade 更新系统版本. 作者:耑新新,发布于  博客园 转载请 ...

  8. Linux salt

    引用自:https://blog.csdn.net/langsim/article/details/43939295 Saltstack介绍 Saltstack是一个新的基础设施管理工具.目前处于快速 ...

  9. c# windows服务如何获取自己的安装路径

    public static string GetWindowsServiceInstallPath(string ServiceName)        {            string key ...

  10. Unity 中几点注意的地方

    1.面板摆放的规范 2.project工程面板中 文件夹命名的规范,不同类型的东西要分类摆放,例如Script 3.不要留空函数体(系统自动生成Start, Uadate等),空函数体一样会执行,尤其 ...