1. 程式人生 > >Bloom效果【Unity Shader入門精要12.5】

Bloom效果【Unity Shader入門精要12.5】

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

//12.5 Bloom效果
Shader "Unlit/Chapter12-Bloom"
{
	Properties
	{
		//宣告屬性
		_MainTex ("Base(RGB)", 2D) = "white" {}
		_Bloom("Bloom(RGB)",2D)="black"{}
		_LumianceThreshold("Luminace 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;

		//1.定義提亮區域需要使用的頂點著色器和片元著色器
				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);
				//將取樣的到的亮度 - 閥值,並把結果擷取到0到1 
					fixed val = clamp(luminance(c) - _LuminanceThreshold, 0.0, 1.0);
					//然後把上面的值和原畫素值相乘,得到提取後的亮度區域
					
					return c * val;
				}

		//4.定義混合亮度部分影象和原影象時使用的頂點片元著色器
				struct v2fBloom {
					float4 pos:SV_POSITION;
					half4 uv:TEXCOORD0;
				};
				v2fBloom vertBloom(appdata_img v) {
					v2fBloom o;
					o.pos = UnityObjectToClipPos(v.vertex);
					//定義了兩個紋理座標並儲存在同一個型別為half4的變數uv中
					o.uv.xy = v.texcoord;
					o.uv.zw = v.texcoord;
					
		/*判斷當前平臺是否是DirectX型別的平臺.
		當在這樣的平臺下開啟了抗鋸齒後,主紋理的紋素值大小在豎直方向上會變成負值,以便我們對主紋理進行正確的取樣*/
					#if UNITY_UV_STARTS_AT_TOP
					//因此可以通過判斷_MainTex_TexelSize.y 是否小於0來檢測是否開啟了抗鋸齒
					if (_MainTex_TexelSize.y < 0.0)
						//如果是,就需要對除主紋理外的其他紋理的取樣座標進行豎直方向上的翻轉
						/*由於要同時處理多張紋理,因此在DirectX這樣的平臺下如果開啟了抗鋸齒,
						主紋理和亮度在豎直方向上的朝向就是不同的了,就需要對亮度紋理的取樣座標進行翻轉*/
						o.uv.w = 1.0 - o.uv.w;
					#endif
					return o;
				}
				fixed4 fragBloom(v2fBloom i) :SV_Target{
					//uv的xy對應了_MainTex.即原影象的紋理座標,zw對應_Bloom,即模糊後的較亮區域的紋理座標
					return tex2D(_MainTex,i.uv.xy) + tex2D(_Bloom,i.uv.zw);
				}


			ENDCG




//定義Bloom效果的4個Pass
			ZTest Always Cull Off ZWrite Off
		//1.提取亮度
		Pass
		{
			CGPROGRAM
			#pragma vertex vertExtractBright
			#pragma fragment fragExtractBright
			ENDCG
		}
		//2.使用12.4的高斯模糊
			UsePass "Unlit/Chapter12-GaussianBlur/GAUSSIAN_BLUR_VERTICAL"
		//3.使用12.4的高斯模糊
			UsePass "Unlit/Chapter12-GaussianBlur/GAUSSIAN_BLUR_HORIZONTAL"
		//4.混合
		Pass{
			CGPROGRAM
#pragma vertex vertBloom
#pragma fragment fragBloom
			ENDCG
		}

	}
			Fallback off
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//12.5 Bloom 效果
public class Bloom : PostEffectsBase  {
    //宣告效果使用的shader,並據此建立材質
    public Shader bloomShader;
    private Material bloomMaterial = null;
    public Material material
    {
        get
        {
            bloomMaterial = CheckShaderAndCreateMaterial(bloomShader, bloomMaterial);
            return bloomMaterial;
        }
    }
    //迭代次數
    [Range(0, 4)]
    public int iterations = 3;
    //模糊程度
    [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;
    /*絕大多數情況下,影象的亮度值不會超過一。
     但如果開啟了HDR,硬體會允許我們把顏色值儲存在一個更高精度範圍的緩衝中,此時畫素的亮度值可能會超過1*/

        /*bloom需要三個步驟
         1:提取影象中最亮的區域,
         2:高斯模糊迭代處理
         3:混合*/
    private void OnRenderImage(RenderTexture src, RenderTexture dest)
    {
        if (material !=null )
        {//1:
            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;
            //使用Shader中的第一個Pass提取影象中較亮的區域,將提取到的儲存在buffer中
            Graphics.Blit(src, buffer0, material, 0);

         
            for (int i = 0; i < iterations; i++)
            {
          //2:
                material.SetFloat("_BlurSize", 1.0f + i * blurSpread);
                RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);

                //shader的第二個pass
                Graphics.Blit(buffer0, buffer1, material, 1);
                RenderTexture.ReleaseTemporary(buffer0);
                buffer0 = buffer1;
                buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);

                //shader的第三個pass
                Graphics.Blit(buffer0, buffer1, material, 2);
                RenderTexture.ReleaseTemporary(buffer0);
                //將模糊後的區域儲存在buffer0 中
                buffer0 = buffer1;

            }
         //3:
            //將buffer0傳遞給材質中的_Bloom紋理屬性
            material.SetTexture("_Bloom", buffer0);
            //使用第四個pass進行混合,儲存在目標渲染紋理dest中
            Graphics.Blit(src, dest, material, 3);
            //釋放臨時快取
            RenderTexture.ReleaseTemporary(buffer0);

        }else
        {
            Graphics.Blit(src, dest);
        }

    }
}