大家好,我是阿赵。
继续介绍屏幕后处理效果,这一期讲一下Bloom效果。
Bloom效果,就是一种全屏泛光的效果,让模型和特效有一种真的在发光的感觉。
根据参数不一样,可以做出不同的发光效果。
之前在介绍模糊效果的时候说过,Bloom效果也是基于模糊效果制作的。
Bloom的原理很简单,先用模糊处理,算出一张模糊后的图片,然后把这张图片的RGB和原始屏幕图片的RGB相加就可以了。由于是RGB颜色叠加,所以本身图片颜色越接近白色的地方,越容易爆掉,所以越接近白色的地方发光的感觉就越明显。如果想Bloom的效果更爆一点,就自己对模糊后的图片进一步做处理吧,最常见的手段就是先乘后加,或者Power后再相加也可以
实现很简单,在原来的模糊效果代码上面,我们应该再加多一个叠加的Shader就可以了。
half4 frag (v2f_img i) : SV_Target
{
half4 col = tex2D(_MainTex, i.uv);
half4 brightCol = tex2D(_brightTex, i.uv);
col.rgb += brightCol.rgb;
return col;
}
我这里在处理模糊之前,先加多了一个BrightRange的计算。这是为了控制一下发光的范围。BrightRange的算法很简单,先取原始图片的rgb三个通道颜色值最大的通道的值,然后用这个最大值减去一个我们指定的值。最后把原图的rgb颜色乘以这个值就可以了。
fixed4 frag (v2f_img i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
float br = max(max(col.r, col.g), col.b);
br = max(0, (br - _BrightCut)) / max(br, 0.00001);
col.rgb *= br;
return col;
}
通过这个值,我们就可以更好的控制发光的范围
三、源码
1、C#部分
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BloomCtrl : MonoBehaviour
{
private Material blurMat;
private Material brightMat;
private Material bloomMat;
public bool isBlur = false;
[Range(0, 4)]
public float blurSize = 0;
[Range(-3, 3)]
public float blurOffset = 1;
[Range(1, 3)]
public int blurType = 3;
[Range(0, 1)]
public float brightCut = 0.5f;
void Start()
{
}
void Update()
{
}
private Material GetBlurMat(int bType)
{
if (bType == 1)
{
return new Material(Shader.Find("Hidden/AzhaoBoxBlur"));
}
else if (bType == 2)
{
return new Material(Shader.Find("Hidden/AzhaoGaussianBlur"));
}
else if (bType == 3)
{
return new Material(Shader.Find("Hidden/AzhaoKawaseBlur"));
}
else
{
return null;
}
}
private void ReleaseRT(RenderTexture rt)
{
if (rt != null)
{
RenderTexture.ReleaseTemporary(rt);
}
}
private bool CheckNeedCreateBlurMat(Material mat, int bType)
{
if (mat == null)
{
return true;
}
if (mat.shader == null)
{
return true;
}
if (bType == 1)
{
if (mat.shader.name != "Hidden/AzhaoBoxBlur")
{
return true;
}
else
{
return false;
}
}
else if (bType == 2)
{
if (mat.shader.name != "Hidden/AzhaoGaussianBlur")
{
return true;
}
else
{
return false;
}
}
else if (bType == 3)
{
if (mat.shader.name != "Hidden/AzhaoKawaseBlur")
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
private void BlurFun(RenderTexture source, RenderTexture destination, float blurTime, int bType, float offset)
{
if (CheckNeedCreateBlurMat(blurMat, bType) == true)
{
blurMat = GetBlurMat(bType);
}
if (blurMat == null || blurMat.shader == null || blurMat.shader.isSupported == false)
{
return;
}
blurMat.SetFloat("_BlurOffset", offset);
float width = source.width;
float height = source.height;
int w = Mathf.FloorToInt(width);
int h = Mathf.FloorToInt(height);
RenderTexture rt1 = RenderTexture.GetTemporary(w, h);
RenderTexture rt2 = RenderTexture.GetTemporary(w, h);
Graphics.Blit(source, rt1);
for (int i = 0; i < blurTime; i++)
{
ReleaseRT(rt2);
width = width / 2;
height = height / 2;
w = Mathf.FloorToInt(width);
h = Mathf.FloorToInt(height);
rt2 = RenderTexture.GetTemporary(w, h);
Graphics.Blit(rt1, rt2, blurMat, 0);
width = width / 2;
height = height / 2;
w = Mathf.FloorToInt(width);
h = Mathf.FloorToInt(height);
ReleaseRT(rt1);
rt1 = RenderTexture.GetTemporary(w, h);
Graphics.Blit(rt2, rt1, blurMat, 1);
}
for (int i = 0; i < blurTime; i++)
{
ReleaseRT(rt2);
width = width * 2;
height = height * 2;
w = Mathf.FloorToInt(width);
h = Mathf.FloorToInt(height);
rt2 = RenderTexture.GetTemporary(w, h);
Graphics.Blit(rt1, rt2, blurMat, 0);
width = width * 2;
height = height * 2;
w = Mathf.FloorToInt(width);
h = Mathf.FloorToInt(height);
ReleaseRT(rt1);
rt1 = RenderTexture.GetTemporary(w, h);
Graphics.Blit(rt2, rt1, blurMat, 1);
}
Graphics.Blit(rt1, destination);
ReleaseRT(rt1);
rt1 = null;
ReleaseRT(rt2);
rt2 = null;
return;
}
private bool BrightRangeFun(RenderTexture source, RenderTexture destination)
{
if (brightMat == null)
{
brightMat = new Material(Shader.Find("Hidden/BrightRange"));
}
if (brightMat == null || brightMat.shader == null || brightMat.shader.isSupported == false)
{
return false;
}
brightMat.SetFloat("_BrightCut", brightCut);
Graphics.Blit(source, destination, brightMat);
return true;
}
private bool BloomAddFun(RenderTexture source, RenderTexture destination, RenderTexture brightTex)
{
if (bloomMat == null)
{
bloomMat = new Material(Shader.Find("Hidden/AzhaoBloom"));
}
if (bloomMat == null || bloomMat.shader == null || bloomMat.shader.isSupported == false)
{
return false;
}
bloomMat.SetTexture("_brightTex", brightTex);
Graphics.Blit(source, destination, bloomMat);
return true;
}
private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if (isBlur == true)
{
RenderTexture finalRt = source;
RenderTexture rt2 = RenderTexture.GetTemporary(source.width, source.height);
RenderTexture rt3 = RenderTexture.GetTemporary(source.width, source.height);
BrightRangeFun(source, rt2);
if (blurSize > 0)
{
BlurFun(rt2, rt3, blurSize, blurType, blurOffset);
BloomAddFun(source, finalRt, rt3);
}
Graphics.Blit(finalRt, destination);
ReleaseRT(finalRt);
ReleaseRT(rt2);
ReleaseRT(rt3);
}
else
{
Graphics.Blit(source, destination);
}
}
}
2、Shader
模糊shader就不重复了,参考之前的文章
这里提供一下Bloom和BrightRange的shader
1.Bloom
Shader "Hidden/AzhaoBloom"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_brightTex("BrightTex",2D) = "black"
}
SubShader
{
// No culling or depth
Cull Off ZWrite Off ZTest Always
Pass
{
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
sampler2D _brightTex;
half4 frag (v2f_img i) : SV_Target
{
half4 col = tex2D(_MainTex, i.uv);
half4 brightCol = tex2D(_brightTex, i.uv);
col.rgb += brightCol.rgb;
return col;
}
ENDCG
}
}
}
2.BrightRange
Shader "Hidden/BrightRange"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_BrightCut("LightVal",Range(0,1)) = 0.5
}
SubShader
{
// No culling or depth
Cull Off ZWrite Off ZTest Always
Pass
{
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
float _BrightCut;
fixed4 frag (v2f_img i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
float br = max(max(col.r, col.g), col.b);
br = max(0, (br - _BrightCut)) / max(br, 0.00001);
col.rgb *= br;
return col;
}
ENDCG
}
}
}