非HDR开启bloom的效果
主要是URP作者的教程
工程源码在我的git中:我的git工程网址
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
partial class PostFXStack
{
private const string bufferName = "Post FX";
CommandBuffer buffer = new CommandBuffer()
{
name = bufferName
};
private ScriptableRenderContext context;
private Camera camera;
private PostFXSettings settings;
private int fxSourceId = Shader.PropertyToID("_PostFXSource");
private int fxSource2Id = Shader.PropertyToID("_PostFXSource2");
private int bloomBucibicUpsamplingId = Shader.PropertyToID("_BloomBicubicUpsampling");
private int bloomPrefilterId = Shader.PropertyToID("_BloomPrefilter");
private int bloomThresholdId = Shader.PropertyToID("_BloomThreshold");
private int bloomIntensityId = Shader.PropertyToID("_BloomIntensity");
public bool IsActive => settings != null;
private const int maxBloomPyramidLevels = 16;
private int bloomPyramidId;
enum Pass
{
// BloomPrefilter,
// BloomCombine,
BloomVertical,
BloomHorizontal,
BloomCombine,
BloomPrefilter,
Copy,
}
public PostFXStack()
{
bloomPyramidId = Shader.PropertyToID("_BloomPyramid0");
for (int i = 0; i < maxBloomPyramidLevels * 2; i++)
{
// id的结果是 申请的顺序 +1的结果
Shader.PropertyToID("_BloomPyramid"+i);
}
}
void DoBloom(int sourceId)
{
buffer.BeginSample("Bloom");
PostFXSettings.BloomSettings bloom = settings.Bloom;
int width = camera.pixelWidth / 2, height = camera.pixelHeight / 2;
Vector4 threshold;
threshold.x = Mathf.GammaToLinearSpace(bloom.threshold);
threshold.y = threshold.x * bloom.thresholdKnee;
threshold.z = threshold.y * 2f;
threshold.w = 0.25f /( threshold.y + 0.00001f);
threshold.y -= threshold.x;
buffer.SetGlobalVector(bloomThresholdId,threshold);
RenderTextureFormat format = RenderTextureFormat.Default;
buffer.GetTemporaryRT(bloomPrefilterId,width,height,0,FilterMode.Bilinear,format);
Draw(sourceId,bloomPrefilterId,Pass.BloomPrefilter);
width /= 2;
height /= 2;
int formId = bloomPrefilterId;
int toId = bloomPyramidId + 1;
int i;
for ( i = 0; i < bloom.maxIterations; i++)
{
if (bloom.maxIterations == 0 || bloom.intensity <=0 || height < bloom.downscaleLimit * 2 || width < bloom.downscaleLimit * 2)
{
break;
}
int midId = toId - 1;
buffer.GetTemporaryRT(midId,width,height,0,FilterMode.Bilinear,format);
buffer.GetTemporaryRT(toId,width,height,0,FilterMode.Bilinear,format);
Draw(formId, midId,Pass.BloomHorizontal);
Draw(midId,toId,Pass.BloomVertical);
formId = toId;
toId += 2;
width /= 2;
height /= 2;
}
buffer.ReleaseTemporaryRT(bloomPrefilterId);
buffer.SetGlobalFloat(bloomBucibicUpsamplingId,bloom.bicubicUpsampling ? 1f : 0f);
buffer.SetGlobalFloat(bloomIntensityId,bloom.intensity);
//Draw(formId,BuiltinRenderTextureType.CameraTarget,Pass.BloomHorizontal);
if (i>1)
{
buffer.ReleaseTemporaryRT(formId - 1);
toId -= 5;
for (i -= 1; i > 0; i--)
{
buffer.SetGlobalTexture(fxSource2Id,toId+1);
Draw(formId,toId,Pass.BloomCombine);
buffer.ReleaseTemporaryRT(formId);
buffer.ReleaseTemporaryRT(toId + 1);
formId = toId;
toId -= 2;
}
}
else
{
buffer.ReleaseTemporaryRT(bloomPyramidId);
}
buffer.SetGlobalTexture(fxSource2Id,sourceId);
Draw(formId,BuiltinRenderTextureType.CameraTarget,Pass.BloomCombine);
buffer.ReleaseTemporaryRT(formId);
buffer.EndSample("Bloom");
}
/// <summary>
/// 用来替换 buff.Blit 的函数, 比Blit 更高效
/// </summary>
/// <param name="from"></param>
/// <param name="to"></param>
/// <param name="pass"></param>
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);
}
public void Setup(ScriptableRenderContext context, Camera camera, PostFXSettings settings)
{
this.context = context;
this.camera = camera;
this.settings = camera.cameraType <= CameraType.SceneView ? settings : null;
ApplySceneViewState();
}
public void Render(int sourceId)
{
//Draw(sourceId,BuiltinRenderTextureType.CameraTarget,Pass.Copy);
DoBloom(sourceId);
//buffer.Blit(sourceId,BuiltinRenderTextureType.CameraTarget);
context.ExecuteCommandBuffer(buffer);
buffer.Clear();
}
// void ApplySceneViewState()
// {
// if (camera.cameraType == CameraType.SceneView && !SceneView.currentDrawingSceneView.sceneViewState.showImageEffects )
// {
// settings = null;
// }
// }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
[CreateAssetMenu(menuName = "Rendering/Custom Post FX Settings")]
public class PostFXSettings : ScriptableObject
{
[SerializeField]
private Shader shader = default;
[System.Serializable]
public struct BloomSettings
{
[Range(0f,16f)]
public int maxIterations;
[FormerlySerializedAs("downscaleLimit")] [Min(1f)]
public int downscaleLimit;
// 是否使用三线性插值滤波
public bool bicubicUpsampling;
[Min(0f)]
public float threshold;
[Range(0f,1f)]
public float thresholdKnee;
[Min(0f)]
public float intensity;
}
[SerializeField]
private BloomSettings bloom = default;
public BloomSettings Bloom => bloom;
[System.NonSerialized]
private Material material;
public Material Material
{
get
{
if (material==null && shader != null)
{
material = new Material(shader);
material.hideFlags = HideFlags.HideAndDontSave;
}
return material;
}
}
}
Shader "Hidden/CustomRP/PostFXStack"
{
SubShader
{
Cull Off
ZTest Always
Zwrite Off
HLSLINCLUDE
#include "../ShaderLibrary/Common.hlsl"
#include "PostFXStackPasses.hlsl"
ENDHLSL
Pass
{
Name "Bloom Vertical"
HLSLPROGRAM
#pragma target 3.5
#pragma vertex DefaultPassVertex
#pragma fragment BloomVerticalPassFragment
ENDHLSL
}
//
Pass
{
Name "Bloom Horizontal"
HLSLPROGRAM
#pragma target 3.5
#pragma vertex DefaultPassVertex
#pragma fragment BloomHorizontalPassFragment
ENDHLSL
}
Pass
{
Name "Bloom Combine"
HLSLPROGRAM
#pragma target 3.5
#pragma vertex DefaultPassVertex
#pragma fragment BloomCombinePassFragment
ENDHLSL
}
Pass
{
Name "Bloom Prefilter"
HLSLPROGRAM
#pragma target 3.5
#pragma vertex DefaultPassVertex
#pragma fragment BloomprefilterPassFragment
ENDHLSL
}
Pass
{
Name "Copy"
HLSLPROGRAM
#pragma target 3.5
#pragma vertex DefaultPassVertex
#pragma fragment CopyPassFragment
ENDHLSL
}
}
}
// unity 标准输入库
#ifndef CUSTOM_POST_FX_STACE_INCLUDE
#define CUSTOM_POST_FX_STACE_INCLUDE
#include "../../../Library/PackageCache/com.unity.render-pipelines.core@10.3.2/ShaderLibrary/Filtering.hlsl"
TEXTURE2D(_PostFXSource);
TEXTURE2D(_PostFXSource2);
SAMPLER(sampler_linear_clamp);
float4 _PostFXSource_TexelSize;
bool _BloomBicubicUpsampling;
float4 _BloomThreshold;
float _BloomIntensity;
float4 GetSourceTexelSize()
{
return _PostFXSource_TexelSize;
}
float4 GetSource(float2 screenUV)
{
return SAMPLE_TEXTURE2D_LOD(_PostFXSource,sampler_linear_clamp,screenUV,0);
}
float4 GetSource2(float2 screenUV)
{
return SAMPLE_TEXTURE2D_LOD(_PostFXSource2,sampler_linear_clamp,screenUV,0);
}
float4 GetSourceBicubic(float2 screenUV)
{
return SampleTexture2DBicubic(TEXTURE2D_ARGS(_PostFXSource,sampler_linear_clamp),screenUV,_PostFXSource_TexelSize.zwxy,1.0,1.0);
}
struct Varyings
{
float4 positionCS : SV_POSITION;
float2 screenUV : VAR_SCREEN_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.screenUV = float2(vertexID <= 1 ? 0.0 : 2.0, vertexID == 1 ? 2.0 : 0.0);
// y 值上下反转
if(_ProjectionParams.x < 0.0)
{
output.screenUV.y = 1.0 - output.screenUV.y;
}
return output;
}
float4 CopyPassFragment(Varyings input) : SV_TARGET
{
// 用于调试
//return float4(input.screenUV,0.0,1.0);
return GetSource(input.screenUV);
}
//----------------
//----------------
//----------------
//----------------
// 水平方向的模糊
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.screenUV + float2(offset,0.0)).rgb * weights[i];
}
return float4(color,1.0);
}
// 在vertical 中可以缩减到5次, 但是在 BloomHorizontal中不能,因为已经在该pass中使用了双线性过滤
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.screenUV + float2(0.0,offset)).rgb * weights[i];
}
return float4(color,1.0);
}
float4 BloomCombinePassFragment(Varyings input):SV_TARGET
{
float3 lowRes;
if (_BloomBicubicUpsampling)
{
lowRes = GetSourceBicubic(input.screenUV).rgb;
}
else
{
lowRes = GetSource(input.screenUV).rgb;
}
float3 hightRes = GetSource2(input.screenUV).rgb;
return float4(lowRes * _BloomIntensity + hightRes,1.0);
}
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.screenUV).rgb);
return float4(color,1.0);
}
#endif