HDR及其应用(tone mapping/bloom

作者: 万里_aa3f | 来源:发表于2019-01-18 15:36 被阅读9次

    HDR及其应用(tone mapping、bloom)

    1.什么是HDR,作用是什么
    2.HDR的有关应用及其原理
    3.tone mapping应用ACES
    4.bloom算法
    5.unity 脚本+shader

    1.什么是HDR

    首先要了解一张32位4通道 bmp格式的图片,它的RGBA每个通道分别占有8位,每个通道每种颜色能表示的深度信息位2的8次方也就是256种颜色;
    细分后只能表现出256:1的差别,然而在自然中太阳光下的对比度是5000:1;用256的区分度来描述5000的区分度,显然是吃力的。这时候,为了记录更多的光照信息,我们需要能表现出更大范围的对比的图像HDR(High Dynamic Range)。普通的范围就叫LDR(Low Dynamic Range)。

    2.HDR的相关应用

    在引擎的后期处理中相关应用一般有:automatic exposure control + Tonemapping + Bloom, 先根据场景的一帧计算出平均亮度,如果偏暗就加亮一些,反之亦然,调整好亮度之后再调整灰度,让明部跟暗部保持更多的细节,最后对高光部分做个Bloom,看起来更真实。

    所谓tone mapping就是根据场景的当前亮度,将HDR映射到LDR上,并保证图像细节不丢失,不失真。
    bloom效果将HDR中>1的像素部分通过高斯模糊,叠加到原图片上。来表明这个地方非常亮,亮度都溢出了!
    原来图片效果

    亮部没有细节亮成了一坨


    tone mapping

    亮部细节出现


    tone mapping+bloom

    +bloom表现亮到溢出的效果


    3.tone mapping ACES算法

                float3 ACESToneMapping(float3 color, float adapted_lum)
                {
                    const float A = 2.51f;
                    const float B = 0.03f;
                    const float C = 2.43f;
                    const float D = 0.59f;
                    const float E = 0.14f;
                    color *= adapted_lum;
                    return (color * (A * color + B)) / (color * (C * color + D) + E);
                }
    

    4.Bloom算法

    网上资料很多,这里就简答介绍下:
    算法:卷积配高斯核
    原理:原理是把图像的亮的部分通过卷积模糊再叠加到原图像上,产生了bloom效果。高斯核目的:当前像素和周围的像素按一定权重混合,产生一定模糊效果权重分布如下,离当前像素越远,权重越低。
    高斯公式:



    事实上,我们不用在片元上计算,直接用求出来的核就好了


    5.Unity脚本+shader

    实现思路(高斯模糊参考入门精要):
    脚本:重点在OnRenderImage()中;
    1.先将超过阈值的亮度信息提取出来,进行高斯模糊;使用shader 中的Pass0
    2.高斯模糊:申请两个buffer,用RenderTexture分别进行横向及纵向模糊; 使用shader中的Pass1,2
    3.与tone mapping转换过的图片相叠加,Pass3

    脚本源码
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    [ExecuteInEditMode]
    [RequireComponent(typeof(Camera))]
    public class ToneMappingAndBloom : MonoBehaviour
    {
        public Shader curShader;
        private Material curMaterial;
        Material material{
            get{
                if(curMaterial==null){
                    curMaterial=new Material(curShader);
                    curMaterial.hideFlags=HideFlags.HideAndDontSave;
                }
                return curMaterial;
            }
        }
    
        //tone mapping
        [Range(0,3)]
        public float _lum=0.5f;
        public float _Contrast=1.1f;
    
        //bloom
        [Range(0,5)]
        public float _LumThreshold=1.0f;
        [Range(1,5)]
        public int _iter=2;
        [Range(0,5)]
        public float blurSpread=0.6f;
    
        void Start()
        {
            if(!SystemInfo.supportsImageEffects){
                enabled=false;
                return ;
            }
            if(!curShader||!curShader.isSupported){
                enabled=false;
            }
        }
    
        void OnRenderImage(RenderTexture src,RenderTexture dest){
            if(material!=null){
                material.SetFloat("_LumThreshold",_LumThreshold);                               //000
                RenderTexture buffer0=RenderTexture.GetTemporary(src.width,src.height,0);
                buffer0.filterMode=FilterMode.Bilinear;
                Graphics.Blit(src,buffer0,material,0);  //加上筛选      pass 0
    
                for(int i=0;i<_iter;i++){
                    material.SetFloat("_uvAdd",i+1*blurSpread);            // 111  222 
    
                    RenderTexture buffer1=RenderTexture.GetTemporary(src.width,src.height,0);
                    Graphics.Blit(buffer0,buffer1,material,1);                  //pass  1
                    RenderTexture.ReleaseTemporary(buffer0);        
                    buffer0=buffer1;
    
                    buffer1=RenderTexture.GetTemporary(src.width,src.height,0);
                    Graphics.Blit(buffer0,buffer1,material,2);          //pass 2
                    RenderTexture.ReleaseTemporary(buffer0);
                    buffer0=buffer1;
                }
    
    
                material.SetFloat("_Lum",_lum);                     //333
                material.SetFloat("_Contrast",_Contrast);           //333
                material.SetTexture("_BloomTex",buffer0);           //333
                Graphics.Blit(src,dest,material,3);                 //pass 3
    
                RenderTexture.ReleaseTemporary(buffer0);
            }
            else{
                Graphics.Blit(src,dest);
            }
        }
    
    
        void OnDisable(){
            if(curMaterial){
                DestroyImmediate(curMaterial);
            }
        }
    }
    
    
    shader源码
    
    Shader "Luzheng/ToneMappingAndBloom"
    {
        Properties
        {
            _MainTex ("Texture", 2D) = "white" {}
            //tone mapping
            _BloomTex("BloomTex",2D)="white"{}
            _Lum("Lum",float)=1
            _Contrast("Contrast",float)=1
            //bloom
            _uvAdd("uvAdd",float)=1
            //
            _LumThreshold("LumThreshold",float)=1.0
        }
        SubShader
        {
            // No culling or depth
            Cull Off ZWrite Off ZTest Always
    
            pass{
                CGPROGRAM
                #pragma vertex vert 
                #pragma fragment frag 
                #include"UnityCG.cginc"
    
                float _LumThreshold;
                sampler2D _MainTex;
    
                struct a2v{
                    float4 vertex:POSITION;
                    float2 uv:TEXCOORD0;
                };
                struct v2f{
                    float4 pos:SV_POSITION;
                    float2 uv:TEXCOORD0;
                };
    
                v2f vert(a2v v){
                    v2f o;
                    o.uv=v.uv;
                    o.pos=UnityObjectToClipPos(v.vertex);
                    return o;
                }
    
                fixed4 frag(v2f i):SV_TARGET{
                    float3 col=tex2D(_MainTex,i.uv).xyz;
                    fixed lum=Luminance(col);
                    fixed val=clamp(lum-_LumThreshold,0,1);
                    return fixed4(col*val,1.0);
                }
    
                ENDCG
            }
    
            pass{
                CGPROGRAM
                #pragma vertex vert 
                #pragma fragment frag 
    
                #include"UnityCG.cginc"
    
                struct a2v{
                    float4 vertex:POSITION;
                    float2 uv:TEXCOORD0;
                };
                struct v2f{
                    float4 pos:SV_POSITION;
                    float2 uv[5]:TEXCOORD0;
                };
    
                sampler2D _MainTex;
                float4 _MainTex_TexelSize;
                float _uvAdd;
    
                v2f vert(a2v v){
                    v2f o;
                    o.uv[0]=v.uv;
                    o.uv[1]=v.uv + float2(_MainTex_TexelSize.x , 0) * _uvAdd;
                    o.uv[2]=v.uv - float2(_MainTex_TexelSize.x , 0) * _uvAdd;
                    o.uv[3]=v.uv + float2( _MainTex_TexelSize.x * 2.0 , 0) * _uvAdd;
                    o.uv[4]=v.uv - float2(_MainTex_TexelSize.x * 2.0 , 0) * _uvAdd;
                    o.pos=UnityObjectToClipPos(v.vertex);
                    return o;
                }
                fixed4 frag(v2f i):SV_TARGET{
                    
                    fixed3 col=tex2D(_MainTex,i.uv[0]).xyz * 0.4026;
                    col+=tex2D(_MainTex  , i.uv[1]).xyz * 0.2442;
                    col+=tex2D(_MainTex , i.uv[2]).xyz * 0.2442;
                    col+=tex2D(_MainTex , i.uv[3]).xyz * 0.0545;
                    col+=tex2D(_MainTex , i.uv[4]).xyz * 0.0545;
    
                    return fixed4(col,1.0);
                }
                ENDCG
            }
    
            pass{
                CGPROGRAM
                #pragma vertex vert 
                #pragma fragment frag 
    
                #include"UnityCG.cginc"
    
                struct a2v{
                    float4 vertex:POSITION;
                    float2 uv:TEXCOORD0;
                };
                struct v2f{
                    float4 pos:SV_POSITION;
                    float2 uv[5]:TEXCOORD0;
                };
    
                sampler2D _MainTex;
                float4 _MainTex_TexelSize;
                float _uvAdd;
    
                v2f vert(a2v v){
                    v2f o;
                    o.uv[0]=v.uv;
                    o.uv[1]=v.uv + float2(0 , _MainTex_TexelSize.y) * _uvAdd;
                    o.uv[2]=v.uv - float2(0 , _MainTex_TexelSize.y) * _uvAdd;
                    o.uv[3]=v.uv + float2(0 , _MainTex_TexelSize.y * 2.0 ) * _uvAdd;
                    o.uv[4]=v.uv - float2(0 , _MainTex_TexelSize.y * 2.0 ) * _uvAdd;
                    o.pos=UnityObjectToClipPos(v.vertex);
                    return o;
                }
                fixed4 frag(v2f i):SV_TARGET{
                    
                    fixed3 col=tex2D(_MainTex,i.uv[0]).xyz * 0.4026;
                    col+=tex2D(_MainTex  , i.uv[1]).xyz * 0.2442;
                    col+=tex2D(_MainTex , i.uv[2]).xyz * 0.2442;
                    col+=tex2D(_MainTex , i.uv[3]).xyz * 0.0545;
                    col+=tex2D(_MainTex , i.uv[4]).xyz * 0.0545;
    
                    return fixed4(col,1.0);
                }
                ENDCG
            }
    
            Pass
            {
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
    
                #include "UnityCG.cginc"
    
                struct appdata
                {
                    float4 vertex : POSITION;
                    float2 uv : TEXCOORD0;
                };
    
                struct v2f
                {
                    float2 uv : TEXCOORD0;
                    float4 vertex : SV_POSITION;
                };
    
                v2f vert (appdata v)
                {
                    v2f o;
                    o.vertex = UnityObjectToClipPos(v.vertex);
                    o.uv = v.uv;
                    return o;
                }
    
                sampler2D _MainTex;
                float _Lum;
                float _Contrast;
                sampler2D _BloomTex;
    
                float3 ACESToneMapping(float3 color, float adapted_lum)
                {
                    const float A = 2.51f;
                    const float B = 0.03f;
                    const float C = 2.43f;
                    const float D = 0.59f;
                    const float E = 0.14f;
                    color *= adapted_lum;
                    return (color * (A * color + B)) / (color * (C * color + D) + E);
                }
    
                fixed4 frag (v2f i) : SV_Target
                {
                    fixed3 col = tex2D(_MainTex, i.uv).xyz;
                    //ton mapping
                    col=ACESToneMapping(col,_Lum);
    
                    //控制对比度
                    fixed3 avgColor=fixed3(0.5,0.5,0.5);
                    col=lerp(avgColor,col,_Contrast);
    
                    //bloom
                    fixed3 bloomColor=tex2D(_BloomTex,i.uv);
                    
                    return fixed4(col+bloomColor,1.0);
                }
                ENDCG
            }
        }
    }
    
    

    相关文章

      网友评论

        本文标题:HDR及其应用(tone mapping/bloom

        本文链接:https://www.haomeiwen.com/subject/lmdndqtx.html