Files
Cielonos/Assets/PotaToon/Runtime/Scripts/PotaToonPostProcessPass.cs
SoulliesOfficial f7af60351b 阶段性完成
2025-12-08 05:27:53 -05:00

997 lines
53 KiB
C#

using System;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using UnityEngine.Experimental.Rendering;
#if UNITY_6000_0_OR_NEWER
using UnityEngine.Rendering.RenderGraphModule;
#endif
using Utils = PotaToon.PotaToonPostProcessUtils;
namespace PotaToon
{
public class PotaToonPostProcessPass : ScriptableRenderPass
{
private class PostProcessData
{
public bool enabled;
public bool screenOutline;
public bool screenOutlineExcludeInnerLines;
public Color screenOutlineColor;
public float screenOutlineThickness;
public float screenOutlineEdgeStrength;
public bool gammaAdjust;
public PotaToonToneMapping toneMappingMode;
public HableCurve hableCurve;
public float postExposure; // Brightness
public Vector4 hueSatCon;
public Color colorFilter;
public Vector3 lmsColorBalance;
public float screenRimWidth;
public Color screenRimColor;
}
private struct BloomData
{
public bool enabled;
public float threshold;
public float intensity;
public float scatter;
public float clamp;
public Color tint;
public bool highQualityFiltering;
public BloomDownscaleMode downscale;
public int maxIterations;
public Texture dirtTexture;
public float dirtIntensity;
public void SetBloomDataForCharacter(PotaToon volume)
{
enabled = volume.charBloom.value && volume.intensity.value > 0;
threshold = volume.threshold.value;
intensity = volume.intensity.value;
scatter = volume.scatter.value;
clamp = volume.clamp.value;
tint = volume.tint.value;
highQualityFiltering = volume.highQualityFiltering.value;
downscale = volume.downscale.value;
maxIterations = volume.maxIterations.value;
dirtTexture = volume.dirtTexture.value;
dirtIntensity = volume.dirtIntensity.value;
}
public void SetBloomDataForEnvironment(PotaToon volume)
{
enabled = volume.envBloom.value && volume.envBloomIntensity.value > 0;
threshold = volume.envBloomThreshold.value;
intensity = volume.envBloomIntensity.value;
scatter = volume.envBloomScatter.value;
clamp = volume.envBloomClamp.value;
tint = volume.envBloomTint.value;
highQualityFiltering = volume.envBloomHighQualityFiltering.value;
downscale = volume.envBloomDownscale.value;
maxIterations = volume.envBloomMaxIterations.value;
dirtTexture = volume.envBloomDirtTexture.value;
dirtIntensity = volume.envBloomDirtIntensity.value;
}
}
private static class ShaderConstants
{
public static readonly int _Params = Shader.PropertyToID("_Params");
public static readonly int _SourceTexLowMip = Shader.PropertyToID("_SourceTexLowMip");
public static readonly int _Bloom_Params = Shader.PropertyToID("_Bloom_Params");
public static readonly int _Bloom_Texture = Shader.PropertyToID("_Bloom_Texture");
public static readonly int _LensDirt_Texture = Shader.PropertyToID("_LensDirt_Texture");
public static readonly int _LensDirt_Params = Shader.PropertyToID("_LensDirt_Params");
public static readonly int _LensDirt_Intensity = Shader.PropertyToID("_LensDirt_Intensity");
public static int[] _BloomMipUp;
public static int[] _BloomMipDown;
}
private static class CustomShaderKeywordStrings
{
public const string PotaToonCharacterBloom = "_POTA_TOON_CHARACTER_BLOOM";
}
private static class ShaderPassIDs
{
public static readonly int character = 0;
public static readonly int environment = 1;
public static readonly int screenOutline = 2;
public static readonly int screenRim = 3;
}
private const int k_MaxPyramidSize = 16;
private ProfilingSampler[] m_ProfilingSamplers = new ProfilingSampler[2];
private Material m_CharMaterial;
private Material m_EnvMaterial;
private MaterialPropertyBlock m_PropertyBlock;
private Texture m_TonyMcMapfaceTexture;
private RTHandle m_CopiedColor;
private PostProcessData m_CharPostProcessData;
private PostProcessData m_EnvPostProcessData;
private HableCurve m_CharHableCurve;
private HableCurve m_EnvHableCurve;
internal static HableCurve s_GTHableCurve;
private RenderTextureDescriptor m_Descriptor;
private readonly GraphicsFormat m_DefaultColorFormat; // The default format for post-processing, follows back-buffer format in URP.
private BloomData m_CharBloom;
private BloomData m_EnvBloom;
private RTHandle[] m_BloomMipDown;
private RTHandle[] m_BloomMipUp;
private Material m_CharBloomMaterial;
private Material m_EnvBloomMaterial;
private Material[] m_CharBloomUpsample;
private Material[] m_EnvBloomUpsample;
#if UNITY_6000_0_OR_NEWER
private TextureHandle[] _BloomMipUp;
private TextureHandle[] _BloomMipDown;
private BloomMaterialParams m_BloomParamsPrev;
#endif
public PotaToonPostProcessPass(string featureName)
{
renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing;
var uberPostMat = Resources.Load<Material>("PotaToonUberPost");
var bloomMat = Resources.Load<Material>("PotaToonBloom");
var uberPostShader = uberPostMat != null ? uberPostMat.shader : Shader.Find("Hidden/Universal Render Pipeline/UberPost");
var bloomShader = bloomMat != null ? bloomMat.shader : Shader.Find("Hidden/Universal Render Pipeline/Bloom");
m_CharMaterial = Utils.Load(uberPostShader);
m_EnvMaterial = Utils.Load(uberPostShader);
m_TonyMcMapfaceTexture = Resources.Load<Texture>("TonyMcMapface");
m_ProfilingSamplers[0] = new ProfilingSampler(featureName);
m_ProfilingSamplers[1] = new ProfilingSampler("PotaToon Copy Color");
m_PropertyBlock = new MaterialPropertyBlock();
m_CharPostProcessData = new PostProcessData();
m_EnvPostProcessData = new PostProcessData();
m_CharHableCurve = new HableCurve();
m_EnvHableCurve = new HableCurve();
s_GTHableCurve = new HableCurve();
s_GTHableCurve.Init(0f, 0.312f, 0.532f, 2.136752f, 0f, 1.15f);
// Bloom - Copied from URP
ShaderConstants._BloomMipUp = new int[k_MaxPyramidSize];
ShaderConstants._BloomMipDown = new int[k_MaxPyramidSize];
m_BloomMipUp = new RTHandle[k_MaxPyramidSize];
m_BloomMipDown = new RTHandle[k_MaxPyramidSize];
#if UNITY_6000_0_OR_NEWER
_BloomMipUp = new TextureHandle[k_MaxPyramidSize];
_BloomMipDown = new TextureHandle[k_MaxPyramidSize];
#endif
for (int i = 0; i < k_MaxPyramidSize; i++)
{
ShaderConstants._BloomMipUp[i] = Shader.PropertyToID("_BloomMipUp" + i);
ShaderConstants._BloomMipDown[i] = Shader.PropertyToID("_BloomMipDown" + i);
// Get name, will get Allocated with descriptor later
m_BloomMipUp[i] = RTHandles.Alloc(ShaderConstants._BloomMipUp[i], name: "_BloomMipUp" + i);
m_BloomMipDown[i] = RTHandles.Alloc(ShaderConstants._BloomMipDown[i], name: "_BloomMipDown" + i);
}
m_CharBloomMaterial = Utils.Load(bloomShader);
m_EnvBloomMaterial = Utils.Load(bloomShader);
m_CharBloomUpsample = new Material[k_MaxPyramidSize];
m_EnvBloomUpsample = new Material[k_MaxPyramidSize];
for (uint i = 0; i < k_MaxPyramidSize; ++i)
{
m_CharBloomUpsample[i] = Utils.Load(bloomShader);
m_EnvBloomUpsample[i] = Utils.Load(bloomShader);
}
m_DefaultColorFormat = Utils.GetDefaultColorFormat();
}
public void Setup(PotaToon volume)
{
m_CharPostProcessData.enabled = volume.charPostProcessing.value;
m_CharPostProcessData.screenOutline = volume.charScreenOutline.value;
m_CharPostProcessData.screenOutlineExcludeInnerLines = volume.charScreenOutlineExcludeInnerLines.value;
m_CharPostProcessData.screenOutlineColor = volume.charScreenOutlineColor.value;
m_CharPostProcessData.screenOutlineThickness = volume.charScreenOutlineThickness.value;
m_CharPostProcessData.screenOutlineEdgeStrength = volume.charScreenOutlineEdgeStrength.value;
m_CharPostProcessData.gammaAdjust = volume.charGammaAdjust.value;
m_CharPostProcessData.toneMappingMode = volume.charToneMapping.value;
m_CharPostProcessData.hableCurve = m_CharHableCurve;
m_CharPostProcessData.hableCurve.Init(
volume.charToeStrength.value,
volume.charToeLength.value,
volume.charShoulderStrength.value,
volume.charShoulderLength.value,
volume.charShoulderAngle.value,
volume.charGamma.value
);
m_CharPostProcessData.postExposure = volume.charPostExposure.value;
m_CharPostProcessData.hueSatCon = new Vector4(volume.charHueShift.value / 360f, volume.charSaturation.value / 100f + 1f, volume.charContrast.value / 100f + 1f, 0f);
m_CharPostProcessData.colorFilter = volume.charColorFilter.value;
m_CharPostProcessData.lmsColorBalance = ColorUtils.ColorBalanceToLMSCoeffs(volume.whiteBalanceTemperature.value, volume.whiteBalanceTint.value);
m_CharPostProcessData.screenRimWidth = volume.screenRimWidth.value * 0.025f;
m_CharPostProcessData.screenRimColor = volume.screenRimColor.value;
m_CharPostProcessData.screenRimColor.a = Mathf.Max(1f, CharacterShadowUtils.shadowCamera.maxScreenRimDistance);
m_EnvPostProcessData.enabled = volume.envPostProcessing.value;
m_EnvPostProcessData.toneMappingMode = volume.envToneMapping.value;
m_EnvPostProcessData.hableCurve = m_EnvHableCurve;
m_EnvPostProcessData.hableCurve.Init(
volume.envToeStrength.value,
volume.envToeLength.value,
volume.envShoulderStrength.value,
volume.envShoulderLength.value,
volume.envShoulderAngle.value,
volume.envGamma.value
);
m_EnvPostProcessData.postExposure = volume.envPostExposure.value;
m_EnvPostProcessData.hueSatCon = new Vector4(volume.envHueShift.value / 360f, volume.envSaturation.value / 100f + 1f, volume.envContrast.value / 100f + 1f, 0f);
m_EnvPostProcessData.colorFilter = volume.envColorFilter.value;
m_EnvPostProcessData.lmsColorBalance = ColorUtils.ColorBalanceToLMSCoeffs(volume.envWhiteBalanceTemperature.value, volume.envWhiteBalanceTint.value);
m_CharBloom.SetBloomDataForCharacter(volume);
m_EnvBloom.SetBloomDataForEnvironment(volume);
Shader.SetKeyword(PotaToonGlobalKeywords.ScreenRim, m_CharPostProcessData.screenRimWidth > 0f);
Shader.SetGlobalFloat(ShaderIDs._ScreenRimWidth, m_CharPostProcessData.screenRimWidth);
Shader.SetGlobalVector(ShaderIDs._ScreenRimColor, m_CharPostProcessData.screenRimColor);
}
public void Dispose()
{
m_CopiedColor?.Release();
CoreUtils.Destroy(m_CharMaterial);
CoreUtils.Destroy(m_EnvMaterial);
CoreUtils.Destroy(m_CharBloomMaterial);
CoreUtils.Destroy(m_EnvBloomMaterial);
foreach (var handle in m_BloomMipDown)
handle?.Release();
foreach (var handle in m_BloomMipUp)
handle?.Release();
for (uint i = 0; i < k_MaxPyramidSize; ++i)
{
CoreUtils.Destroy(m_CharBloomUpsample[i]);
CoreUtils.Destroy(m_EnvBloomUpsample[i]);
}
}
#if UNITY_6000_0_OR_NEWER
[Obsolete]
#endif
public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
{
if (!m_CharPostProcessData.enabled && !m_EnvPostProcessData.enabled)
return;
m_Descriptor = renderingData.cameraData.cameraTargetDescriptor;
var colorCopyDescriptor = renderingData.cameraData.cameraTargetDescriptor;
colorCopyDescriptor.depthBufferBits = (int)DepthBits.None;
RenderingUtils.ReAllocateIfNeeded(ref m_CopiedColor, colorCopyDescriptor, name: "_PotaToonTempCopiedColor", filterMode:FilterMode.Bilinear);
}
#if UNITY_6000_0_OR_NEWER
[Obsolete]
#endif
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
if (!m_CharPostProcessData.enabled && !m_EnvPostProcessData.enabled)
return;
var cmd = CommandBufferPool.Get();
#if UNITY_2021_3
var src = renderingData.cameraData.renderer.cameraColorTarget;
m_CharMaterial?.EnableKeyword("_USE_DRAW_PROCEDURAL");
m_EnvMaterial?.EnableKeyword("_USE_DRAW_PROCEDURAL");
#else
var src = renderingData.cameraData.renderer.cameraColorTargetHandle;
#endif
bool characterPostProcessEnabled = m_CharMaterial != null && m_CharPostProcessData.enabled;
bool environmentPostProcessEnabled = m_EnvMaterial != null && m_EnvPostProcessData.enabled;
bool screenOutlineEnabled = characterPostProcessEnabled && m_CharPostProcessData.screenOutline;
using (new ProfilingScope(cmd, m_ProfilingSamplers[0]))
{
DisableAllBloomKeywords(m_EnvMaterial);
if (environmentPostProcessEnabled)
{
if (m_EnvBloom.enabled)
{
using (new ProfilingScope(cmd, new ProfilingSampler("PotaToon Environment Bloom")))
#if UNITY_6000_0_OR_NEWER
SetupBloom(cmd, renderingData.cameraData.renderer.cameraColorTargetHandle, m_EnvMaterial, in m_EnvBloom, renderingData.cameraData.isAlphaOutputEnabled, false, false);
#else
#if UNITY_2021_3
SetupBloom(cmd, renderingData.cameraData.renderer.cameraColorTarget, m_EnvMaterial, in m_EnvBloom, false, false, false);
#else
SetupBloom(cmd, renderingData.cameraData.renderer.cameraColorTargetHandle, m_EnvMaterial, in m_EnvBloom, false, false, false);
#endif
#endif
}
using (new ProfilingScope(cmd, m_ProfilingSamplers[0]))
{
#if UNITY_2021_3
Blitter2021.BlitCameraTexture(cmd, src, m_CopiedColor);
#else
Blitter.BlitCameraTexture(cmd, src, m_CopiedColor);
#endif
CoreUtils.SetRenderTarget(cmd, src);
m_PropertyBlock.SetTexture(ShaderIDs._BlitTexture, m_CopiedColor);
m_PropertyBlock.SetVector(ShaderIDs._BlitScaleBias, new Vector4(1, 1, 0, 0));
m_PropertyBlock.SetTexture(ShaderIDs._TonyMcMapfaceLut, m_TonyMcMapfaceTexture);
Utils.SetMaterialToneMappingKeywords(m_EnvMaterial, m_EnvPostProcessData.toneMappingMode, m_EnvPostProcessData.hableCurve);
m_EnvMaterial.SetFloat(ShaderIDs._PostExposure, m_EnvPostProcessData.postExposure);
m_EnvMaterial.SetVector(ShaderIDs._HueSatCon, m_EnvPostProcessData.hueSatCon);
m_EnvMaterial.SetColor(ShaderIDs._ColorFilter, m_EnvPostProcessData.colorFilter);
m_EnvMaterial.SetVector(ShaderIDs._ColorBalance, m_EnvPostProcessData.lmsColorBalance);
#if UNITY_2021_3
CoreUtils2021.DrawFullScreen(cmd, m_EnvMaterial, m_PropertyBlock, ShaderPassIDs.environment);
#else
CoreUtils.DrawFullScreen(cmd, m_EnvMaterial, m_PropertyBlock, ShaderPassIDs.environment);
#endif
}
}
DisableAllBloomKeywords(m_CharMaterial);
if (characterPostProcessEnabled)
{
if (screenOutlineEnabled)
{
m_CharMaterial.SetFloat(ShaderIDs._ScreenOutlineThickness, m_CharPostProcessData.screenOutlineThickness);
m_CharMaterial.SetFloat(ShaderIDs._ScreenOutlineEdgeStrength, m_CharPostProcessData.screenOutlineEdgeStrength);
m_CharMaterial.SetColor(ShaderIDs._ScreenOutlineColor, m_CharPostProcessData.screenOutlineColor);
m_CharMaterial.SetKeyword(new LocalKeyword(m_CharMaterial.shader, "_EXCLUDE_INNER_SCREEN_OUTLINES"), m_CharPostProcessData.screenOutlineExcludeInnerLines);
#if UNITY_2021_3
CoreUtils2021.DrawFullScreen(cmd, m_CharMaterial, m_PropertyBlock, ShaderPassIDs.screenOutline);
#else
CoreUtils.DrawFullScreen(cmd, m_CharMaterial, m_PropertyBlock, ShaderPassIDs.screenOutline);
#endif
}
if (m_CharBloom.enabled)
{
using (new ProfilingScope(cmd, new ProfilingSampler("PotaToon Character Bloom")))
#if UNITY_6000_0_OR_NEWER
SetupBloom(cmd, renderingData.cameraData.renderer.cameraColorTargetHandle, m_CharMaterial, in m_CharBloom, renderingData.cameraData.isAlphaOutputEnabled, true, environmentPostProcessEnabled && m_EnvBloom.enabled);
#else
#if UNITY_2021_3
SetupBloom(cmd, renderingData.cameraData.renderer.cameraColorTarget, m_CharMaterial, in m_CharBloom, false, true, m_EnvBloom.enabled);
#else
SetupBloom(cmd, renderingData.cameraData.renderer.cameraColorTargetHandle, m_CharMaterial, in m_CharBloom, false, true, m_EnvBloom.enabled);
#endif
#endif
}
using (new ProfilingScope(cmd, m_ProfilingSamplers[0]))
{
#if UNITY_2021_3
Blitter2021.BlitCameraTexture(cmd, src, m_CopiedColor);
#else
Blitter.BlitCameraTexture(cmd, src, m_CopiedColor);
#endif
CoreUtils.SetRenderTarget(cmd, src);
m_PropertyBlock.SetTexture(ShaderIDs._BlitTexture, m_CopiedColor);
m_PropertyBlock.SetVector(ShaderIDs._BlitScaleBias, new Vector4(1, 1, 0, 0));
m_PropertyBlock.SetTexture(ShaderIDs._TonyMcMapfaceLut, m_TonyMcMapfaceTexture);
Utils.SetMaterialToneMappingKeywords(m_CharMaterial, m_CharPostProcessData.toneMappingMode, m_CharPostProcessData.hableCurve);
m_CharMaterial.SetFloat(ShaderIDs._CharGammaAdjust, m_CharPostProcessData.gammaAdjust ? 1f : 0f);
m_CharMaterial.SetFloat(ShaderIDs._PostExposure, m_CharPostProcessData.postExposure);
m_CharMaterial.SetVector(ShaderIDs._HueSatCon, m_CharPostProcessData.hueSatCon);
m_CharMaterial.SetColor(ShaderIDs._ColorFilter, m_CharPostProcessData.colorFilter);
m_CharMaterial.SetVector(ShaderIDs._ColorBalance, m_CharPostProcessData.lmsColorBalance);
m_CharMaterial.SetFloat(ShaderIDs._ScreenRimWidth, m_CharPostProcessData.screenRimWidth);
m_CharMaterial.SetVector(ShaderIDs._ScreenRimColor, m_CharPostProcessData.screenRimColor);
#if UNITY_2021_3
CoreUtils2021.DrawFullScreen(cmd, m_CharMaterial, m_PropertyBlock, ShaderPassIDs.character);
#else
CoreUtils.DrawFullScreen(cmd, m_CharMaterial, m_PropertyBlock, ShaderPassIDs.character);
#endif
}
}
}
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
#region Bloom
private void DisableAllBloomKeywords(Material material)
{
if (material != null)
{
material.DisableKeyword(ShaderKeywordStrings.BloomLQ);
material.DisableKeyword(ShaderKeywordStrings.BloomHQ);
material.DisableKeyword(ShaderKeywordStrings.BloomLQDirt);
material.DisableKeyword(ShaderKeywordStrings.BloomHQDirt);
}
}
#if UNITY_2021_3
private void SetupBloom(CommandBuffer cmd, RenderTargetIdentifier source, Material uberMaterial, in BloomData bloomData, bool enableAlphaOutput, bool isCharacterBloom, bool hasResourcesCreated)
#else
private void SetupBloom(CommandBuffer cmd, RTHandle source, Material uberMaterial, in BloomData bloomData, bool enableAlphaOutput, bool isCharacterBloom, bool hasResourcesCreated)
#endif
{
// Start at half-res
int downres = 1;
switch (bloomData.downscale)
{
case BloomDownscaleMode.Half:
downres = 1;
break;
case BloomDownscaleMode.Quarter:
downres = 2;
break;
default:
throw new System.ArgumentOutOfRangeException();
}
int tw = m_Descriptor.width >> downres;
int th = m_Descriptor.height >> downres;
// Determine the iteration count
int maxSize = Mathf.Max(tw, th);
int iterations = Mathf.FloorToInt(Mathf.Log(maxSize, 2f) - 1);
int mipCount = Mathf.Clamp(iterations, 1, bloomData.maxIterations);
// Pre-filtering parameters
float clamp = bloomData.clamp;
float threshold = Mathf.GammaToLinearSpace(bloomData.threshold);
float thresholdKnee = threshold * 0.5f; // Hardcoded soft knee
// Material setup
float scatter = Mathf.Lerp(0.05f, 0.95f, bloomData.scatter);
var bloomMaterial = isCharacterBloom ? m_CharBloomMaterial : m_EnvBloomMaterial;
bloomMaterial.SetVector(ShaderConstants._Params, new Vector4(scatter, clamp, threshold, thresholdKnee));
CoreUtils.SetKeyword(bloomMaterial, ShaderKeywordStrings.BloomHQ, bloomData.highQualityFiltering);
#if UNITY_6000_0_OR_NEWER
CoreUtils.SetKeyword(bloomMaterial, ShaderKeywordStrings._ENABLE_ALPHA_OUTPUT, enableAlphaOutput);
#endif
// Prefilter
CoreUtils.SetKeyword(bloomMaterial, CustomShaderKeywordStrings.PotaToonCharacterBloom, isCharacterBloom);
if (!hasResourcesCreated)
{
var desc = Utils.GetCompatibleDescriptor(m_Descriptor, tw, th, m_DefaultColorFormat);
for (int i = 0; i < mipCount; i++)
{
#if UNITY_6000_0_OR_NEWER
RenderingUtils.ReAllocateHandleIfNeeded(ref m_BloomMipUp[i], desc, FilterMode.Bilinear, TextureWrapMode.Clamp, name: m_BloomMipUp[i].name);
RenderingUtils.ReAllocateHandleIfNeeded(ref m_BloomMipDown[i], desc, FilterMode.Bilinear, TextureWrapMode.Clamp, name: m_BloomMipDown[i].name);
#else
RenderingUtils.ReAllocateIfNeeded(ref m_BloomMipUp[i], desc, FilterMode.Bilinear, TextureWrapMode.Clamp, name: m_BloomMipUp[i].name);
RenderingUtils.ReAllocateIfNeeded(ref m_BloomMipDown[i], desc, FilterMode.Bilinear, TextureWrapMode.Clamp, name: m_BloomMipDown[i].name);
#endif
desc.width = Mathf.Max(1, desc.width >> 1);
desc.height = Mathf.Max(1, desc.height >> 1);
}
}
#if UNITY_2021_3
Blitter2021.BlitCameraTexture(cmd, source, m_BloomMipDown[0], RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store, material:bloomMaterial, pass:0);
#else
Blitter.BlitCameraTexture(cmd, source, m_BloomMipDown[0], RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store, bloomMaterial, 0);
#endif
// Downsample - gaussian pyramid
var lastDown = m_BloomMipDown[0];
for (int i = 1; i < mipCount; i++)
{
// Classic two pass gaussian blur - use mipUp as a temporary target
// First pass does 2x downsampling + 9-tap gaussian
// Second pass does 9-tap gaussian using a 5-tap filter + bilinear filtering
#if UNITY_2021_3
Blitter2021.BlitCameraTexture(cmd, lastDown, m_BloomMipUp[i], RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store, material:bloomMaterial, pass:1);
Blitter2021.BlitCameraTexture(cmd, m_BloomMipUp[i], m_BloomMipDown[i], RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store, material:bloomMaterial, pass:2);
#else
Blitter.BlitCameraTexture(cmd, lastDown, m_BloomMipUp[i], RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store, bloomMaterial, 1);
Blitter.BlitCameraTexture(cmd, m_BloomMipUp[i], m_BloomMipDown[i], RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store, bloomMaterial, 2);
#endif
lastDown = m_BloomMipDown[i];
}
// Upsample (bilinear by default, HQ filtering does bicubic instead
for (int i = mipCount - 2; i >= 0; i--)
{
var lowMip = (i == mipCount - 2) ? m_BloomMipDown[i + 1] : m_BloomMipUp[i + 1];
var highMip = m_BloomMipDown[i];
var dst = m_BloomMipUp[i];
cmd.SetGlobalTexture(ShaderConstants._SourceTexLowMip, lowMip);
#if UNITY_2021_3
Blitter2021.BlitCameraTexture(cmd, highMip, dst, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store, material:bloomMaterial, pass:3);
#else
Blitter.BlitCameraTexture(cmd, highMip, dst, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store, bloomMaterial, 3);
#endif
}
// Setup bloom on uber
var tint = bloomData.tint.linear;
var luma = ColorUtils.Luminance(tint);
tint = luma > 0f ? tint * (1f / luma) : Color.white;
var bloomParams = new Vector4(bloomData.intensity, tint.r, tint.g, tint.b);
uberMaterial.SetVector(ShaderConstants._Bloom_Params, bloomParams);
cmd.SetGlobalTexture(ShaderConstants._Bloom_Texture, m_BloomMipUp[0]);
// Setup lens dirtiness on uber
// Keep the aspect ratio correct & center the dirt texture, we don't want it to be
// stretched or squashed
var dirtTexture = bloomData.dirtTexture == null ? Texture2D.blackTexture : bloomData.dirtTexture;
float dirtRatio = dirtTexture.width / (float)dirtTexture.height;
float screenRatio = m_Descriptor.width / (float)m_Descriptor.height;
var dirtScaleOffset = new Vector4(1f, 1f, 0f, 0f);
float dirtIntensity = bloomData.dirtIntensity;
if (dirtRatio > screenRatio)
{
dirtScaleOffset.x = screenRatio / dirtRatio;
dirtScaleOffset.z = (1f - dirtScaleOffset.x) * 0.5f;
}
else if (screenRatio > dirtRatio)
{
dirtScaleOffset.y = dirtRatio / screenRatio;
dirtScaleOffset.w = (1f - dirtScaleOffset.y) * 0.5f;
}
uberMaterial.SetVector(ShaderConstants._LensDirt_Params, dirtScaleOffset);
uberMaterial.SetFloat(ShaderConstants._LensDirt_Intensity, dirtIntensity);
uberMaterial.SetTexture(ShaderConstants._LensDirt_Texture, dirtTexture);
// Keyword setup - a bit convoluted as we're trying to save some variants in Uber...
if (bloomData.highQualityFiltering)
uberMaterial.EnableKeyword(dirtIntensity > 0f ? ShaderKeywordStrings.BloomHQDirt : ShaderKeywordStrings.BloomHQ);
else
uberMaterial.EnableKeyword(dirtIntensity > 0f ? ShaderKeywordStrings.BloomLQDirt : ShaderKeywordStrings.BloomLQ);
}
#endregion
#if UNITY_6000_0_OR_NEWER
#region RenderGraph
private class PassData
{
public Material charMaterial;
public Material envMaterial;
public MaterialPropertyBlock propertyBlock;
public TextureHandle cameraColor;
public Texture tonyMcMapfaceLut;
public PostProcessData charPostProcessData;
public PostProcessData envPostProcessData;
}
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
if (!m_CharPostProcessData.enabled && !m_EnvPostProcessData.enabled)
return;
var renderingData = frameData.Get<UniversalRenderingData>();
var lightData = frameData.Get<UniversalLightData>();
var cameraData = frameData.Get<UniversalCameraData>();
var resourceData = frameData.Get<UniversalResourceData>();
m_Descriptor = cameraData.cameraTargetDescriptor;
var colorCopyDescriptor = cameraData.cameraTargetDescriptor;
colorCopyDescriptor.depthBufferBits = (int)DepthBits.None;
TextureHandle copiedColor = UniversalRenderer.CreateRenderGraphTexture(renderGraph, colorCopyDescriptor, "_PotaToonTempCopiedColor", true);
bool characterPostProcessEnabled = m_CharMaterial != null && m_CharPostProcessData.enabled;
bool environmentPostProcessEnabled = m_EnvMaterial != null && m_EnvPostProcessData.enabled;
bool screenOutlineEnabled = characterPostProcessEnabled && m_CharPostProcessData.screenOutline;
if (environmentPostProcessEnabled || !screenOutlineEnabled)
{
using (var builder = renderGraph.AddRasterRenderPass<PassData>("[PotaToon] Post Process - Copy Color", out var passData, m_ProfilingSamplers[1]))
{
builder.SetRenderAttachment(copiedColor, 0);
builder.UseTexture(resourceData.cameraColor);
passData.cameraColor = resourceData.cameraColor;
builder.SetRenderFunc((PassData data, RasterGraphContext context) =>
{
Blitter.BlitTexture(context.cmd, data.cameraColor, Vector2.one, 0, false);
});
}
}
// Env PP
DisableAllBloomKeywords(m_EnvMaterial);
if (environmentPostProcessEnabled)
{
if (m_EnvBloom.enabled)
{
TextureHandle currentSource = resourceData.activeColorTexture;
RenderBloomTexture(renderGraph, in currentSource, out var BloomTexture, in m_EnvBloom, cameraData.isAlphaOutputEnabled, false, false);
UberPostSetupBloomPass(renderGraph, in BloomTexture, in m_EnvBloom, m_EnvMaterial);
}
using (var builder = renderGraph.AddRasterRenderPass<PassData>("[PotaToon] Post Process - Environment", out var passData, m_ProfilingSamplers[0]))
{
builder.AllowGlobalStateModification(true);
builder.SetRenderAttachment(resourceData.cameraColor, 0);
builder.UseTexture(copiedColor);
passData.envMaterial = m_EnvMaterial;
passData.propertyBlock = m_PropertyBlock;
passData.cameraColor = copiedColor;
passData.tonyMcMapfaceLut = m_TonyMcMapfaceTexture;
passData.envPostProcessData = m_EnvPostProcessData;
builder.SetRenderFunc((PassData data, RasterGraphContext context) =>
{
data.propertyBlock.SetTexture(ShaderIDs._BlitTexture, data.cameraColor);
data.propertyBlock.SetVector(ShaderIDs._BlitScaleBias, new Vector4(1, 1, 0, 0));
data.propertyBlock.SetTexture(ShaderIDs._TonyMcMapfaceLut, data.tonyMcMapfaceLut);
Utils.SetMaterialToneMappingKeywords(data.envMaterial, data.envPostProcessData.toneMappingMode, data.envPostProcessData.hableCurve);
data.envMaterial.SetFloat(ShaderIDs._PostExposure, data.envPostProcessData.postExposure);
data.envMaterial.SetVector(ShaderIDs._HueSatCon, data.envPostProcessData.hueSatCon);
data.envMaterial.SetColor(ShaderIDs._ColorFilter, data.envPostProcessData.colorFilter);
data.envMaterial.SetVector(ShaderIDs._ColorBalance, data.envPostProcessData.lmsColorBalance);
CoreUtils.DrawFullScreen(context.cmd, data.envMaterial, data.propertyBlock, ShaderPassIDs.environment);
});
}
if (characterPostProcessEnabled && !screenOutlineEnabled)
{
using (var builder = renderGraph.AddRasterRenderPass<PassData>("[PotaToon] Post Process - Copy Color", out var passData, m_ProfilingSamplers[1]))
{
builder.SetRenderAttachment(copiedColor, 0);
builder.UseTexture(resourceData.cameraColor);
passData.cameraColor = resourceData.cameraColor;
builder.SetRenderFunc((PassData data, RasterGraphContext context) =>
{
Blitter.BlitTexture(context.cmd, data.cameraColor, Vector2.one, 0, false);
});
}
}
}
// Char PP
DisableAllBloomKeywords(m_CharMaterial);
if (characterPostProcessEnabled)
{
if (screenOutlineEnabled)
{
using (var builder = renderGraph.AddRasterRenderPass<PassData>("[PotaToon] Post Process - Character Screen Outline", out var passData, m_ProfilingSamplers[0]))
{
builder.SetRenderAttachment(resourceData.cameraColor, 0);
passData.charMaterial = m_CharMaterial;
passData.propertyBlock = m_PropertyBlock;
passData.charPostProcessData = m_CharPostProcessData;
builder.SetRenderFunc((PassData data, RasterGraphContext context) =>
{
data.charMaterial.SetFloat(ShaderIDs._ScreenOutlineThickness, data.charPostProcessData.screenOutlineThickness);
data.charMaterial.SetFloat(ShaderIDs._ScreenOutlineEdgeStrength, data.charPostProcessData.screenOutlineEdgeStrength);
data.charMaterial.SetColor(ShaderIDs._ScreenOutlineColor, data.charPostProcessData.screenOutlineColor);
data.charMaterial.SetKeyword(new LocalKeyword(data.charMaterial.shader, "_EXCLUDE_INNER_SCREEN_OUTLINES"), data.charPostProcessData.screenOutlineExcludeInnerLines);
CoreUtils.DrawFullScreen(context.cmd, data.charMaterial, data.propertyBlock, ShaderPassIDs.screenOutline);
});
}
using (var builder = renderGraph.AddRasterRenderPass<PassData>("[PotaToon] Post Process - Copy Color", out var passData, m_ProfilingSamplers[1]))
{
builder.SetRenderAttachment(copiedColor, 0);
builder.UseTexture(resourceData.cameraColor);
passData.cameraColor = resourceData.cameraColor;
builder.SetRenderFunc((PassData data, RasterGraphContext context) =>
{
Blitter.BlitTexture(context.cmd, data.cameraColor, Vector2.one, 0, false);
});
}
}
if (m_CharBloom.enabled)
{
TextureHandle currentSource = resourceData.activeColorTexture;
RenderBloomTexture(renderGraph, in currentSource, out var BloomTexture, in m_CharBloom, cameraData.isAlphaOutputEnabled, true, environmentPostProcessEnabled && m_EnvBloom.enabled);
UberPostSetupBloomPass(renderGraph, in BloomTexture, in m_CharBloom, m_CharMaterial);
}
using (var builder = renderGraph.AddRasterRenderPass<PassData>("[PotaToon] Post Process - Character", out var passData, m_ProfilingSamplers[0]))
{
builder.AllowGlobalStateModification(true);
builder.SetRenderAttachment(resourceData.cameraColor, 0);
builder.UseTexture(copiedColor);
passData.charMaterial = m_CharMaterial;
passData.envMaterial = m_EnvMaterial;
passData.propertyBlock = m_PropertyBlock;
passData.cameraColor = copiedColor;
passData.tonyMcMapfaceLut = m_TonyMcMapfaceTexture;
passData.charPostProcessData = m_CharPostProcessData;
passData.envPostProcessData = m_EnvPostProcessData;
builder.SetRenderFunc((PassData data, RasterGraphContext context) =>
{
data.propertyBlock.SetTexture(ShaderIDs._BlitTexture, data.cameraColor);
data.propertyBlock.SetVector(ShaderIDs._BlitScaleBias, new Vector4(1, 1, 0, 0));
data.propertyBlock.SetTexture(ShaderIDs._TonyMcMapfaceLut, data.tonyMcMapfaceLut);
Utils.SetMaterialToneMappingKeywords(data.charMaterial, data.charPostProcessData.toneMappingMode, data.charPostProcessData.hableCurve);
data.charMaterial.SetFloat(ShaderIDs._CharGammaAdjust, data.charPostProcessData.gammaAdjust ? 1f : 0f);
data.charMaterial.SetFloat(ShaderIDs._PostExposure, data.charPostProcessData.postExposure);
data.charMaterial.SetVector(ShaderIDs._HueSatCon, data.charPostProcessData.hueSatCon);
data.charMaterial.SetColor(ShaderIDs._ColorFilter, data.charPostProcessData.colorFilter);
data.charMaterial.SetVector(ShaderIDs._ColorBalance, data.charPostProcessData.lmsColorBalance);
data.charMaterial.SetFloat(ShaderIDs._ScreenRimWidth, data.charPostProcessData.screenRimWidth);
data.charMaterial.SetVector(ShaderIDs._ScreenRimColor, data.charPostProcessData.screenRimColor);
CoreUtils.DrawFullScreen(context.cmd, data.charMaterial, data.propertyBlock, ShaderPassIDs.character);
});
}
}
}
#region Bloom
private class UberSetupBloomPassData
{
internal Vector4 bloomParams;
internal Vector4 dirtScaleOffset;
internal float dirtIntensity;
internal Texture dirtTexture;
internal bool highQualityFilteringValue;
internal TextureHandle bloomTexture;
internal Material uberMaterial;
}
private void UberPostSetupBloomPass(RenderGraph rendergraph, in TextureHandle bloomTexture, in BloomData bloomData, Material uberMaterial)
{
using (var builder = rendergraph.AddRasterRenderPass<UberSetupBloomPassData>("[PotaToon] Setup Bloom Post Processing", out var passData, new ProfilingSampler("RG_UberPostSetupBloomPass")))
{
// Setup bloom on uber
var tint = bloomData.tint.linear;
var luma = ColorUtils.Luminance(tint);
tint = luma > 0f ? tint * (1f / luma) : Color.white;
var bloomParams = new Vector4(bloomData.intensity, tint.r, tint.g, tint.b);
// Setup lens dirtiness on uber
// Keep the aspect ratio correct & center the dirt texture, we don't want it to be
// stretched or squashed
var dirtTexture = bloomData.dirtTexture == null ? Texture2D.blackTexture : bloomData.dirtTexture;
float dirtRatio = dirtTexture.width / (float)dirtTexture.height;
float screenRatio = m_Descriptor.width / (float)m_Descriptor.height;
var dirtScaleOffset = new Vector4(1f, 1f, 0f, 0f);
float dirtIntensity = bloomData.dirtIntensity;
if (dirtRatio > screenRatio)
{
dirtScaleOffset.x = screenRatio / dirtRatio;
dirtScaleOffset.z = (1f - dirtScaleOffset.x) * 0.5f;
}
else if (screenRatio > dirtRatio)
{
dirtScaleOffset.y = dirtRatio / screenRatio;
dirtScaleOffset.w = (1f - dirtScaleOffset.y) * 0.5f;
}
passData.bloomParams = bloomParams;
passData.dirtScaleOffset = dirtScaleOffset;
passData.dirtIntensity = dirtIntensity;
passData.dirtTexture = dirtTexture;
passData.highQualityFilteringValue = bloomData.highQualityFiltering;
passData.bloomTexture = bloomTexture;
builder.UseTexture(bloomTexture, AccessFlags.Read);
passData.uberMaterial = uberMaterial;
// TODO RENDERGRAPH: properly setup dependencies between passes
builder.AllowPassCulling(false);
builder.SetRenderFunc(static (UberSetupBloomPassData data, RasterGraphContext context) =>
{
var uberMaterial = data.uberMaterial;
uberMaterial.SetVector(ShaderConstants._Bloom_Params, data.bloomParams);
uberMaterial.SetVector(ShaderConstants._LensDirt_Params, data.dirtScaleOffset);
uberMaterial.SetFloat(ShaderConstants._LensDirt_Intensity, data.dirtIntensity);
uberMaterial.SetTexture(ShaderConstants._LensDirt_Texture, data.dirtTexture);
// Keyword setup - a bit convoluted as we're trying to save some variants in Uber...
if (data.highQualityFilteringValue)
uberMaterial.EnableKeyword(data.dirtIntensity > 0f ? ShaderKeywordStrings.BloomHQDirt : ShaderKeywordStrings.BloomHQ);
else
uberMaterial.EnableKeyword(data.dirtIntensity > 0f ? ShaderKeywordStrings.BloomLQDirt : ShaderKeywordStrings.BloomLQ);
uberMaterial.SetTexture(ShaderConstants._Bloom_Texture, data.bloomTexture);
});
}
}
private class BloomPassData
{
internal int mipCount;
internal Material material;
internal Material[] upsampleMaterials;
internal TextureHandle sourceTexture;
internal TextureHandle[] bloomMipUp;
internal TextureHandle[] bloomMipDown;
internal bool isCharacterBloom;
}
internal struct BloomMaterialParams
{
internal Vector4 parameters;
internal bool highQualityFiltering;
internal bool enableAlphaOutput;
internal bool Equals(ref BloomMaterialParams other)
{
return parameters == other.parameters &&
highQualityFiltering == other.highQualityFiltering &&
enableAlphaOutput == other.enableAlphaOutput;
}
}
private void RenderBloomTexture(RenderGraph renderGraph, in TextureHandle source, out TextureHandle destination, in BloomData bloomData, bool enableAlphaOutput, bool isCharacterBloom, bool hasResourcesCreated)
{
// Start at half-res
int downres = 1;
switch (bloomData.downscale)
{
case BloomDownscaleMode.Half:
downres = 1;
break;
case BloomDownscaleMode.Quarter:
downres = 2;
break;
default:
throw new ArgumentOutOfRangeException();
}
//We should set the limit the downres result to ensure we dont turn 1x1 textures, which should technically be valid
//into 0x0 textures which will be invalid
int tw = Mathf.Max(1, m_Descriptor.width >> downres);
int th = Mathf.Max(1, m_Descriptor.height >> downres);
// Determine the iteration count
int maxSize = Mathf.Max(tw, th);
int iterations = Mathf.FloorToInt(Mathf.Log(maxSize, 2f) - 1);
int mipCount = Mathf.Clamp(iterations, 1, bloomData.maxIterations);
// Setup
using(new ProfilingScope(new ProfilingSampler("BloomSetup")))
{
// Pre-filtering parameters
float clamp = bloomData.clamp;
float threshold = Mathf.GammaToLinearSpace(bloomData.threshold);
float thresholdKnee = threshold * 0.5f; // Hardcoded soft knee
// Material setup
float scatter = Mathf.Lerp(0.05f, 0.95f, bloomData.scatter);
BloomMaterialParams bloomParams = new BloomMaterialParams();
bloomParams.parameters = new Vector4(scatter, clamp, threshold, thresholdKnee);
bloomParams.highQualityFiltering = bloomData.highQualityFiltering;
bloomParams.enableAlphaOutput = enableAlphaOutput;
// Setting keywords can be somewhat expensive on low-end platforms.
// Previous params are cached to avoid setting the same keywords every frame.
var material = isCharacterBloom ? m_CharBloomMaterial : m_EnvBloomMaterial;
bool bloomParamsDirty = !m_BloomParamsPrev.Equals(ref bloomParams);
bool isParamsPropertySet = material.HasProperty(ShaderConstants._Params);
if (bloomParamsDirty || !isParamsPropertySet)
{
material.SetVector(ShaderConstants._Params, bloomParams.parameters);
CoreUtils.SetKeyword(material, ShaderKeywordStrings.BloomHQ, bloomParams.highQualityFiltering);
CoreUtils.SetKeyword(material, ShaderKeywordStrings._ENABLE_ALPHA_OUTPUT, bloomParams.enableAlphaOutput);
// These materials are duplicate just to allow different bloom blits to use different textures.
for (uint i = 0; i < k_MaxPyramidSize; ++i)
{
var materialPyramid = isCharacterBloom ? m_CharBloomUpsample[i] : m_EnvBloomUpsample[i];
materialPyramid.SetVector(ShaderConstants._Params, bloomParams.parameters);
CoreUtils.SetKeyword(materialPyramid, ShaderKeywordStrings.BloomHQ, bloomParams.highQualityFiltering);
CoreUtils.SetKeyword(materialPyramid, ShaderKeywordStrings._ENABLE_ALPHA_OUTPUT, bloomParams.enableAlphaOutput);
}
m_BloomParamsPrev = bloomParams;
}
// Create bloom mip pyramid textures
if (!hasResourcesCreated)
{
var desc = Utils.GetCompatibleDescriptor(m_Descriptor, tw, th, m_DefaultColorFormat);
_BloomMipDown[0] = UniversalRenderer.CreateRenderGraphTexture(renderGraph, desc, m_BloomMipDown[0].name, false, FilterMode.Bilinear);
_BloomMipUp[0] = UniversalRenderer.CreateRenderGraphTexture(renderGraph, desc, m_BloomMipUp[0].name, false, FilterMode.Bilinear);
for (int i = 1; i < mipCount; i++)
{
tw = Mathf.Max(1, tw >> 1);
th = Mathf.Max(1, th >> 1);
ref TextureHandle mipDown = ref _BloomMipDown[i];
ref TextureHandle mipUp = ref _BloomMipUp[i];
desc.width = tw;
desc.height = th;
// NOTE: Reuse RTHandle names for TextureHandles
mipDown = UniversalRenderer.CreateRenderGraphTexture(renderGraph, desc, m_BloomMipDown[i].name, false, FilterMode.Bilinear);
mipUp = UniversalRenderer.CreateRenderGraphTexture(renderGraph, desc, m_BloomMipUp[i].name, false, FilterMode.Bilinear);
}
}
}
using (var builder = renderGraph.AddUnsafePass<BloomPassData>("[PotaToon] Blit Bloom Mipmaps", out var passData, new ProfilingSampler("PotaToon Bloom")))
{
passData.mipCount = mipCount;
passData.material = isCharacterBloom ? m_CharBloomMaterial : m_EnvBloomMaterial;
passData.upsampleMaterials = isCharacterBloom ? m_CharBloomUpsample : m_EnvBloomUpsample;
passData.sourceTexture = source;
passData.bloomMipDown = _BloomMipDown;
passData.bloomMipUp = _BloomMipUp;
passData.isCharacterBloom = isCharacterBloom;
// TODO RENDERGRAPH: properly setup dependencies between passes
builder.AllowPassCulling(false);
builder.UseTexture(source, AccessFlags.Read);
for (int i = 0; i < mipCount; i++)
{
builder.UseTexture(_BloomMipDown[i], AccessFlags.ReadWrite);
builder.UseTexture(_BloomMipUp[i], AccessFlags.ReadWrite);
}
builder.SetRenderFunc(static (BloomPassData data, UnsafeGraphContext context) =>
{
// TODO: can't call BlitTexture with unsafe command buffer
var cmd = CommandBufferHelpers.GetNativeCommandBuffer(context.cmd);
var material = data.material;
int mipCount = data.mipCount;
var loadAction = RenderBufferLoadAction.DontCare; // Blit - always write all pixels
var storeAction = RenderBufferStoreAction.Store; // Blit - always read by then next Blit
// Prefilter
using(new ProfilingScope(cmd, new ProfilingSampler("RG_BloomPrefilter")))
{
CoreUtils.SetKeyword(material, CustomShaderKeywordStrings.PotaToonCharacterBloom, data.isCharacterBloom);
Blitter.BlitCameraTexture(cmd, data.sourceTexture, data.bloomMipDown[0], loadAction, storeAction, material, 0);
}
// Downsample - gaussian pyramid
// Classic two pass gaussian blur - use mipUp as a temporary target
// First pass does 2x downsampling + 9-tap gaussian
// Second pass does 9-tap gaussian using a 5-tap filter + bilinear filtering
using(new ProfilingScope(cmd, new ProfilingSampler("RG_BloomDownsample")))
{
TextureHandle lastDown = data.bloomMipDown[0];
for (int i = 1; i < mipCount; i++)
{
TextureHandle mipDown = data.bloomMipDown[i];
TextureHandle mipUp = data.bloomMipUp[i];
Blitter.BlitCameraTexture(cmd, lastDown, mipUp, loadAction, storeAction, material, 1);
Blitter.BlitCameraTexture(cmd, mipUp, mipDown, loadAction, storeAction, material, 2);
lastDown = mipDown;
}
}
using (new ProfilingScope(cmd, new ProfilingSampler("RG_BloomUpsample")))
{
// Upsample (bilinear by default, HQ filtering does bicubic instead
for (int i = mipCount - 2; i >= 0; i--)
{
TextureHandle lowMip = (i == mipCount - 2) ? data.bloomMipDown[i + 1] : data.bloomMipUp[i + 1];
TextureHandle highMip = data.bloomMipDown[i];
TextureHandle dst = data.bloomMipUp[i];
// We need a separate material for each upsample pass because setting the low texture mip source
// gets overriden by the time the render func is executed.
// Material is a reference, so all the blits would share the same material state in the cmdbuf.
// NOTE: another option would be to use cmd.SetGlobalTexture().
var upMaterial = data.upsampleMaterials[i];
upMaterial.SetTexture(ShaderConstants._SourceTexLowMip, lowMip);
Blitter.BlitCameraTexture(cmd, highMip, dst, loadAction, storeAction, upMaterial, 3);
}
}
});
destination = passData.bloomMipUp[0];
}
}
#endregion
#endregion
#endif
}
}