Unity3d 中的 HDR_BLOOM

虽然u3d标准资源包自带这货,但是我就喜欢重复造轮子,你奈我何

unity3d_hdr_bloom

简单来说 hdr_bloom 就3个Shader
第一个是shader, 筛选出像素亮度
第二个shader, 对像素进行模糊
第三个shader, 合并原始纹理 + 模糊亮度的纹理

第一个Shader
筛选亮度的算法

float brightness = col.r * 0.299 + col.g * 0.587 + col.b * 0.114;

之后我们给 Shader 一个 uniform 参数,作为亮度的调节参数即可

之后,为了让亮度周围出现光晕,我们将亮度纹理绘制在一张特别小的纹理上,然后再将这张小纹理进行拉伸,使其出现像素马赛克效果,这样处理后,迈赛克的亮度部分就会比原始纹理的明亮部分范围更大了,初步的光晕范围就出来了。
但是这部分光晕因为是马赛克,于是要将其进行模糊处理

绘制到缩小纹理再拉伸的效果是这样的

unity3d_hdr_bloom_2


请不要无脑复制转载本文, 由 dreamfairy 原创, 转载请注明出处 本文地址 http://www.dreamfairy.cn/blog/2016/05/28/unity3d-%e4%b8%ad%e7%9a%84-hdr_bloom/
那么实际操作的时候,我们将亮度提取的纹理先绘制到 1/16 大小的纹理上, 模糊后,再绘制到 1/8 纹理上, 然后 1/4, 1/2
最后将 1/2的模糊亮度纹理 和 原始纹理进行像素合并即可

这是模糊 1/2 后的纹理效果
unity3d_hdr_bloom_3

模糊的算法,原本采用 3×3 的二维高斯模糊算法,但是实际上和 2×2 的平滑模糊效果差不多, 于是就使用 2×2 的平滑模糊算法来做, 实际上使用 2个pass, 1个pass使用纵向3次 一维高斯算法, 1个pass 使用横向3次 一维高斯算法的效果最好, 不过我目前在做手机上的shader,差这么一点变现力还是可以接受的。

后面是3个完整Shader

取亮度

Shader "Aoi/OutputLuminosityShader"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_Brightness("BrightnessBias", float) = 0.1
	}
	SubShader
	{
		Tags { "RenderType"="Opaque" }
		LOD 100

		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			// make fog work
			#pragma multi_compile_fog
			
			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct v2f
			{
				float2 uv : TEXCOORD0;
				UNITY_FOG_COORDS(1)
				float4 vertex : SV_POSITION;
			};

			sampler2D _MainTex;
			uniform float _Brightness;
			float4 _MainTex_ST;
			
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				UNITY_TRANSFER_FOG(o,o.vertex);
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				// sample the texture
				fixed4 col = tex2D(_MainTex, i.uv);
				float brightness = col.r * 0.299 + col.g * 0.587 + col.b * 0.114;

				if (brightness < _Brightness) {
					col.rgb = 0;
				}

				// apply fog
				UNITY_APPLY_FOG(i.fogCoord, col);
				return col;
			}
			ENDCG
		}
	}
}

2×2模糊

Shader "Aoi/2x2SimpleBlur"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
	}
	SubShader
	{
		Tags { "RenderType"="Opaque" }
		LOD 100

		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 leftUpUV : TEXCOORD1;
				float4 rightDownUV : TEXCOORD2;
				float4 vertex : SV_POSITION;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			float4 _MainTex_TexelSize;
			
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);

				float2 uv = TRANSFORM_TEX(v.uv, _MainTex);
				o.uv = uv;
				o.leftUpUV = float4(uv.x - _MainTex_TexelSize.x, uv.y, uv.x, uv.y + _MainTex_TexelSize.y);
				o.rightDownUV = float4(uv.x + _MainTex_TexelSize.x, uv.y, uv.x, uv.y - _MainTex_TexelSize.y);
				
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				// sample the texture
				fixed4 col = tex2D(_MainTex, i.uv);
				
				col += tex2D(_MainTex, i.leftUpUV.xy);
				col += tex2D(_MainTex, i.leftUpUV.zw);
				col += tex2D(_MainTex, i.rightDownUV.xy);
				col += tex2D(_MainTex, i.rightDownUV.zw);

				col /= 5;
				
				return col;
			}
			ENDCG
		}
	}
}

合并纹理 的shader 就不上了
实际C# Code

using UnityEngine;
using System.Collections;

public class HDRBloomPostEffect : MonoBehaviour {
    public float BrightnessBias = 1.0f;

    private Material m_hdrBloomMat;
    private Material m_filterMat;
    private Material m_combineMat;

    // Use this for initialization
    void Start () {
        m_hdrBloomMat = new Material(Shader.Find("Aoi/OutputLuminosityShader"));
        m_filterMat = new Material(Shader.Find("Aoi/2x2SimpleBlur"));
        m_combineMat = new Material(Shader.Find("Aoi/CombineTex"));
    }

    float ComputeGaussianWeight(int posX, int posY, float sigma)
    {
        float v1 = 1.0f / (2.0f * Mathf.PI * sigma * sigma);
        float v2 = -(posX * posX + posY * posY) / (2 * sigma * sigma);
        v2 = Mathf.Exp(v2);

        return v1 * v2;
    }

    void OnRenderImage(RenderTexture src, RenderTexture dst) {
        RenderTexture rt16 = RenderTexture.GetTemporary(src.width / 16, src.height / 16);
        RenderTexture rt8 = RenderTexture.GetTemporary(src.width / 8, src.height / 8);
        RenderTexture rt4 = RenderTexture.GetTemporary(src.width / 4, src.height / 4);
        RenderTexture rt2 = RenderTexture.GetTemporary(src.width / 2, src.height / 2);

        m_hdrBloomMat.SetFloat("_Brightness", BrightnessBias);
        Graphics.Blit(src, rt16, m_hdrBloomMat);

        Graphics.Blit(rt16, rt8, m_filterMat);
        Graphics.Blit(rt8, dst, m_filterMat);
        Graphics.Blit(rt4, rt2, m_filterMat);

        m_combineMat.SetTexture("_AnotherTex", src);
        Graphics.Blit(rt2, dst, m_combineMat);

        RenderTexture.ReleaseTemporary(rt16);
        RenderTexture.ReleaseTemporary(rt8);
        RenderTexture.ReleaseTemporary(rt4);
        RenderTexture.ReleaseTemporary(rt2);
    }
}