前言:前几天看米哈游的技术总监说:《崩坏3》的bloom效果的实现是
(1)高亮像素过滤
(2)向下采样(降采样)
(3)向上采样
(4)将模糊后的图像和原图像混合
经过上面的步骤,能高效的实现bloom效果
常规的bloom是使用: 提取高亮+ 卷积滤波横向和纵向+ 混合高亮模糊图和原图(这种实现方式和原理介绍,网上一大堆,这里就不再细说了)
一.效果图:
话不多说,直接上代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
[ImageEffectAllowedInSceneView]
public class Bloom : MonoBehaviour
{
#region constData
const int firstPass = 0;
const int downPass = 1;
const int upPass = 2;
const int mixPass = 3;
#endregion
public Shader bloom;
private Material material;
[Range(0,8)]
public int Interations;
[Range(1,10)]// 已经限定从1开始, 只有开启HDR的才会开启bloom
public float Threshold =1;
[Range(0,1)]
public float SoftThreshold = 0.5f;
[Range(0,10)]
public float Intensity = 1;
RenderTexture[] textures = new RenderTexture[8];
private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if (!material)
{
material = new Material(bloom);
material.hideFlags = HideFlags.HideAndDontSave;
}
material.SetFloat("_Threshold", Threshold);
material.SetFloat("_Intensity", Intensity);
float knee = Threshold - SoftThreshold;
Vector4 filter;
filter.x = Threshold;
filter.y = Threshold - knee;
filter.z = 2 * knee;
filter.w = 4 * knee + 0.00001f;
// 将参数,以V4的形式传递进shader,将计算量留在CPU,减少GPU的计算
material.SetVector("_Filter", filter);
int width = source.width;
int height = source.height;
width /= 2;
height /= 2;
RenderTextureFormat format = source.format;
RenderTexture currentDestination = textures[0] = RenderTexture.GetTemporary(width, height, 0, format);
Graphics.Blit(source, currentDestination,material,firstPass);
RenderTexture currentSource = currentDestination;
int i = 1;
// 向下采样
for (; i < Interations; i++)
{
width /= 2;
height /= 2;
if (height<2)
{
break;
}
currentDestination = textures[i] = RenderTexture.GetTemporary(width, height, 0, format);
Graphics.Blit(currentSource, currentDestination,material,downPass);
currentSource = currentDestination;
}
//向上采样
for (i-=2; i>=0; i--)
{
currentDestination = textures[i];
textures[i] = null;
Graphics.Blit(currentSource, currentDestination,material,upPass);
RenderTexture.ReleaseTemporary(currentSource);
currentSource = currentDestination;
}
material.SetTexture("_SourceTex", source);
//混合
Graphics.Blit(currentDestination, destination,material,mixPass);
RenderTexture.ReleaseTemporary(currentDestination);
}
}
Shader "custom/Bloom"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
//_SourceTex("SourceTex",2D) = "white"{}
//_Threshold("_Threshold",float) = 1
//_Filter("_Filter",Vector) = (0,0,0,0)
}
CGINCLUDE
#include "UnityCG.cginc"
float _Threshold;
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;
float4 _MainTex_TexelSize;
sampler2D _SourceTex;
float4 _SourceTex_ST;
vector _Filter;
float _Intensity;
half3 Sample(float2 uv)
{
return tex2D(_MainTex,uv).rgb;
}
//
half3 SampleBox(float2 uv,float detail)
{
float4 o = _MainTex_TexelSize.xyxy * float4(-detail,-detail,detail,detail);
half3 c = Sample(uv+ o.xy) + Sample(uv + o.xz) + Sample(uv+o.zw) +Sample(uv+o.zx);
return c*0.25;
}
// half3 Prefilter(half3 c)
// {
// half brightness = max(c.r,max(c.g,c.b));
// half contribution = max(0,brightness - _Threshold);
// contribution /= max(brightness,0.00001);
// return contribution;
// }
half3 Prefilter(half3 c)
{
half brightness = max(c.r,max(c.g,c.b));
half soft = brightness - _Filter.y;
soft = clamp(soft,0,_Filter.z);
soft = soft * soft/_Filter.w;
half contribution = max(soft,brightness- _Filter.x);
contribution /= max(brightness,0.00001);
return contribution*c;
}
ENDCG
SubShader
{
// No culling or depth
Cull Off ZWrite Off ZTest Always
Pass
{ //像素的预筛选
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
fixed4 frag (v2f i) : SV_Target
{
half3 col = Prefilter(SampleBox(i.uv,1));
return fixed4(col,1);
}
ENDCG
}
Pass
{
//下采样
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
fixed4 frag (v2f i) : SV_Target
{
half3 col = SampleBox(i.uv,1);
return fixed4(col,1);
}
ENDCG
}
Pass
{
Blend One One
// 上采样
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
fixed4 frag (v2f i) : SV_Target
{
half3 col = SampleBox(i.uv,0.5);
return fixed4(col,1);
}
ENDCG
}
Pass
{
//混合
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
fixed4 frag (v2f i) : SV_Target
{
half3 col = SampleBox(i.uv,0.5) + tex2D(_SourceTex,i.uv).rgb * _Intensity;
//half3 col = SampleBox(i.uv,1);
return fixed4(col,1);
}
ENDCG
}
}
}