一.简介

 
今天来学习一下全屏Bloom效果,有时候也叫Glow效果,中文一般叫做“全屏泛光”,这是一种可以模拟出HDR的全屏后处理效果,但是实现原理与HDR相差很远,效果比HDR差一些,但是比HDR的性能要节省很多。这篇文章里我们只是实现了一版基于全屏颜色遮罩的Bloom处理,具体针对某个对象进行Bloom的效果在以后的文章中会进行讲解。
 

二.原理介绍

 
这里不得不提一下传说中的HDR,从接触引擎开始,就一群大牛们经常讨论到这个词,然而作为一个新手,一直对这个传说中的技术抱着“敬畏”的态度,不过如今逐渐熟悉了一些渲染相关的东西,贱贱地,也没有那么害怕这个技术了。今天就来学习一下HDR,不过HDR不是这篇文章的主角,所以只是简要介绍。
 

1.HDR

 
HDR(High Dynamic Range),翻译过来就是高动态范围。所谓High,指的是亮度的范围更高。我们知道,正常屏幕上一个像素是由RGB三原色组成的,每个通道用八位二进制表示,也就是0-255,转化为16进制,就是白色为0xFFFFFF,黑色为0x000000。而真实世界的亮度的最大值远远比屏幕能够显示的亮度大,比如太阳的亮度会是我们屏幕亮度的几万倍,而我们的虽然不能识别这样广范围的亮度,但是屏幕上的亮度范围是远远不能表达真实世界的亮度分布的。假设我们真实世界的亮度是0-10000,那么这个就是我们所谓的High,比我们屏幕上的亮度范围要高。如果不使用HDR,就会出现场景中大块的亮或者暗,造成场景对比度不明显,影响画面效果。
 
要怎样用有限的亮度分布模拟更高范围的亮度分布,就是HDR在做的事情。实现这样的功能的技术叫做ToneMapping,据不官方翻译,叫做色调映射技术。这种技术会让画面对比度更加柔和,将高的亮度范围更加平滑地缩放到0-255这一低光照范围,主要运用的原理是局部适应性。我们人眼,在比较暗的地方,原来也能看清楚东西,但是如果突然到了一个比较亮的地方,我们会什么都看不清,需要矫正一会儿才能适应当前的亮度水平,之前玩过一小阵子CryEngine3,默认是开启这种效果的,感觉还是屌屌的。关于ToneMapping技术的原理,可以参考这篇文章这篇文章,还有这篇屌炸天的论文,这里就不过多介绍了。
 

2.Bloom

 
这篇文章的主题并不是HDR,而是Bloom。Bloom可以模拟出HDR的效果,但是原理上和HDR相差甚远。HDR实际上是通过映射技术,来达到整体调整全局亮度属性的,这种调整是颜色,强度等都可以进行调整,而Bloom仅仅是能够将光照范围调高达到过饱和,也就是让亮的地方更亮。不过Bloom效果实现起来简单,性能消耗也小,却可以达到不错的效果。关于HDR和Bloom之间的差别,可以参考这篇文章
 
这里介绍一下Bloom的实现原理,其实比较简单,首先我们需要设置一个我们要泛光的亮度阈值,第一遍处理时,我们需要对原场景图进行筛选,所有小于这个阈值的像素都被筛掉,所有大于该值的像素留下来,这样,我们就得到了一张只包含需要泛光部分的贴图,其余部分是黑色的;泛光效果是由衍射效果产生的,我们现实世界中看到的泛光效果,最亮的地方实际上是会向暗的地方扩散的,也就是说在亮的地方,边界是不明显的,所以我们就需要对泛光是部分,也就是我们上一步操作的结果图片进行模糊操作,达到光溢出的效果,最后,我们将处理过的图像和原图像进行叠加,就得到了最终的效果。老外有篇文章写得比较好,这里我把这张图借来用用:
 
 
在DX上直接实现Bloom的可以参照这篇文章
 

三.代码实现

 
c#部分:
  1. using UnityEngine;
  2. using System.Collections;
  3. [ExecuteInEditMode]
  4. public class BloomEffect : PostEffectBase
  5. {
  6. //分辨率
  7. public int downSample = 1;
  8. //采样率
  9. public int samplerScale = 1;
  10. //高亮部分提取阈值
  11. public Color colorThreshold = Color.gray;
  12. //Bloom泛光颜色
  13. public Color bloomColor = Color.white;
  14. //Bloom权值
  15. [Range(0.0f, 1.0f)]
  16. public float bloomFactor = 0.5f;
  17. void OnRenderImage(RenderTexture source, RenderTexture destination)
  18. {
  19. if (_Material)
  20. {
  21. //申请两块RT,并且分辨率按照downSameple降低
  22. RenderTexture temp1 = RenderTexture.GetTemporary(source.width >> downSample, source.height >> downSample, 0, source.format);
  23. RenderTexture temp2 = RenderTexture.GetTemporary(source.width >> downSample, source.height >> downSample, 0, source.format);
  24. //直接将场景图拷贝到低分辨率的RT上达到降分辨率的效果
  25. Graphics.Blit(source, temp1);
  26. //根据阈值提取高亮部分,使用pass0进行高亮提取
  27. _Material.SetVector("_colorThreshold", colorThreshold);
  28. Graphics.Blit(temp1, temp2, _Material, 0);
  29. //高斯模糊,两次模糊,横向纵向,使用pass1进行高斯模糊
  30. _Material.SetVector("_offsets", new Vector4(0, samplerScale, 0, 0));
  31. Graphics.Blit(temp2, temp1, _Material, 1);
  32. _Material.SetVector("_offsets", new Vector4(samplerScale, 0, 0, 0));
  33. Graphics.Blit(temp1, temp2, _Material, 1);
  34. //Bloom,将模糊后的图作为Material的Blur图参数
  35. _Material.SetTexture("_BlurTex", temp2);
  36. _Material.SetVector("_bloomColor", bloomColor);
  37. _Material.SetFloat("_bloomFactor", bloomFactor);
  38. //使用pass2进行景深效果计算,清晰场景图直接从source输入到shader的_MainTex中
  39. Graphics.Blit(source, destination, _Material, 2);
  40. //释放申请的RT
  41. RenderTexture.ReleaseTemporary(temp1);
  42. RenderTexture.ReleaseTemporary(temp2);
  43. }
  44. }
  45. }
shader部分:
  1. Shader "Custom/BloomEffect" {
  2. Properties{
  3. _MainTex("Base (RGB)", 2D) = "white" {}
  4. _BlurTex("Blur", 2D) = "white"{}
  5. }
  6. CGINCLUDE
  7. #include "UnityCG.cginc"
  8. //用于阈值提取高亮部分
  9. struct v2f_threshold
  10. {
  11. float4 pos : SV_POSITION;
  12. float2 uv : TEXCOORD0;
  13. };
  14. //用于blur
  15. struct v2f_blur
  16. {
  17. float4 pos : SV_POSITION;
  18. float2 uv  : TEXCOORD0;
  19. float4 uv01 : TEXCOORD1;
  20. float4 uv23 : TEXCOORD2;
  21. float4 uv45 : TEXCOORD3;
  22. };
  23. //用于bloom
  24. struct v2f_bloom
  25. {
  26. float4 pos : SV_POSITION;
  27. float2 uv  : TEXCOORD0;
  28. float2 uv1 : TEXCOORD1;
  29. };
  30. sampler2D _MainTex;
  31. float4 _MainTex_TexelSize;
  32. sampler2D _BlurTex;
  33. float4 _BlurTex_TexelSize;
  34. float4 _offsets;
  35. float4 _colorThreshold;
  36. float4 _bloomColor;
  37. float _bloomFactor;
  38. //高亮部分提取shader
  39. v2f_threshold vert_threshold(appdata_img v)
  40. {
  41. v2f_threshold o;
  42. o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
  43. o.uv = v.texcoord.xy;
  44. //dx中纹理从左上角为初始坐标,需要反向
  45. #if UNITY_UV_STARTS_AT_TOP
  46. if (_MainTex_TexelSize.y < 0)
  47. o.uv.y = 1 - o.uv.y;
  48. #endif
  49. return o;
  50. }
  51. fixed4 frag_threshold(v2f_threshold i) : SV_Target
  52. {
  53. fixed4 color = tex2D(_MainTex, i.uv);
  54. //仅当color大于设置的阈值的时候才输出
  55. return saturate(color - _colorThreshold);
  56. }
  57. //高斯模糊 vert shader(上一篇文章有详细注释)
  58. v2f_blur vert_blur(appdata_img v)
  59. {
  60. v2f_blur o;
  61. _offsets *= _MainTex_TexelSize.xyxy;
  62. o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
  63. o.uv = v.texcoord.xy;
  64. o.uv01 = v.texcoord.xyxy + _offsets.xyxy * float4(1, 1, -1, -1);
  65. o.uv23 = v.texcoord.xyxy + _offsets.xyxy * float4(1, 1, -1, -1) * 2.0;
  66. o.uv45 = v.texcoord.xyxy + _offsets.xyxy * float4(1, 1, -1, -1) * 3.0;
  67. return o;
  68. }
  69. //高斯模糊 pixel shader(上一篇文章有详细注释)
  70. fixed4 frag_blur(v2f_blur i) : SV_Target
  71. {
  72. fixed4 color = fixed4(0,0,0,0);
  73. color += 0.40 * tex2D(_MainTex, i.uv);
  74. color += 0.15 * tex2D(_MainTex, i.uv01.xy);
  75. color += 0.15 * tex2D(_MainTex, i.uv01.zw);
  76. color += 0.10 * tex2D(_MainTex, i.uv23.xy);
  77. color += 0.10 * tex2D(_MainTex, i.uv23.zw);
  78. color += 0.05 * tex2D(_MainTex, i.uv45.xy);
  79. color += 0.05 * tex2D(_MainTex, i.uv45.zw);
  80. return color;
  81. }
  82. //Bloom效果 vertex shader
  83. v2f_bloom vert_bloom(appdata_img v)
  84. {
  85. v2f_bloom o;
  86. //mvp矩阵变换
  87. o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
  88. //uv坐标传递
  89. o.uv.xy = v.texcoord.xy;
  90. o.uv1.xy = o.uv.xy;
  91. #if UNITY_UV_STARTS_AT_TOP
  92. if (_MainTex_TexelSize.y < 0)
  93. o.uv.y = 1 - o.uv.y;
  94. #endif
  95. return o;
  96. }
  97. fixed4 frag_bloom(v2f_bloom i) : SV_Target
  98. {
  99. //取原始清晰图片进行uv采样
  100. fixed4 ori = tex2D(_MainTex, i.uv1);
  101. //取模糊普片进行uv采样
  102. fixed4 blur = tex2D(_BlurTex, i.uv);
  103. //输出= 原始图像,叠加bloom权值*bloom颜色*泛光颜色
  104. fixed4 final = ori + _bloomFactor * blur * _bloomColor;
  105. return final;
  106. }
  107. ENDCG
  108. SubShader
  109. {
  110. //pass 0: 提取高亮部分
  111. Pass
  112. {
  113. ZTest Off
  114. Cull Off
  115. ZWrite Off
  116. Fog{ Mode Off }
  117. CGPROGRAM
  118. #pragma vertex vert_threshold
  119. #pragma fragment frag_threshold
  120. ENDCG
  121. }
  122. //pass 1: 高斯模糊
  123. Pass
  124. {
  125. ZTest Off
  126. Cull Off
  127. ZWrite Off
  128. Fog{ Mode Off }
  129. CGPROGRAM
  130. #pragma vertex vert_blur
  131. #pragma fragment frag_blur
  132. ENDCG
  133. }
  134. //pass 2: Bloom效果
  135. Pass
  136. {
  137. ZTest Off
  138. Cull Off
  139. ZWrite Off
  140. Fog{ Mode Off }
  141. CGPROGRAM
  142. #pragma vertex vert_bloom
  143. #pragma fragment frag_bloom
  144. ENDCG
  145. }
  146. }
  147. }

注:PostEffectBase类是后处理类的基类,在前面的文章已有详细介绍,这里不再贴出。

 

四.效果展示

 
原始图片效果:
开启Bloom效果,过滤阈值设为灰色(128,128,128),泛光颜色为白色(256,256,256),泛光权重为1:
开启Bloom效果,过滤阈值设为灰色(0,0,0),泛光颜色为白色(256,256,256),泛光权重为1,一种过度曝光的效果:
开启Bloom效果,过滤阈值设为灰色(0,0,0),泛光颜色为绿色(0,256,0),泛光权重为1,夜视仪效果:
 
 

本篇文章介绍的主要是全屏泛光的实现方式,也就是泛光的部分只是通过一个全屏的颜色阈值来进行设定,超过这一阈值的颜色就进行泛光操作,否则不会泛光。这样做效率较高,但是没办法控制单独的物体进行泛光。网上也有这种针对单独对象的bloom操作,可以实现更加细致的Bloom效果,这篇文章可以进行参考。

Unity Shader-后处理:Bloom全屏泛光的更多相关文章

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

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

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

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

  3. Unity Shader 屏幕后效果——Bloom外发光

    Bloom的原理很简单,主要是提取渲染图像中的亮部区域,并对亮部区域进行模糊处理,再与原始图像混合而成. 一般对亮部进行模糊处理的部分采用高斯模糊,关于高斯模糊,详见之前的另一篇博客: https:/ ...

  4. Unity shader学习之屏幕后期处理效果之Bloom效果

    Bloom特效是游戏中常见的一种屏幕效果.这种特效可以模拟真实摄像机的一种图像效果,它让画面中较亮的区域“扩散”到周围的区域中,造成一种朦胧的效果. Bloom的实现原理很简单,首先根据一个阈值提取出 ...

  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 入门精要学习 (冯乐乐 著)

    第1篇 基础篇 第1章 欢迎来到Shader的世界 第2章 渲染流水线 第3章 Unity Shader 基础 第4章 学习Shader所需的数学基础 第2篇 初级篇 第5章 开始Unity Shad ...

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

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

  9. Unity Shader 知识点总结(二)

    紧接着上一篇文章的shader入门知识的总结,本文主要总结shader中的纹理贴图.透明度混合.顶点动画.后期特效处理等操作.如果有什么地方有错,请指出更正,谢谢.本文的代码主要来自开源书:unity ...

  10. 小强学渲染之Unity Shader噪声应用

    之前玩Tencent的仙剑4手游时,杀死boss会看到boss有“消融”的效果,就是身体上有多个洞洞然后往四周扩散直至尸体完全消失,但效果是没有关闭背面剔除的“穿帮”效果,可能也是考虑性能因素. em ...

随机推荐

  1. compile php with openssl on mac osx error 填坑

    从源码手动编译 PHP 时出现如下错误:           Default   1 2 3 4 5 6 7 8 9 10 11 12 13 14 Undefined symbols for arch ...

  2. STM32F412应用开发笔记之七:片上ADC的应用测试

    在我们的应用项目中需要采集一些模拟量,这些量使用MCU自带的ADC就可以满足要求.在NUCLEO-F412ZG实验板上的STM32F412ZG有一个16通道的ADC,我们试验用它采集几个数据. 在NU ...

  3. python之__dict__与dir(转载)

    Python下一切皆对象,每个对象都有多个属性(attribute),Python对属性有一套统一的管理方案. __dict__与dir()的区别: dir()是一个函数,返回的是list: __di ...

  4. linux 搜索文件

    https://blog.csdn.net/json_ligege/article/details/72865645 1.find   find是最常见和最强大的查找命令,你可以用它找到任何你想找的文 ...

  5. poj2823 单调队列初步

    什么是单调队列:头元素一直是队列当中的最大值,队列中的值按照递减顺序排列,可以从末尾插入一个元素,或从两段删除元素 1.插入元素,为了保证队列的单调性(这里假设为递减性),在插入元素v时要将对位的元素 ...

  6. hdu3642扫描线 长方体

    立方体交,自己写的莫名其妙MLE了,不知道为什么 #include<iostream> #include<cstring> #include<cstdio> #in ...

  7. Math对象的常用属性和方法

    属性 描述 Math.PI 返回π(3.1415926) 方法 描述 Math.round() 将数字四舍五入到离它最近的整数 Math.sart(n) 返回平方根,例如Math.sart(9)返回3 ...

  8. Ubuntu 16.04 LTS 搭建ftp服务器

    其实我之前搭建好了,但是最近我上来看好像跟没搭建一样呢,于是我从新搭建一遍? 我的ubuntu版本: cat /etc/issue Ubuntu 16.04 LTS \n \l 1.安装vsftpd( ...

  9. [HEOI2016/TJOI2016]序列

    题解: 很水的题目 首先容易发现每个位置实际上只有最大值是有用的 然后把条件变成dp[i]=max(dp[j]+1)(j<i,F[i]>G[j],G[i]>H[j]) 然后我研究了一 ...

  10. 【深入spring】IoC容器的实现

    本文乃学习整理参考而来 IoC概述: 在spring中,IoC容器实现了依赖控制反转,它可以再对象生成或初始化时直接将数据注入到对象中,也可以通过将对象引用注入到对象数据域中的方式来注入方法调用的依赖 ...