Shader后期处理-Bloom
原理
- 对原场景图进行筛选,所有小于这个阈值的像素都被筛掉
- 对上一步操作的结果进行模糊操作,达到光溢出的效果
- 最后将处理过的图像和原图像进行叠加,得到最终效果
参考
代码
- Post_Bloom.shader
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176Shader "Post/Post_Bloom"
{
Properties
{
_MainTex("Base (RGB)", 2D) = "white" {}
_BlurTex("Blur", 2D) = "white"{}
}
CGINCLUDE
#include "UnityCG.cginc"
struct v2f_threshold
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
struct v2f_blur
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 uv01 : TEXCOORD1;
float4 uv23 : TEXCOORD2;
float4 uv45 : TEXCOORD3;
};
struct v2f_bloom
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float2 uv1 : TEXCOORD1;
};
sampler2D _MainTex;
float4 _MainTex_TexelSize;
sampler2D _BlurTex;
float4 _BlurTex_TexelSize;
float4 _offsets;
float4 _colorThreshold;
float4 _bloomColor;
float _bloomFactor;
v2f_threshold vert_threshold(appdata_img v)
{
v2f_threshold o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord.xy;
//dx中纹理从左上角为初始坐标,需要反向
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0)
o.uv.y = 1 - o.uv.y;
#endif
return o;
}
fixed luminance(fixed4 color)
{
return 0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b;
}
fixed4 frag_threshold(v2f_threshold i) : SV_Target
{
fixed4 color = tex2D(_MainTex, i.uv);
fixed val = saturate(luminance(color) - luminance(_colorThreshold));
return color * val;
// return saturate(color - _colorThreshold);
}
v2f_blur vert_blur(appdata_img v)
{
v2f_blur o;
_offsets *= _MainTex_TexelSize.xyxy;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord.xy;
o.uv01 = v.texcoord.xyxy + _offsets.xyxy * float4(1, 1, -1, -1);
o.uv23 = v.texcoord.xyxy + _offsets.xyxy * float4(1, 1, -1, -1) * 2.0;
o.uv45 = v.texcoord.xyxy + _offsets.xyxy * float4(1, 1, -1, -1) * 3.0;
return o;
}
fixed4 frag_blur(v2f_blur i) : SV_Target
{
fixed4 color = fixed4(0, 0, 0, 0);
color += 0.40 * tex2D(_MainTex, i.uv);
color += 0.15 * tex2D(_MainTex, i.uv01.xy);
color += 0.15 * tex2D(_MainTex, i.uv01.zw);
color += 0.10 * tex2D(_MainTex, i.uv23.xy);
color += 0.10 * tex2D(_MainTex, i.uv23.zw);
color += 0.05 * tex2D(_MainTex, i.uv45.xy);
color += 0.05 * tex2D(_MainTex, i.uv45.zw);
return color;
}
v2f_bloom vert_bloom(appdata_img v)
{
v2f_bloom o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv.xy = v.texcoord.xy;
o.uv1.xy = o.uv.xy;
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0)
o.uv.y = 1 - o.uv.y;
#endif
return o;
}
fixed4 frag_bloom(v2f_bloom i) : SV_Target
{
fixed4 ori = tex2D(_MainTex, i.uv1);
fixed4 blur = tex2D(_BlurTex, i.uv);
fixed4 final = ori + _bloomFactor * blur * _bloomColor;
return final;
}
ENDCG
SubShader
{
//pass 0: 提取高亮部分
Pass
{
ZTest Off
Cull Off
ZWrite Off
Fog
{
Mode Off
}
CGPROGRAM
#pragma vertex vert_threshold
#pragma fragment frag_threshold
ENDCG
}
//pass 1: 高斯模糊
Pass
{
ZTest Off
Cull Off
ZWrite Off
Fog
{
Mode Off
}
CGPROGRAM
#pragma vertex vert_blur
#pragma fragment frag_blur
ENDCG
}
//pass 2: Bloom效果
Pass
{
ZTest Off
Cull Off
ZWrite Off
Fog
{
Mode Off
}
CGPROGRAM
#pragma vertex vert_bloom
#pragma fragment frag_bloom
ENDCG
}
}
} - PostBloom.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57using UnityEngine;
[ExecuteInEditMode]
[ImageEffectAllowedInSceneView]
[RequireComponent(typeof(Camera))]
public class PostBloom : MonoBehaviour
{
[SerializeField] private Shader shader;
private Material m_material;
[Range(0.0f, 1.0f)] public float bloomFactor = 0.5f;
[Range(0, 4)] public int downSample = 1;
public int samplerScale = 1;
public Color colorThreshold = Color.gray;
public Color bloomColor = Color.white;
void Awake()
{
m_material = new Material(shader);
}
void OnValidate()
{
if (shader)
m_material = new Material(shader);
}
void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if (m_material)
{
RenderTexture temp1 = RenderTexture.GetTemporary(source.width >> downSample, source.height >> downSample, 0,
source.format);
RenderTexture temp2 = RenderTexture.GetTemporary(source.width >> downSample, source.height >> downSample, 0,
source.format);
Graphics.Blit(source, temp1);
m_material.SetVector("_colorThreshold", colorThreshold);
Graphics.Blit(temp1, temp2, m_material, 0);
m_material.SetVector("_offsets", new Vector4(0, samplerScale, 0, 0));
Graphics.Blit(temp2, temp1, m_material, 1);
m_material.SetVector("_offsets", new Vector4(samplerScale, 0, 0, 0));
Graphics.Blit(temp1, temp2, m_material, 1);
m_material.SetTexture("_BlurTex", temp2);
m_material.SetVector("_bloomColor", bloomColor);
m_material.SetFloat("_bloomFactor", bloomFactor);
Graphics.Blit(source, destination, m_material, 2);
RenderTexture.ReleaseTemporary(temp1);
RenderTexture.ReleaseTemporary(temp2);
}
}
}
Shader后期处理-Bloom
https://automask.github.io/wild/2021/06/17/lab/S_Unity_Post_Bloom/