• Unity Shader-后处理:Bloom全屏泛光


    一.简介

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

  • 相关阅读:
    全美在线上云 保证上千考场统一监考
    如何构建一个较为通用的业务技术架构
    在tomcat下context.xml中配置各种数据库连接池
    Java中的多线程
    彻底理解ThreadLocal
    plsql工具使用
    软件清单
    EL表达式
    AOP(execution表达式)
    JSTL标签库之核心标签
  • 原文地址:https://www.cnblogs.com/lancidie/p/8665261.html
Copyright © 2020-2023  润新知