https://catlikecoding.com/unity/tutorials/custom-srp/hdr/
上图中,旋转摄像机,会看到一闪一闪的。
漫反射bloom
#ifndef CUSTOM_POST_FX_PASSES_INCLUDED
#define CUSTOM_POST_FX_PASSES_INCLUDED
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Filtering.hlsl"
TEXTURE2D(_PostFXSource);
TEXTURE2D(_PostFXSource2);
SAMPLER(sampler_linear_clamp);
float4 _PostFXSource_TexelSize;
float4 GetSourceTexelSize () {
return _PostFXSource_TexelSize;
}
float4 GetSource(float2 fxUV) {
return SAMPLE_TEXTURE2D(_PostFXSource, sampler_linear_clamp, fxUV);
}
float4 GetSourceBicubic (float2 fxUV) {
return SampleTexture2DBicubic(
TEXTURE2D_ARGS(_PostFXSource, sampler_linear_clamp), fxUV,
_PostFXSource_TexelSize.zwxy, 1.0, 0.0
);
}
float4 GetSource2(float2 fxUV) {
return SAMPLE_TEXTURE2D(_PostFXSource2, sampler_linear_clamp, fxUV);
}
struct Varyings {
float4 positionCS : SV_POSITION;
float2 fxUV : VAR_FX_UV;
};
Varyings DefaultPassVertex (uint vertexID : SV_VertexID) {
Varyings output;
output.positionCS = float4(
vertexID <= 1 ? -1.0 : 3.0,
vertexID == 1 ? 3.0 : -1.0,
0.0, 1.0
);
output.fxUV = float2(
vertexID <= 1 ? 0.0 : 2.0,
vertexID == 1 ? 2.0 : 0.0
);
if (_ProjectionParams.x < 0.0) {
output.fxUV.y = 1.0 - output.fxUV.y;
}
return output;
}
bool _BloomBicubicUpsampling;
float _BloomIntensity;
float4 BloomAddPassFragment (Varyings input) : SV_TARGET {
float3 lowRes;
if (_BloomBicubicUpsampling) {
lowRes = GetSourceBicubic(input.fxUV).rgb;
}
else {
lowRes = GetSource(input.fxUV).rgb;
}
float3 highRes = GetSource2(input.fxUV).rgb;
return float4(lowRes * _BloomIntensity + highRes, 1.0);
}
float4 BloomHorizontalPassFragment (Varyings input) : SV_TARGET {
float3 color = 0.0;
float offsets[] = {
-4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0
};
float weights[] = {
0.01621622, 0.05405405, 0.12162162, 0.19459459, 0.22702703,
0.19459459, 0.12162162, 0.05405405, 0.01621622
};
for (int i = 0; i < 9; i++) {
float offset = offsets[i] * 2.0 * GetSourceTexelSize().x;
color += GetSource(input.fxUV + float2(offset, 0.0)).rgb * weights[i];
}
return float4(color, 1.0);
}
float4 _BloomThreshold;
float3 ApplyBloomThreshold (float3 color) {
float brightness = Max3(color.r, color.g, color.b);
float soft = brightness + _BloomThreshold.y;
soft = clamp(soft, 0.0, _BloomThreshold.z);
soft = soft * soft * _BloomThreshold.w;
float contribution = max(soft, brightness - _BloomThreshold.x);
contribution /= max(brightness, 0.00001);
return color * contribution;
}
float4 BloomPrefilterPassFragment (Varyings input) : SV_TARGET {
float3 color = ApplyBloomThreshold(GetSource(input.fxUV).rgb);
return float4(color, 1.0);
}
float4 BloomPrefilterFirefliesPassFragment (Varyings input) : SV_TARGET {
float3 color = 0.0;
float weightSum = 0.0;
float2 offsets[] = {
float2(0.0, 0.0),
float2(-1.0, -1.0), float2(-1.0, 1.0), float2(1.0, -1.0), float2(1.0, 1.0)
};
for (int i = 0; i < 5; i++) {
float3 c =
GetSource(input.fxUV + offsets[i] * GetSourceTexelSize().xy * 2.0).rgb;
c = ApplyBloomThreshold(c);
float w = 1.0 / (Luminance(c) + 1.0);
color += c * w;
weightSum += w;
}
color /= weightSum;
return float4(color, 1.0);
}
float4 BloomScatterPassFragment (Varyings input) : SV_TARGET {
float3 lowRes;
if (_BloomBicubicUpsampling) {
lowRes = GetSourceBicubic(input.fxUV).rgb;
}
else {
lowRes = GetSource(input.fxUV).rgb;
}
float3 highRes = GetSource2(input.fxUV).rgb;
return float4(lerp(highRes, lowRes, _BloomIntensity), 1.0);
}
float4 BloomScatterFinalPassFragment (Varyings input) : SV_TARGET {
float3 lowRes;
if (_BloomBicubicUpsampling) {
lowRes = GetSourceBicubic(input.fxUV).rgb;
}
else {
lowRes = GetSource(input.fxUV).rgb;
}
float3 highRes = GetSource2(input.fxUV).rgb;
lowRes += highRes - ApplyBloomThreshold(highRes);
return float4(lerp(highRes, lowRes, _BloomIntensity), 1.0);
}
float4 BloomVerticalPassFragment (Varyings input) : SV_TARGET {
float3 color = 0.0;
float offsets[] = {
-3.23076923, -1.38461538, 0.0, 1.38461538, 3.23076923
};
float weights[] = {
0.07027027, 0.31621622, 0.22702703, 0.31621622, 0.07027027
};
for (int i = 0; i < 5; i++) {
float offset = offsets[i] * GetSourceTexelSize().y;
color += GetSource(input.fxUV + float2(0.0, offset)).rgb * weights[i];
}
return float4(color, 1.0);
}
float4 CopyPassFragment (Varyings input) : SV_TARGET {
return GetSource(input.fxUV);
}
float4 ToneMappingACESPassFragment (Varyings input) : SV_TARGET {
float4 color = GetSource(input.fxUV);
color.rgb = min(color.rgb, 60.0);
color.rgb = AcesTonemap(unity_to_ACES(color.rgb));
return color;
}
float4 ToneMappingNeutralPassFragment (Varyings input) : SV_TARGET {
float4 color = GetSource(input.fxUV);
color.rgb = min(color.rgb, 60.0);
color.rgb = NeutralTonemap(color.rgb);
return color;
}
float4 ToneMappingReinhardPassFragment (Varyings input) : SV_TARGET {
float4 color = GetSource(input.fxUV);
color.rgb = min(color.rgb, 60.0);
color.rgb /= color.rgb + 1.0;
return color;
}
#endif
using UnityEngine;
using UnityEngine.Rendering;
public partial class PostFXStack {
enum Pass {
BloomAdd,
BloomHorizontal,
BloomPrefilter,
BloomPrefilterFireflies,
BloomScatter,
BloomScatterFinal,
BloomVertical,
Copy,
ToneMappingACES,
ToneMappingNeutral,
ToneMappingReinhard
}
const string bufferName = "Post FX";
const int maxBloomPyramidLevels = 16;
int
bloomBucibicUpsamplingId = Shader.PropertyToID("_BloomBicubicUpsampling"),
bloomIntensityId = Shader.PropertyToID("_BloomIntensity"),
bloomPrefilterId = Shader.PropertyToID("_BloomPrefilter"),
bloomResultId = Shader.PropertyToID("_BloomResult"),
bloomThresholdId = Shader.PropertyToID("_BloomThreshold"),
fxSourceId = Shader.PropertyToID("_PostFXSource"),
fxSource2Id = Shader.PropertyToID("_PostFXSource2");
CommandBuffer buffer = new CommandBuffer {
name = bufferName
};
ScriptableRenderContext context;
Camera camera;
PostFXSettings settings;
int bloomPyramidId;
bool useHDR;
public bool IsActive => settings != null;
public PostFXStack () {
bloomPyramidId = Shader.PropertyToID("_BloomPyramid0");
for (int i = 1; i < maxBloomPyramidLevels * 2; i++) {
Shader.PropertyToID("_BloomPyramid" + i);
}
}
public void Setup (
ScriptableRenderContext context, Camera camera, PostFXSettings settings,
bool useHDR
) {
this.useHDR = useHDR;
this.context = context;
this.camera = camera;
this.settings =
camera.cameraType <= CameraType.SceneView ? settings : null;
ApplySceneViewState();
}
public void Render (int sourceId) {
if (DoBloom(sourceId)) {
DoToneMapping(bloomResultId);
buffer.ReleaseTemporaryRT(bloomResultId);
}
else {
DoToneMapping(sourceId);
}
context.ExecuteCommandBuffer(buffer);
buffer.Clear();
}
bool DoBloom (int sourceId) {
PostFXSettings.BloomSettings bloom = settings.Bloom;
int width = camera.pixelWidth / 2, height = camera.pixelHeight / 2;
if (
bloom.maxIterations == 0 || bloom.intensity <= 0f ||
height < bloom.downscaleLimit * 2 || width < bloom.downscaleLimit * 2
) {
return false;
}
buffer.BeginSample("Bloom");
Vector4 threshold;
threshold.x = Mathf.GammaToLinearSpace(bloom.threshold);
threshold.y = threshold.x * bloom.thresholdKnee;
threshold.z = 2f * threshold.y;
threshold.w = 0.25f / (threshold.y + 0.00001f);
threshold.y -= threshold.x;
buffer.SetGlobalVector(bloomThresholdId, threshold);
RenderTextureFormat format = useHDR ?
RenderTextureFormat.DefaultHDR : RenderTextureFormat.Default;
buffer.GetTemporaryRT(
bloomPrefilterId, width, height, 0, FilterMode.Bilinear, format
);
Draw(
sourceId, bloomPrefilterId, bloom.fadeFireflies ?
Pass.BloomPrefilterFireflies : Pass.BloomPrefilter
);
width /= 2;
height /= 2;
int fromId = bloomPrefilterId, toId = bloomPyramidId + 1;
int i;
for (i = 0; i < bloom.maxIterations; i++) {
if (height < bloom.downscaleLimit || width < bloom.downscaleLimit) {
break;
}
int midId = toId - 1;
buffer.GetTemporaryRT(
midId, width, height, 0, FilterMode.Bilinear, format
);
buffer.GetTemporaryRT(
toId, width, height, 0, FilterMode.Bilinear, format
);
Draw(fromId, midId, Pass.BloomHorizontal);
Draw(midId, toId, Pass.BloomVertical);
fromId = toId;
toId += 2;
width /= 2;
height /= 2;
}
buffer.ReleaseTemporaryRT(bloomPrefilterId);
buffer.SetGlobalFloat(
bloomBucibicUpsamplingId, bloom.bicubicUpsampling ? 1f : 0f
);
Pass combinePass, finalPass;
float finalIntensity;
if (bloom.mode == PostFXSettings.BloomSettings.Mode.Additive) {
combinePass = finalPass = Pass.BloomAdd;
buffer.SetGlobalFloat(bloomIntensityId, 1f);
finalIntensity = bloom.intensity;
}
else {
combinePass = Pass.BloomScatter;
finalPass = Pass.BloomScatterFinal;
buffer.SetGlobalFloat(bloomIntensityId, bloom.scatter);
finalIntensity = Mathf.Min(bloom.intensity, 1f);
}
if (i > 1) {
buffer.ReleaseTemporaryRT(fromId - 1);
toId -= 5;
for (i -= 1; i > 0; i--) {
buffer.SetGlobalTexture(fxSource2Id, toId + 1);
Draw(fromId, toId, combinePass);
buffer.ReleaseTemporaryRT(fromId);
buffer.ReleaseTemporaryRT(toId + 1);
fromId = toId;
toId -= 2;
}
}
else {
buffer.ReleaseTemporaryRT(bloomPyramidId);
}
buffer.SetGlobalFloat(bloomIntensityId, finalIntensity);
buffer.SetGlobalTexture(fxSource2Id, sourceId);
buffer.GetTemporaryRT(
bloomResultId, camera.pixelWidth, camera.pixelHeight, 0,
FilterMode.Bilinear, format
);
Draw(fromId, bloomResultId, finalPass);
buffer.ReleaseTemporaryRT(fromId);
buffer.EndSample("Bloom");
return true;
}
void DoToneMapping(int sourceId) {
PostFXSettings.ToneMappingSettings.Mode mode = settings.ToneMapping.mode;
Pass pass = mode < 0 ? Pass.Copy : Pass.ToneMappingACES + (int)mode;
Draw(sourceId, BuiltinRenderTextureType.CameraTarget, pass);
}
void Draw (
RenderTargetIdentifier from, RenderTargetIdentifier to, Pass pass
) {
buffer.SetGlobalTexture(fxSourceId, from);
buffer.SetRenderTarget(
to, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store
);
buffer.DrawProcedural(
Matrix4x4.identity, settings.Material, (int)pass,
MeshTopology.Triangles, 3
);
}
}