Unity Shader学习(七)---bloom效果

(一)bloom效果(全屏泛光)的基本原理
先了解HDR技术:就是用有限的高度分布模拟更高范围的高亮度分布,实现这样的功能的技术叫做色调映射技术(Tone Mapping)。
Tone Mapping计算像素的亮度:
Lum(P) = 0.27R + 0.67G + 0.06B
Lscale(x,y) = a * Lum(x,y)/Lumave---Lumave平局亮度
Lfinal = Lscale/(1+Lscale)
光晕效果就是抽出场景中色彩比较亮的部分,加以模糊,使较亮的像素扩散到周边的像素中,再把模糊后的图像叠加到Tone Mapping之后的图像上。
 那么HDR与bloom效果的差别到底在什么地方呢?
第一,HDR效果就是超亮的光照与超暗的黑暗的某种结合,这个效果是光照产生的,强度、颜色等方面是游戏程序可动态控制的,是一种即时动态光影;bloom效果则是物体本身发出的光照,仅仅是将光照范围调高到过饱和,是游戏程序无法动态控制的,是一种全屏泛光。
第二,bloom效果无需HDR就可以实现,但是bloom效果是很受限的,它只支持8位RGBA,而HDR最高支持到32位RGBA。
第三,bloom效果的实现很简单,比如《半条命2》的MOD就是一个很小的很简单的MOD,而且bloom效果不受显卡的规格的限制,你甚至可以在TNT显卡上实现bloom效果(当然效果很差)!而HDR,必须是6XXX以上的显卡才能够实现,这里的HDR是指nVIDIA的HDR。这时有必要谈nVIDIA和ATI的显卡所实现的HDR,两者还是有区别的,具体区别就很专业了,总之从真实性表现来看,nVIDIA的显卡实现的HDR更好一些。HDR是nVIDIA提出的概念,从技术上来讲,ATI当然无法严格克隆nVIDIA的技术,所以ATI的HDR是另一种途径实现的尽可能接近的HDR,不能算“真”HDR,据传ATI的R520能够真正实现FP16 HDR。
Bloom效果实现的流程与HDR的物理还原不同,它只是一种简单的近似模拟:
第一步:先获取屏幕图像,然后对每个像素进行亮度检测,若大于某个阀值即保留原始颜色值,否则置为黑色;
第二步:对上一步获取的图像,做一个模糊,通常使用高斯模糊。
第三步:将模糊后的图片和原图片做一个加权和。

通过这三步就可以达到一个全屏泛光的效果。

(二)高斯模糊

对于一个五维的高斯核,我们需要的是3个权重值就可以,由于对称性。高斯方程很好的模拟了每个像素对于当前处理像素的影响程度,距离越近,影响越大。

程序部分:

using UnityEngine;
using System.Collections;

public class GaussianBlur : PostEffectsBase {

    public Shader gaussianBlurShader;
    private Material gaussianBlurMaterial = null;

    public Material material {  
        get {
            gaussianBlurMaterial = CheckShaderAndCreateMaterial(gaussianBlurShader, gaussianBlurMaterial);
            return gaussianBlurMaterial;
        }  
    }

    // Blur iterations - larger number means more blur.
    [Range(0, 4)]
    public int iterations = 3;
    
    // Blur spread for each iteration - larger value means more blur
    [Range(0.2f, 3.0f)]
    public float blurSpread = 0.6f;
    [Range(1, 8)]
    public int downSample = 2;
    
    /// 3rd edition: use iterations for larger blur
    void OnRenderImage (RenderTexture src, RenderTexture dest) {
        if (material != null) {
            int rtW = src.width/downSample;//利用缩放对图样进行采样,减少需要处理的像素个数
            int rtH = src.height/downSample;

            RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, 0);
            buffer0.filterMode = FilterMode.Bilinear;

            Graphics.Blit(src, buffer0);

            for (int i = 0; i < iterations; i++) {//多次迭代计算模糊
                material.SetFloat("_BlurSize", 1.0f + i * blurSpread);

                RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);

                // Render the vertical pass
                Graphics.Blit(buffer0, buffer1, material, 0);

                RenderTexture.ReleaseTemporary(buffer0);
                buffer0 = buffer1;
                buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);

                // Render the horizontal pass
                Graphics.Blit(buffer0, buffer1, material, 1);

                RenderTexture.ReleaseTemporary(buffer0);
                buffer0 = buffer1;
            }

            Graphics.Blit(buffer0, dest);
            RenderTexture.ReleaseTemporary(buffer0);
        } else {
            Graphics.Blit(src, dest);
        }
    }
}
shader部分:

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Unity Shaders Book/Chapter 12/Gaussian Blur" {
    Properties {
        _MainTex ("Base (RGB)"2D) = "white" {}
        _BlurSize ("Blur Size"Float) = 1.0
    }
    SubShader {
        CGINCLUDE
        
        #include "UnityCG.cginc"
        
        sampler2D _MainTex;  
        half4 _MainTex_TexelSize;
        float _BlurSize;
          
        struct v2f {
            float4 pos : SV_POSITION;
            half2 uv[5]: TEXCOORD0;
        };
          
        v2f vertBlurVertical(appdata_img v) {
            v2f o;
            o.pos = UnityObjectToClipPos(v.vertex);
            
            half2 uv = v.texcoord;
            
            o.uv[0] = uv;
            o.uv[1] = uv + float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
            o.uv[2] = uv - float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
            o.uv[3] = uv + float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
            o.uv[4] = uv - float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
                     
            return o;
        }
        
        v2f vertBlurHorizontal(appdata_img v) {
            v2f o;
            o.pos = UnityObjectToClipPos(v.vertex);
            
            half2 uv = v.texcoord;
            
            o.uv[0] = uv;
            o.uv[1] = uv + float2(_MainTex_TexelSize.x * 1.00.0) * _BlurSize;
            o.uv[2] = uv - float2(_MainTex_TexelSize.x * 1.00.0) * _BlurSize;
            o.uv[3] = uv + float2(_MainTex_TexelSize.x * 2.00.0) * _BlurSize;
            o.uv[4] = uv - float2(_MainTex_TexelSize.x * 2.00.0) * _BlurSize;
                     
            return o;
        }
        
        fixed4 fragBlur(v2f i) : SV_Target {
            float weight[3] = {0.40260.24420.0545};
            
            fixed3 sum = tex2D(_MainTex, i.uv[0]).rgb * weight[0];
            
            for (int it = 1; it < 3; it++) {
                sum += tex2D(_MainTex, i.uv[it*2-1]).rgb * weight[it];
                sum += tex2D(_MainTex, i.uv[it*2]).rgb * weight[it];
            }
            
            return fixed4(sum, 1.0);
        }
            
        ENDCG
        
        ZTest Always Cull Off ZWrite Off
        
        Pass {
            NAME "GAUSSIAN_BLUR_VERTICAL"
            
            CGPROGRAM
              
            #pragma vertex vertBlurVertical  
            #pragma fragment fragBlur
              
            ENDCG  
        }
        
        Pass {  
            NAME "GAUSSIAN_BLUR_HORIZONTAL"
            
            CGPROGRAM  
            
            #pragma vertex vertBlurHorizontal  
            #pragma fragment fragBlur
            
            ENDCG
        }
    } 
    FallBack "Diffuse"
}
(三)bloom效果

程序控制:

using UnityEngine;
using System.Collections;

public class Bloom : PostEffectsBase {

    public Shader bloomShader;
    private Material bloomMaterial = null;
    public Material material {  
        get {
            bloomMaterial = CheckShaderAndCreateMaterial(bloomShader, bloomMaterial);
            return bloomMaterial;
        }  
    }

    // Blur iterations - larger number means more blur.
    [Range(0, 4)]
    public int iterations = 3;
    
    // Blur spread for each iteration - larger value means more blur
    [Range(0.2f, 3.0f)]
    public float blurSpread = 0.6f;

    [Range(1, 8)]
    public int downSample = 2;

    [Range(0.0f, 4.0f)]
    public float luminanceThreshold = 0.6f;

    void OnRenderImage (RenderTexture src, RenderTexture dest) {
        if (material != null) {
            material.SetFloat("_LuminanceThreshold", luminanceThreshold);

            int rtW = src.width/downSample;
            int rtH = src.height/downSample;
            
            RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, 0);
            buffer0.filterMode = FilterMode.Bilinear;
            
            Graphics.Blit(src, buffer0, material, 0);
            
            for (int i = 0; i < iterations; i++) {
                material.SetFloat("_BlurSize", 1.0f + i * blurSpread);
                
                RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);
                
                // Render the vertical pass
                Graphics.Blit(buffer0, buffer1, material, 1);
                
                RenderTexture.ReleaseTemporary(buffer0);
                buffer0 = buffer1;
                buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);
                
                // Render the horizontal pass
                Graphics.Blit(buffer0, buffer1, material, 2);
                
                RenderTexture.ReleaseTemporary(buffer0);
                buffer0 = buffer1;
            }

            material.SetTexture ("_Bloom", buffer0);  
            Graphics.Blit (src, dest, material, 3);  

            RenderTexture.ReleaseTemporary(buffer0);
        } else {
            Graphics.Blit(src, dest);
        }
    }
}
脚本控制:

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Unity Shaders Book/Chapter 12/Bloom" {
    Properties {
        _MainTex ("Base (RGB)"2D) = "white" {}
        _Bloom ("Bloom (RGB)"2D) = "black" {}
        _LuminanceThreshold ("Luminance Threshold"Float) = 0.5
        _BlurSize ("Blur Size"Float) = 1.0
    }
    SubShader {
        CGINCLUDE
        
        #include "UnityCG.cginc"
        
        sampler2D _MainTex;
        half4 _MainTex_TexelSize;
        sampler2D _Bloom;
        float _LuminanceThreshold;
        float _BlurSize;
        
        struct v2f {
            float4 pos : SV_POSITION; 
            half2 uv : TEXCOORD0;
        };    
        
        v2f vertExtractBright(appdata_img v) {
            v2f o;
            
            o.pos = UnityObjectToClipPos(v.vertex);
            
            o.uv = v.texcoord;
                     
            return o;
        }
        
        fixed luminance(fixed4 color) {
            return  0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b; 
        }
        
        fixed4 fragExtractBright(v2f i) : SV_Target {
            fixed4 c = tex2D(_MainTex, i.uv);
            fixed val = clamp(luminance(c) - _LuminanceThreshold, 0.01.0);
            
            return c * val;
        }
        
        struct v2fBloom {
            float4 pos : SV_POSITION; 
            half4 uv : TEXCOORD0;
        };
        
        v2fBloom vertBloom(appdata_img v) {
            v2fBloom o;
            
            o.pos = UnityObjectToClipPos (v.vertex);
            o.uv.xy = v.texcoord;        
            o.uv.zw = v.texcoord;
            
            #if UNITY_UV_STARTS_AT_TOP            
            if (_MainTex_TexelSize.y < 0.0)
                o.uv.w = 1.0 - o.uv.w;
            #endif
                            
            return o; 
        }
        
        fixed4 fragBloom(v2fBloom i) : SV_Target {
            return tex2D(_MainTex, i.uv.xy) + tex2D(_Bloom, i.uv.zw);
        } 
        
        ENDCG
        
        ZTest Always Cull Off ZWrite Off
        
        Pass {  
            CGPROGRAM  
            #pragma vertex vertExtractBright  
            #pragma fragment fragExtractBright  
            
            ENDCG  
        }
        
        UsePass "Unity Shaders Book/Chapter 12/Gaussian Blur/GAUSSIAN_BLUR_VERTICAL"
        
        UsePass "Unity Shaders Book/Chapter 12/Gaussian Blur/GAUSSIAN_BLUR_HORIZONTAL"
        
        Pass {  
            CGPROGRAM  
            #pragma vertex vertBloom  
            #pragma fragment fragBloom  
            
            ENDCG  
        }
    }
    FallBack Off
}











你可能感兴趣的:(Unity,计算机图形学)