Last active
May 1, 2022 20:43
-
-
Save WamWooWam/ec6109e240aef0e5313ab27db9a35b61 to your computer and use it in GitHub Desktop.
Sonic Colours-style bloom implemented as a Unity PostProcessEffect
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.Reflection; | |
using UnityEngine; | |
using UnityEngine.Rendering; | |
using UnityEngine.Rendering.PostProcessing; | |
[Serializable] | |
[PostProcess(typeof(RainbowBloomRenderer), PostProcessEvent.AfterStack, "Rainbow/Bloom")] | |
public sealed class RainbowBloom : PostProcessEffectSettings | |
{ | |
public FloatParameter threshold = new FloatParameter() { value = 0.9f }; | |
} | |
public sealed class RainbowBloomRenderer : PostProcessEffectRenderer<RainbowBloom> | |
{ | |
private const int COLORS_BLOOM_WIDTH = 160; | |
private const int COLORS_BLOOM_HEIGHT = 120; | |
private const int COLORS_BLOOM_BUFFERS = 4; | |
private RenderTexture brightPassSrcTex; | |
private RenderTexture[] bloomTex; | |
private RenderTexture[] bloomTexTemp; | |
private static class Pass | |
{ | |
public static int Downsample = 0, | |
Bloom = 1, | |
AlphaBlend = 2, | |
AdditiveBlend = 3, | |
Copy = 4; | |
} | |
private static int _Param = Shader.PropertyToID(nameof(_Param)); | |
private static int _BloomStar_Param1 = Shader.PropertyToID(nameof(_BloomStar_Param1)); | |
private static int _BlendParams = Shader.PropertyToID(nameof(_BlendParams)); | |
public override void Render(PostProcessRenderContext context) | |
{ | |
context.command.BeginSample("RainbowBloom"); | |
// get our shader | |
var sheet = context.propertySheets.Get(Shader.Find("Hidden/PostProcessing/RainbowBloom")); | |
// aquire our render targets | |
brightPassSrcTex = context.GetScreenSpaceTemporaryRT(widthOverride: COLORS_BLOOM_WIDTH, heightOverride: COLORS_BLOOM_HEIGHT); | |
bloomTex = new RenderTexture[COLORS_BLOOM_BUFFERS]; | |
bloomTexTemp = new RenderTexture[COLORS_BLOOM_BUFFERS]; | |
for (int i = 0; i < COLORS_BLOOM_BUFFERS; i++) | |
{ | |
bloomTex[i] = context.GetScreenSpaceTemporaryRT(widthOverride: COLORS_BLOOM_WIDTH >> i, heightOverride: COLORS_BLOOM_HEIGHT >> i); | |
bloomTexTemp[i] = context.GetScreenSpaceTemporaryRT(widthOverride: COLORS_BLOOM_WIDTH >> i, heightOverride: COLORS_BLOOM_HEIGHT >> i); | |
} | |
// calculate width/height for downsampling the display | |
float rcpWidth = 1.0f / context.screenWidth; | |
float rcpHeight = 1.0f / context.screenHeight; | |
float blockWidth = Math.Max(1.0f, context.screenWidth / (float)COLORS_BLOOM_WIDTH); | |
float blockHeight = Math.Max(1.0f, context.screenHeight / (float)COLORS_BLOOM_HEIGHT); | |
var downSampleParam = new Vector4( | |
((blockWidth / 2.0f + 0.5f) * rcpWidth), | |
rcpWidth, | |
((blockHeight / 2.0f + 0.5f) * rcpHeight), | |
rcpHeight | |
); | |
// downsample the display into brightPassSrcTex | |
sheet.properties.SetVector(_Param, downSampleParam); | |
context.command.BlitFullscreenTriangle(context.source, brightPassSrcTex, sheet, Pass.Downsample); | |
// copy the bright pass into the first bloom texture | |
sheet.properties.SetFloat(_BloomStar_Param1, this.settings.threshold.value); | |
context.command.BlitFullscreenTriangle(brightPassSrcTex, bloomTex[0], sheet, Pass.Bloom); | |
// downscale the previous bloom texture into the next 3 textures | |
for (int i = 1; i < COLORS_BLOOM_BUFFERS; i++) | |
context.command.BlitFullscreenTriangle(bloomTex[i - 1], bloomTex[i]); | |
// for each buffer, shift it up, down, left and right a number of times, this blurs the texture | |
// it's rudimentary, but it works | |
for (int i = 0; i < COLORS_BLOOM_BUFFERS; i++) | |
{ | |
for (int j = 0; j < 2; j++) | |
{ | |
var srcSurface = j == 0 ? bloomTex[i] : bloomTexTemp[i]; | |
var destSurface = j == 0 ? bloomTexTemp[i] : bloomTex[i]; | |
for (int k = 0; k < 1 + (1 << (i + 1)); k++) | |
{ | |
float x = 0.0f; | |
float y = 0.0f; | |
if (j == 0) | |
x = (float)((k + 1) / 2); | |
else | |
y = (float)((k + 1) / 2); | |
if ((k % 2) == 0) | |
{ | |
x *= -1; | |
y *= -1; | |
} | |
// pass our parameters into the shader | |
float xCoord = x / (float)srcSurface.width; | |
float yCoord = y / (float)srcSurface.height; | |
float srcAlphaDstAlpha = 1.0f / (k + 1.0f); | |
sheet.properties.SetVector(_BlendParams, new Vector4(xCoord, yCoord, srcAlphaDstAlpha, 0)); | |
// will it blend? | |
context.command.BlitFullscreenTriangle(srcSurface, destSurface, sheet, Pass.AlphaBlend); | |
} | |
} | |
} | |
// copy the display | |
context.command.BlitFullscreenTriangle(context.source, context.destination); | |
// additively blend the bloom textures | |
for (int i = 3; i >= 0; i--) | |
context.command.BlitFullscreenTriangle(bloomTex[i], context.destination, sheet, Pass.AdditiveBlend); | |
context.command.EndSample("RainbowBloom"); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Shader "Hidden/PostProcessing/RainbowBloom" | |
{ | |
HLSLINCLUDE | |
#include "Packages/com.unity.postprocessing/PostProcessing/Shaders/StdLib.hlsl" | |
#include "Packages/com.unity.postprocessing/PostProcessing/Shaders/Colors.hlsl" | |
#include "Packages/com.unity.postprocessing/PostProcessing/Shaders/Sampling.hlsl" | |
TEXTURE2D_SAMPLER2D(_MainTex, sampler_MainTex); | |
float4 _Param; | |
float _BloomStar_Param1; | |
float4 _BlendParams; | |
float4 BloomFrag(VaryingsDefault i) : SV_Target | |
{ | |
return float4(saturate(saturate(SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord).xyz * 0.5) / (_BloomStar_Param1.x * 0.5) - _BloomStar_Param1.x), 1); | |
} | |
float4 DownsampleFrag(VaryingsDefault i) : SV_Target | |
{ | |
float4 sum = 0; | |
int count = 0; | |
for (float x = -_Param.x; x <= _Param.x; x += _Param.y) | |
{ | |
for (float y = -_Param.z; y <= _Param.z; y += _Param.w) | |
{ | |
sum += SAMPLE_TEXTURE2D_LOD(_MainTex, sampler_MainTex, i.texcoord + float2(x, y), 0); | |
++count; | |
} | |
} | |
return count > 0 ? sum / count : 0; | |
} | |
VaryingsDefault AlphaBlendVert(AttributesDefault v) | |
{ | |
VaryingsDefault o; | |
o.vertex = float4(v.vertex.xy, 0.0, 1.0); | |
o.texcoord = TransformTriangleVertexToUV(v.vertex.xy); | |
#if UNITY_UV_STARTS_AT_TOP | |
o.texcoord = o.texcoord * float2(1.0, -1.0) + float2(0.0, 1.0); | |
#endif | |
o.texcoordStereo = TransformStereoScreenSpaceTex(o.texcoord, 1.0); | |
return o; | |
} | |
float4 AlphaBlendFrag(VaryingsDefault i) : SV_Target | |
{ | |
return float4(SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord + _BlendParams.xy).xyz, _BlendParams.z); | |
} | |
float4 PassFinal(VaryingsDefault i) : SV_Target | |
{ | |
return SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord); | |
} | |
ENDHLSL | |
SubShader | |
{ | |
Cull Off ZWrite Off ZTest Always | |
// 0: Prefilter 13 taps | |
Pass | |
{ | |
HLSLPROGRAM | |
#pragma vertex VertDefault | |
#pragma fragment DownsampleFrag | |
ENDHLSL | |
} | |
Pass | |
{ | |
HLSLPROGRAM | |
#pragma vertex VertDefault | |
#pragma fragment BloomFrag | |
ENDHLSL | |
} | |
Pass | |
{ | |
Blend SrcAlpha OneMinusSrcAlpha | |
HLSLPROGRAM | |
#pragma vertex AlphaBlendVert | |
#pragma fragment AlphaBlendFrag | |
ENDHLSL | |
} | |
Pass | |
{ | |
Blend One One | |
HLSLPROGRAM | |
#pragma vertex VertDefault | |
#pragma fragment PassFinal | |
ENDHLSL | |
} | |
Pass | |
{ | |
HLSLPROGRAM | |
#pragma vertex VertDefault | |
#pragma fragment PassFinal | |
ENDHLSL | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment