arenos-nexus/Arenos Nexus/Library/PackageCache/com.unity.render-pipelines.universal@0d0962426023/Runtime/Passes/PostProcessPassRenderGraph.cs
Daniel 2e704cae70 init
Init Commit Unity
2025-09-25 22:01:28 +02:00

2169 lines
112 KiB
C#

using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
using System;
using UnityEngine.Rendering.Universal.Internal;
namespace UnityEngine.Rendering.Universal
{
internal partial class PostProcessPass : ScriptableRenderPass
{
static readonly int s_CameraDepthTextureID = Shader.PropertyToID("_CameraDepthTexture");
private class UpdateCameraResolutionPassData
{
internal Vector2Int newCameraTargetSize;
}
// Updates render target descriptors and shader constants to reflect a new render size
// This should be called immediately after the resolution changes mid-frame (typically after an upscaling operation).
void UpdateCameraResolution(RenderGraph renderGraph, UniversalCameraData cameraData, Vector2Int newCameraTargetSize)
{
// Update the camera data descriptor to reflect post-upscaled sizes
cameraData.cameraTargetDescriptor.width = newCameraTargetSize.x;
cameraData.cameraTargetDescriptor.height = newCameraTargetSize.y;
// Update the shader constants to reflect the new camera resolution
using (var builder = renderGraph.AddUnsafePass<UpdateCameraResolutionPassData>("Update Camera Resolution", out var passData))
{
passData.newCameraTargetSize = newCameraTargetSize;
// This pass only modifies shader constants
builder.AllowGlobalStateModification(true);
builder.SetRenderFunc(static (UpdateCameraResolutionPassData data, UnsafeGraphContext ctx) =>
{
ctx.cmd.SetGlobalVector(
ShaderPropertyId.screenSize,
new Vector4(
data.newCameraTargetSize.x,
data.newCameraTargetSize.y,
1.0f / data.newCameraTargetSize.x,
1.0f / data.newCameraTargetSize.y
)
);
});
}
}
internal static TextureHandle CreateCompatibleTexture(RenderGraph renderGraph, in TextureHandle source, string name, bool clear, FilterMode filterMode)
{
var desc = source.GetDescriptor(renderGraph);
MakeCompatible(ref desc);
desc.name = name;
desc.clearBuffer = clear;
desc.filterMode = filterMode;
return renderGraph.CreateTexture(desc);
}
internal static TextureHandle CreateCompatibleTexture(RenderGraph renderGraph, in TextureDesc desc, string name, bool clear, FilterMode filterMode)
{
var descCompatible = GetCompatibleDescriptor(desc);
descCompatible.name = name;
descCompatible.clearBuffer = clear;
descCompatible.filterMode = filterMode;
return renderGraph.CreateTexture(descCompatible);
}
internal static TextureDesc GetCompatibleDescriptor(TextureDesc desc, int width, int height, GraphicsFormat format)
{
desc.width = width;
desc.height = height;
desc.format = format;
MakeCompatible(ref desc);
return desc;
}
internal static TextureDesc GetCompatibleDescriptor(TextureDesc desc)
{
MakeCompatible(ref desc);
return desc;
}
internal static void MakeCompatible(ref TextureDesc desc)
{
desc.msaaSamples = MSAASamples.None;
desc.useMipMap = false;
desc.autoGenerateMips = false;
desc.anisoLevel = 0;
desc.discardBuffer = false;
}
#region StopNaNs
private class StopNaNsPassData
{
internal TextureHandle stopNaNTarget;
internal TextureHandle sourceTexture;
internal Material stopNaN;
}
public void RenderStopNaN(RenderGraph renderGraph, in TextureHandle activeCameraColor, out TextureHandle stopNaNTarget)
{
stopNaNTarget = CreateCompatibleTexture(renderGraph, activeCameraColor, "_StopNaNsTarget", true, FilterMode.Bilinear);
using (var builder = renderGraph.AddRasterRenderPass<StopNaNsPassData>("Stop NaNs", out var passData,
ProfilingSampler.Get(URPProfileId.RG_StopNaNs)))
{
passData.stopNaNTarget = stopNaNTarget;
builder.SetRenderAttachment(stopNaNTarget, 0, AccessFlags.ReadWrite);
passData.sourceTexture = activeCameraColor;
builder.UseTexture(activeCameraColor, AccessFlags.Read);
passData.stopNaN = m_Materials.stopNaN;
builder.SetRenderFunc(static (StopNaNsPassData data, RasterGraphContext context) =>
{
var cmd = context.cmd;
RTHandle sourceTextureHdl = data.sourceTexture;
Vector2 viewportScale = sourceTextureHdl.useScaling? new Vector2(sourceTextureHdl.rtHandleProperties.rtHandleScale.x, sourceTextureHdl.rtHandleProperties.rtHandleScale.y) : Vector2.one;
Blitter.BlitTexture(cmd, sourceTextureHdl, viewportScale, data.stopNaN, 0);
});
}
}
#endregion
#region SMAA
private class SMAASetupPassData
{
internal Vector4 metrics;
internal Texture2D areaTexture;
internal Texture2D searchTexture;
internal float stencilRef;
internal float stencilMask;
internal AntialiasingQuality antialiasingQuality;
internal Material material;
}
private class SMAAPassData
{
internal TextureHandle sourceTexture;
internal TextureHandle depthStencilTexture;
internal TextureHandle blendTexture;
internal Material material;
}
public void RenderSMAA(RenderGraph renderGraph, UniversalResourceData resourceData, AntialiasingQuality antialiasingQuality, in TextureHandle source, out TextureHandle SMAATarget)
{
var destDesc = renderGraph.GetTextureDesc(source);
SMAATarget = CreateCompatibleTexture(renderGraph, destDesc, "_SMAATarget", true, FilterMode.Bilinear);
destDesc.clearColor = Color.black;
destDesc.clearColor.a = 0.0f;
var edgeTextureDesc = destDesc;
edgeTextureDesc.format = m_SMAAEdgeFormat;
var edgeTexture = CreateCompatibleTexture(renderGraph, edgeTextureDesc, "_EdgeStencilTexture", true, FilterMode.Bilinear);
var edgeTextureStencilDesc = destDesc;
edgeTextureStencilDesc.format = GraphicsFormatUtility.GetDepthStencilFormat(24);
var edgeTextureStencil = CreateCompatibleTexture(renderGraph, edgeTextureStencilDesc, "_EdgeTexture", true, FilterMode.Bilinear);
var blendTextureDesc = destDesc;
blendTextureDesc.format = GraphicsFormat.R8G8B8A8_UNorm;
var blendTexture = CreateCompatibleTexture(renderGraph, blendTextureDesc, "_BlendTexture", true, FilterMode.Point);
// Anti-aliasing
var material = m_Materials.subpixelMorphologicalAntialiasing;
using (var builder = renderGraph.AddRasterRenderPass<SMAASetupPassData>("SMAA Material Setup", out var passData, ProfilingSampler.Get(URPProfileId.RG_SMAAMaterialSetup)))
{
const int kStencilBit = 64;
// TODO RENDERGRAPH: handle dynamic scaling
passData.metrics = new Vector4(1f / destDesc.width, 1f / destDesc.height, destDesc.width, destDesc.height);
passData.areaTexture = m_Data.textures.smaaAreaTex;
passData.searchTexture = m_Data.textures.smaaSearchTex;
passData.stencilRef = (float)kStencilBit;
passData.stencilMask = (float)kStencilBit;
passData.antialiasingQuality = antialiasingQuality;
passData.material = material;
builder.AllowPassCulling(false);
builder.SetRenderFunc(static (SMAASetupPassData data, RasterGraphContext context) =>
{
// Globals
data.material.SetVector(ShaderConstants._Metrics, data.metrics);
data.material.SetTexture(ShaderConstants._AreaTexture, data.areaTexture);
data.material.SetTexture(ShaderConstants._SearchTexture, data.searchTexture);
data.material.SetFloat(ShaderConstants._StencilRef, data.stencilRef);
data.material.SetFloat(ShaderConstants._StencilMask, data.stencilMask);
// Quality presets
data.material.shaderKeywords = null;
switch (data.antialiasingQuality)
{
case AntialiasingQuality.Low:
data.material.EnableKeyword(ShaderKeywordStrings.SmaaLow);
break;
case AntialiasingQuality.Medium:
data.material.EnableKeyword(ShaderKeywordStrings.SmaaMedium);
break;
case AntialiasingQuality.High:
data.material.EnableKeyword(ShaderKeywordStrings.SmaaHigh);
break;
}
});
}
using (var builder = renderGraph.AddRasterRenderPass<SMAAPassData>("SMAA Edge Detection", out var passData, ProfilingSampler.Get(URPProfileId.RG_SMAAEdgeDetection)))
{
builder.SetRenderAttachment(edgeTexture, 0, AccessFlags.Write);
passData.depthStencilTexture = edgeTextureStencil;
builder.SetRenderAttachmentDepth(edgeTextureStencil, AccessFlags.Write);
passData.sourceTexture = source;
builder.UseTexture(source, AccessFlags.Read);
builder.UseTexture(resourceData.cameraDepth ,AccessFlags.Read);
passData.material = material;
builder.SetRenderFunc(static (SMAAPassData data, RasterGraphContext context) =>
{
var SMAAMaterial = data.material;
var cmd = context.cmd;
RTHandle sourceTextureHdl = data.sourceTexture;
// Pass 1: Edge detection
Vector2 viewportScale = sourceTextureHdl.useScaling ? new Vector2(sourceTextureHdl.rtHandleProperties.rtHandleScale.x, sourceTextureHdl.rtHandleProperties.rtHandleScale.y) : Vector2.one;
Blitter.BlitTexture(cmd, sourceTextureHdl, viewportScale, SMAAMaterial, 0);
});
}
using (var builder = renderGraph.AddRasterRenderPass<SMAAPassData>("SMAA Blend weights", out var passData, ProfilingSampler.Get(URPProfileId.RG_SMAABlendWeight)))
{
builder.SetRenderAttachment(blendTexture, 0, AccessFlags.Write);
passData.depthStencilTexture = edgeTextureStencil;
builder.SetRenderAttachmentDepth(edgeTextureStencil, AccessFlags.Read);
passData.sourceTexture = edgeTexture;
builder.UseTexture(edgeTexture, AccessFlags.Read);
passData.material = material;
builder.SetRenderFunc(static (SMAAPassData data, RasterGraphContext context) =>
{
var SMAAMaterial = data.material;
var cmd = context.cmd;
RTHandle sourceTextureHdl = data.sourceTexture;
// Pass 2: Blend weights
Vector2 viewportScale = sourceTextureHdl.useScaling ? new Vector2(sourceTextureHdl.rtHandleProperties.rtHandleScale.x, sourceTextureHdl.rtHandleProperties.rtHandleScale.y) : Vector2.one;
Blitter.BlitTexture(cmd, sourceTextureHdl, viewportScale, SMAAMaterial, 1);
});
}
using (var builder = renderGraph.AddRasterRenderPass<SMAAPassData>("SMAA Neighborhood blending", out var passData, ProfilingSampler.Get(URPProfileId.RG_SMAANeighborhoodBlend)))
{
builder.AllowGlobalStateModification(true);
builder.SetRenderAttachment(SMAATarget, 0, AccessFlags.Write);
passData.sourceTexture = source;
builder.UseTexture(source, AccessFlags.Read);
passData.blendTexture = blendTexture;
builder.UseTexture(blendTexture, AccessFlags.Read);
passData.material = material;
builder.SetRenderFunc(static (SMAAPassData data, RasterGraphContext context) =>
{
var SMAAMaterial = data.material;
var cmd = context.cmd;
RTHandle sourceTextureHdl = data.sourceTexture;
// Pass 3: Neighborhood blending
SMAAMaterial.SetTexture(ShaderConstants._BlendTexture, data.blendTexture);
Vector2 viewportScale = sourceTextureHdl.useScaling ? new Vector2(sourceTextureHdl.rtHandleProperties.rtHandleScale.x, sourceTextureHdl.rtHandleProperties.rtHandleScale.y) : Vector2.one;
Blitter.BlitTexture(cmd, sourceTextureHdl, viewportScale, SMAAMaterial, 2);
});
}
}
#endregion
#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;
}
public void UberPostSetupBloomPass(RenderGraph rendergraph, Material uberMaterial, in TextureDesc srcDesc)
{
using (new ProfilingScope(ProfilingSampler.Get(URPProfileId.RG_UberPostSetupBloomPass)))
{
// Setup bloom on uber
var tint = m_Bloom.tint.value.linear;
var luma = ColorUtils.Luminance(tint);
tint = luma > 0f ? tint * (1f / luma) : Color.white;
var bloomParams = new Vector4(m_Bloom.intensity.value, 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 = m_Bloom.dirtTexture.value == null ? Texture2D.blackTexture : m_Bloom.dirtTexture.value;
float dirtRatio = dirtTexture.width / (float)dirtTexture.height;
float screenRatio = srcDesc.width / (float)srcDesc.height;
var dirtScaleOffset = new Vector4(1f, 1f, 0f, 0f);
float dirtIntensity = m_Bloom.dirtIntensity.value;
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;
}
var highQualityFilteringValue = m_Bloom.highQualityFiltering.value;
uberMaterial.SetVector(ShaderConstants._Bloom_Params, bloomParams);
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 (highQualityFilteringValue)
uberMaterial.EnableKeyword(dirtIntensity > 0f ? ShaderKeywordStrings.BloomHQDirt : ShaderKeywordStrings.BloomHQ);
else
uberMaterial.EnableKeyword(dirtIntensity > 0f ? ShaderKeywordStrings.BloomLQDirt : ShaderKeywordStrings.BloomLQ);
}
}
private class BloomPassData
{
internal int mipCount;
internal Material material;
internal Material[] upsampleMaterials;
internal TextureHandle sourceTexture;
internal TextureHandle[] bloomMipUp;
internal TextureHandle[] bloomMipDown;
}
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;
}
}
public void RenderBloomTexture(RenderGraph renderGraph, in TextureHandle source, out TextureHandle destination, bool enableAlphaOutput)
{
var srcDesc = source.GetDescriptor(renderGraph);
// Start at half-res
int downres = 1;
switch (m_Bloom.downscale.value)
{
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, srcDesc.width >> downres);
int th = Mathf.Max(1, srcDesc.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, m_Bloom.maxIterations.value);
// Setup
using(new ProfilingScope(ProfilingSampler.Get(URPProfileId.RG_BloomSetup)))
{
// Pre-filtering parameters
float clamp = m_Bloom.clamp.value;
float threshold = Mathf.GammaToLinearSpace(m_Bloom.threshold.value);
float thresholdKnee = threshold * 0.5f; // Hardcoded soft knee
// Material setup
float scatter = Mathf.Lerp(0.05f, 0.95f, m_Bloom.scatter.value);
BloomMaterialParams bloomParams = new BloomMaterialParams();
bloomParams.parameters = new Vector4(scatter, clamp, threshold, thresholdKnee);
bloomParams.highQualityFiltering = m_Bloom.highQualityFiltering.value;
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 = m_Materials.bloom;
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 = m_Materials.bloomUpsample[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
{
var desc = GetCompatibleDescriptor(srcDesc, tw, th, m_DefaultColorFormat);
_BloomMipDown[0] = CreateCompatibleTexture(renderGraph, desc, m_BloomMipDownName[0], false, FilterMode.Bilinear);
_BloomMipUp[0] = CreateCompatibleTexture(renderGraph, desc, m_BloomMipUpName[0], 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;
mipDown = CreateCompatibleTexture(renderGraph, desc, m_BloomMipDownName[i], false, FilterMode.Bilinear);
mipUp = CreateCompatibleTexture(renderGraph, desc, m_BloomMipUpName[i], false, FilterMode.Bilinear);
}
}
}
using (var builder = renderGraph.AddUnsafePass<BloomPassData>("Blit Bloom Mipmaps", out var passData, ProfilingSampler.Get(URPProfileId.Bloom)))
{
passData.mipCount = mipCount;
passData.material = m_Materials.bloom;
passData.upsampleMaterials = m_Materials.bloomUpsample;
passData.sourceTexture = source;
passData.bloomMipDown = _BloomMipDown;
passData.bloomMipUp = _BloomMipUp;
// 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, ProfilingSampler.Get(URPProfileId.RG_BloomPrefilter)))
{
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, ProfilingSampler.Get(URPProfileId.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, ProfilingSampler.Get(URPProfileId.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
#region DoF
public void RenderDoF(RenderGraph renderGraph, UniversalResourceData resourceData, UniversalCameraData cameraData, in TextureHandle source, out TextureHandle destination)
{
var dofMaterial = m_DepthOfField.mode.value == DepthOfFieldMode.Gaussian ? m_Materials.gaussianDepthOfField : m_Materials.bokehDepthOfField;
destination = CreateCompatibleTexture(renderGraph, source, "_DoFTarget", true, FilterMode.Bilinear);
CoreUtils.SetKeyword(dofMaterial, ShaderKeywordStrings._ENABLE_ALPHA_OUTPUT, cameraData.isAlphaOutputEnabled);
if (m_DepthOfField.mode.value == DepthOfFieldMode.Gaussian)
{
RenderDoFGaussian(renderGraph, resourceData, cameraData, source, destination, ref dofMaterial);
}
else if (m_DepthOfField.mode.value == DepthOfFieldMode.Bokeh)
{
RenderDoFBokeh(renderGraph, resourceData, cameraData, source, destination, ref dofMaterial);
}
}
private class DoFGaussianPassData
{
// Setup
internal int downsample;
internal RenderingData renderingData;
internal Vector3 cocParams;
internal bool highQualitySamplingValue;
// Inputs
internal TextureHandle sourceTexture;
internal TextureHandle depthTexture;
internal Material material;
internal Material materialCoC;
// Pass textures
internal TextureHandle halfCoCTexture;
internal TextureHandle fullCoCTexture;
internal TextureHandle pingTexture;
internal TextureHandle pongTexture;
internal RenderTargetIdentifier[] multipleRenderTargets = new RenderTargetIdentifier[2];
// Output textures
internal TextureHandle destination;
};
public void RenderDoFGaussian(RenderGraph renderGraph, UniversalResourceData resourceData, UniversalCameraData cameraData, in TextureHandle source, TextureHandle destination, ref Material dofMaterial)
{
var srcDesc = source.GetDescriptor(renderGraph);
var material = dofMaterial;
int downSample = 2;
int wh = srcDesc.width / downSample;
int hh = srcDesc.height / downSample;
// Pass Textures
var fullCoCTextureDesc = GetCompatibleDescriptor(srcDesc, srcDesc.width, srcDesc.height, m_GaussianCoCFormat);
var fullCoCTexture = CreateCompatibleTexture(renderGraph, fullCoCTextureDesc, "_FullCoCTexture", true, FilterMode.Bilinear);
var halfCoCTextureDesc = GetCompatibleDescriptor(srcDesc, wh, hh, m_GaussianCoCFormat);
var halfCoCTexture = CreateCompatibleTexture(renderGraph, halfCoCTextureDesc, "_HalfCoCTexture", true, FilterMode.Bilinear);
var pingTextureDesc = GetCompatibleDescriptor(srcDesc, wh, hh, m_DefaultColorFormat);
var pingTexture = CreateCompatibleTexture(renderGraph, pingTextureDesc, "_PingTexture", true, FilterMode.Bilinear);
var pongTextureDesc = GetCompatibleDescriptor(srcDesc, wh, hh, m_DefaultColorFormat);
var pongTexture = CreateCompatibleTexture(renderGraph, pongTextureDesc, "_PongTexture", true, FilterMode.Bilinear);
using (var builder = renderGraph.AddUnsafePass<DoFGaussianPassData>("Depth of Field - Gaussian", out var passData))
{
// Setup
float farStart = m_DepthOfField.gaussianStart.value;
float farEnd = Mathf.Max(farStart, m_DepthOfField.gaussianEnd.value);
// Assumes a radius of 1 is 1 at 1080p
// Past a certain radius our gaussian kernel will look very bad so we'll clamp it for
// very high resolutions (4K+).
float maxRadius = m_DepthOfField.gaussianMaxRadius.value * (wh / 1080f);
maxRadius = Mathf.Min(maxRadius, 2f);
passData.downsample = downSample;
passData.cocParams = new Vector3(farStart, farEnd, maxRadius);
passData.highQualitySamplingValue = m_DepthOfField.highQualitySampling.value;
passData.material = material;
passData.materialCoC = m_Materials.gaussianDepthOfFieldCoC;
// Inputs
passData.sourceTexture = source;
builder.UseTexture(source, AccessFlags.Read);
passData.depthTexture = resourceData.cameraDepthTexture;
builder.UseTexture(resourceData.cameraDepthTexture, AccessFlags.Read);
// Pass Textures
passData.fullCoCTexture = fullCoCTexture;
builder.UseTexture(fullCoCTexture, AccessFlags.ReadWrite);
passData.halfCoCTexture = halfCoCTexture;
builder.UseTexture(halfCoCTexture, AccessFlags.ReadWrite);
passData.pingTexture = pingTexture;
builder.UseTexture(pingTexture, AccessFlags.ReadWrite);
passData.pongTexture = pongTexture;
builder.UseTexture(pongTexture, AccessFlags.ReadWrite);
// Outputs
passData.destination = destination;
builder.UseTexture(destination, AccessFlags.Write);
builder.SetRenderFunc(static (DoFGaussianPassData data, UnsafeGraphContext context) =>
{
var dofMat = data.material;
var dofMaterialCoC = data.materialCoC;
var cmd = CommandBufferHelpers.GetNativeCommandBuffer(context.cmd);
RTHandle sourceTextureHdl = data.sourceTexture;
RTHandle dstHdl = data.destination;
// Setup
using (new ProfilingScope(ProfilingSampler.Get(URPProfileId.RG_SetupDoF)))
{
dofMat.SetVector(ShaderConstants._CoCParams, data.cocParams);
CoreUtils.SetKeyword(dofMat, ShaderKeywordStrings.HighQualitySampling,
data.highQualitySamplingValue);
dofMaterialCoC.SetVector(ShaderConstants._CoCParams, data.cocParams);
CoreUtils.SetKeyword(dofMaterialCoC, ShaderKeywordStrings.HighQualitySampling,
data.highQualitySamplingValue);
PostProcessUtils.SetSourceSize(cmd, data.sourceTexture);
dofMat.SetVector(ShaderConstants._DownSampleScaleFactor,
new Vector4(1.0f / data.downsample, 1.0f / data.downsample, data.downsample,
data.downsample));
}
// Compute CoC
using (new ProfilingScope(ProfilingSampler.Get(URPProfileId.RG_DOFComputeCOC)))
{
dofMat.SetTexture(s_CameraDepthTextureID, data.depthTexture);
Blitter.BlitCameraTexture(cmd, data.sourceTexture, data.fullCoCTexture, data.materialCoC, k_GaussianDoFPassComputeCoc);
}
// Downscale & prefilter color + CoC
using (new ProfilingScope(ProfilingSampler.Get(URPProfileId.RG_DOFDownscalePrefilter)))
{
dofMat.SetTexture(ShaderConstants._FullCoCTexture, data.fullCoCTexture);
// Handle packed shader output
data.multipleRenderTargets[0] = data.halfCoCTexture;
data.multipleRenderTargets[1] = data.pingTexture;
CoreUtils.SetRenderTarget(cmd, data.multipleRenderTargets, data.halfCoCTexture);
Vector2 viewportScale = sourceTextureHdl.useScaling ? new Vector2(sourceTextureHdl.rtHandleProperties.rtHandleScale.x, sourceTextureHdl.rtHandleProperties.rtHandleScale.y) : Vector2.one;
Blitter.BlitTexture(cmd, data.sourceTexture, viewportScale, dofMat, k_GaussianDoFPassDownscalePrefilter);
}
// Blur H
using (new ProfilingScope(ProfilingSampler.Get(URPProfileId.RG_DOFBlurH)))
{
dofMat.SetTexture(ShaderConstants._HalfCoCTexture, data.halfCoCTexture);
Blitter.BlitCameraTexture(cmd, data.pingTexture, data.pongTexture, dofMat, k_GaussianDoFPassBlurH);
}
// Blur V
using (new ProfilingScope(ProfilingSampler.Get(URPProfileId.RG_DOFBlurV)))
{
Blitter.BlitCameraTexture(cmd, data.pongTexture, data.pingTexture, dofMat, k_GaussianDoFPassBlurV);
}
// Composite
using (new ProfilingScope(ProfilingSampler.Get(URPProfileId.RG_DOFComposite)))
{
dofMat.SetTexture(ShaderConstants._ColorTexture, data.pingTexture);
dofMat.SetTexture(ShaderConstants._FullCoCTexture, data.fullCoCTexture);
Blitter.BlitCameraTexture(cmd, sourceTextureHdl, dstHdl, dofMat, k_GaussianDoFPassComposite);
}
});
}
}
private class DoFBokehPassData
{
// Setup
internal Vector4[] bokehKernel;
internal int downSample;
internal float uvMargin;
internal Vector4 cocParams;
internal bool useFastSRGBLinearConversion;
// Inputs
internal TextureHandle sourceTexture;
internal TextureHandle depthTexture;
internal Material material;
internal Material materialCoC;
// Pass textures
internal TextureHandle halfCoCTexture;
internal TextureHandle fullCoCTexture;
internal TextureHandle pingTexture;
internal TextureHandle pongTexture;
// Output texture
internal TextureHandle destination;
};
public void RenderDoFBokeh(RenderGraph renderGraph, UniversalResourceData resourceData, UniversalCameraData cameraData, in TextureHandle source, in TextureHandle destination, ref Material dofMaterial)
{
var srcDesc = source.GetDescriptor(renderGraph);
int downSample = 2;
var material = dofMaterial;
int wh = srcDesc.width / downSample;
int hh = srcDesc.height / downSample;
// Pass Textures
var fullCoCTextureDesc = GetCompatibleDescriptor(srcDesc, srcDesc.width, srcDesc.height, GraphicsFormat.R8_UNorm);
var fullCoCTexture = CreateCompatibleTexture(renderGraph, fullCoCTextureDesc, "_FullCoCTexture", true, FilterMode.Bilinear);
var pingTextureDesc = GetCompatibleDescriptor(srcDesc, wh, hh, GraphicsFormat.R16G16B16A16_SFloat);
var pingTexture = CreateCompatibleTexture(renderGraph, pingTextureDesc, "_PingTexture", true, FilterMode.Bilinear);
var pongTextureDesc = GetCompatibleDescriptor(srcDesc, wh, hh, GraphicsFormat.R16G16B16A16_SFloat);
var pongTexture = CreateCompatibleTexture(renderGraph, pongTextureDesc, "_PongTexture", true, FilterMode.Bilinear);
using (var builder = renderGraph.AddUnsafePass<DoFBokehPassData>("Depth of Field - Bokeh", out var passData))
{
// Setup
// "A Lens and Aperture Camera Model for Synthetic Image Generation" [Potmesil81]
float F = m_DepthOfField.focalLength.value / 1000f;
float A = m_DepthOfField.focalLength.value / m_DepthOfField.aperture.value;
float P = m_DepthOfField.focusDistance.value;
float maxCoC = (A * F) / (P - F);
float maxRadius = GetMaxBokehRadiusInPixels(srcDesc.height);
float rcpAspect = 1f / (wh / (float)hh);
// Prepare the bokeh kernel constant buffer
int hash = m_DepthOfField.GetHashCode();
if (hash != m_BokehHash || maxRadius != m_BokehMaxRadius || rcpAspect != m_BokehRCPAspect)
{
m_BokehHash = hash;
m_BokehMaxRadius = maxRadius;
m_BokehRCPAspect = rcpAspect;
PrepareBokehKernel(maxRadius, rcpAspect);
}
float uvMargin = (1.0f / srcDesc.height) * downSample;
passData.bokehKernel = m_BokehKernel;
passData.downSample = downSample;
passData.uvMargin = uvMargin;
passData.cocParams = new Vector4(P, maxCoC, maxRadius, rcpAspect);
passData.useFastSRGBLinearConversion = m_UseFastSRGBLinearConversion;
// Inputs
passData.sourceTexture = source;
builder.UseTexture(source, AccessFlags.Read);
passData.depthTexture = resourceData.cameraDepthTexture;
builder.UseTexture(resourceData.cameraDepthTexture, AccessFlags.Read);
passData.material = material;
passData.materialCoC = m_Materials.bokehDepthOfFieldCoC;
// Pass Textures
passData.fullCoCTexture = fullCoCTexture;
builder.UseTexture(fullCoCTexture, AccessFlags.ReadWrite);
passData.pingTexture = pingTexture;
builder.UseTexture(pingTexture, AccessFlags.ReadWrite);
passData.pongTexture = pongTexture;
builder.UseTexture(pongTexture, AccessFlags.ReadWrite);
// Outputs
passData.destination = destination;
builder.UseTexture(destination, AccessFlags.Write);
// TODO RENDERGRAPH: properly setup dependencies between passes
builder.SetRenderFunc(static (DoFBokehPassData data, UnsafeGraphContext context) =>
{
var dofMat = data.material;
var dofMaterialCoC = data.materialCoC;
var cmd = CommandBufferHelpers.GetNativeCommandBuffer(context.cmd);
RTHandle sourceTextureHdl = data.sourceTexture;
RTHandle dst = data.destination;
// Setup
using (new ProfilingScope(ProfilingSampler.Get(URPProfileId.RG_SetupDoF)))
{
CoreUtils.SetKeyword(dofMat, ShaderKeywordStrings.UseFastSRGBLinearConversion,
data.useFastSRGBLinearConversion);
CoreUtils.SetKeyword(dofMaterialCoC, ShaderKeywordStrings.UseFastSRGBLinearConversion,
data.useFastSRGBLinearConversion);
dofMat.SetVector(ShaderConstants._CoCParams, data.cocParams);
dofMat.SetVectorArray(ShaderConstants._BokehKernel, data.bokehKernel);
dofMat.SetVector(ShaderConstants._DownSampleScaleFactor,
new Vector4(1.0f / data.downSample, 1.0f / data.downSample, data.downSample,
data.downSample));
dofMat.SetVector(ShaderConstants._BokehConstants,
new Vector4(data.uvMargin, data.uvMargin * 2.0f));
PostProcessUtils.SetSourceSize(cmd, data.sourceTexture);
}
// Compute CoC
using (new ProfilingScope(ProfilingSampler.Get(URPProfileId.RG_DOFComputeCOC)))
{
dofMat.SetTexture(s_CameraDepthTextureID, data.depthTexture);
Blitter.BlitCameraTexture(cmd, sourceTextureHdl, data.fullCoCTexture, dofMat, k_BokehDoFPassComputeCoc);
}
// Downscale and Prefilter Color + CoC
using (new ProfilingScope(ProfilingSampler.Get(URPProfileId.RG_DOFDownscalePrefilter)))
{
dofMat.SetTexture(ShaderConstants._FullCoCTexture, data.fullCoCTexture);
Blitter.BlitCameraTexture(cmd, sourceTextureHdl, data.pingTexture, dofMat, k_BokehDoFPassDownscalePrefilter);
}
// Blur
using (new ProfilingScope(ProfilingSampler.Get(URPProfileId.RG_DOFBlurBokeh)))
{
Blitter.BlitCameraTexture(cmd, data.pingTexture, data.pongTexture, dofMat, k_BokehDoFPassBlur);
}
// Post Filtering
using (new ProfilingScope(ProfilingSampler.Get(URPProfileId.RG_DOFPostFilter)))
{
Blitter.BlitCameraTexture(cmd, data.pongTexture, data.pingTexture, dofMat, k_BokehDoFPassPostFilter);
}
// Composite
using (new ProfilingScope(ProfilingSampler.Get(URPProfileId.RG_DOFComposite)))
{
dofMat.SetTexture(ShaderConstants._DofTexture, data.pingTexture);
Blitter.BlitCameraTexture(cmd, sourceTextureHdl, dst, dofMat, k_BokehDoFPassComposite);
}
});
}
}
#endregion
#region Panini
private class PaniniProjectionPassData
{
internal TextureHandle destinationTexture;
internal TextureHandle sourceTexture;
internal Material material;
internal Vector4 paniniParams;
internal bool isPaniniGeneric;
}
public void RenderPaniniProjection(RenderGraph renderGraph, Camera camera, in TextureHandle source, out TextureHandle destination)
{
destination = CreateCompatibleTexture(renderGraph, source, "_PaniniProjectionTarget", true, FilterMode.Bilinear);
// Use source width/height for aspect ratio which can be different from camera aspect. (e.g. viewport)
var desc = source.GetDescriptor(renderGraph);
float distance = m_PaniniProjection.distance.value;
var viewExtents = CalcViewExtents(camera, desc.width, desc.height);
var cropExtents = CalcCropExtents(camera, distance, desc.width, desc.height);
float scaleX = cropExtents.x / viewExtents.x;
float scaleY = cropExtents.y / viewExtents.y;
float scaleF = Mathf.Min(scaleX, scaleY);
float paniniD = distance;
float paniniS = Mathf.Lerp(1f, Mathf.Clamp01(scaleF), m_PaniniProjection.cropToFit.value);
using (var builder = renderGraph.AddRasterRenderPass<PaniniProjectionPassData>("Panini Projection", out var passData, ProfilingSampler.Get(URPProfileId.PaniniProjection)))
{
builder.AllowGlobalStateModification(true);
passData.destinationTexture = destination;
builder.SetRenderAttachment(destination, 0, AccessFlags.Write);
passData.sourceTexture = source;
builder.UseTexture(source, AccessFlags.Read);
passData.material = m_Materials.paniniProjection;
passData.paniniParams = new Vector4(viewExtents.x, viewExtents.y, paniniD, paniniS);
passData.isPaniniGeneric = 1f - Mathf.Abs(paniniD) > float.Epsilon;
builder.SetRenderFunc(static (PaniniProjectionPassData data, RasterGraphContext context) =>
{
var cmd = context.cmd;
RTHandle sourceTextureHdl = data.sourceTexture;
cmd.SetGlobalVector(ShaderConstants._Params, data.paniniParams);
data.material.EnableKeyword(data.isPaniniGeneric ? ShaderKeywordStrings.PaniniGeneric : ShaderKeywordStrings.PaniniUnitDistance);
Vector2 viewportScale = sourceTextureHdl.useScaling ? new Vector2(sourceTextureHdl.rtHandleProperties.rtHandleScale.x, sourceTextureHdl.rtHandleProperties.rtHandleScale.y) : Vector2.one;
Blitter.BlitTexture(cmd, sourceTextureHdl, viewportScale, data.material, 0);
});
return;
}
}
#endregion
#region TemporalAA
private const string _TemporalAATargetName = "_TemporalAATarget";
private void RenderTemporalAA(RenderGraph renderGraph, UniversalResourceData resourceData, UniversalCameraData cameraData, ref TextureHandle source, out TextureHandle destination)
{
destination = CreateCompatibleTexture(renderGraph, source, _TemporalAATargetName, false, FilterMode.Bilinear);
TextureHandle cameraDepth = resourceData.cameraDepth;
TextureHandle motionVectors = resourceData.motionVectorColor;
Debug.Assert(motionVectors.IsValid(), "MotionVectors are invalid. TAA requires a motion vector texture.");
TemporalAA.Render(renderGraph, m_Materials.temporalAntialiasing, cameraData, ref source, ref cameraDepth, ref motionVectors, ref destination);
}
#endregion
#region STP
private const string _UpscaledColorTargetName = "_CameraColorUpscaledSTP";
private void RenderSTP(RenderGraph renderGraph, UniversalResourceData resourceData, UniversalCameraData cameraData, ref TextureHandle source, out TextureHandle destination)
{
TextureHandle cameraDepth = resourceData.cameraDepthTexture;
TextureHandle motionVectors = resourceData.motionVectorColor;
Debug.Assert(motionVectors.IsValid(), "MotionVectors are invalid. STP requires a motion vector texture.");
var srcDesc = source.GetDescriptor(renderGraph);
var destDesc = GetCompatibleDescriptor(srcDesc,
cameraData.pixelWidth,
cameraData.pixelHeight,
// Avoid enabling sRGB because STP works with compute shaders which can't output sRGB automatically.
GraphicsFormatUtility.GetLinearFormat(srcDesc.format));
// STP uses compute shaders so all render textures must enable random writes
destDesc.enableRandomWrite = true;
destination = CreateCompatibleTexture(renderGraph, destDesc, _UpscaledColorTargetName, false, FilterMode.Bilinear);
int frameIndex = Time.frameCount;
var noiseTexture = m_Data.textures.blueNoise16LTex[frameIndex & (m_Data.textures.blueNoise16LTex.Length - 1)];
StpUtils.Execute(renderGraph, resourceData, cameraData, source, cameraDepth, motionVectors, destination, noiseTexture);
// Update the camera resolution to reflect the upscaled size
UpdateCameraResolution(renderGraph, cameraData, new Vector2Int(destDesc.width, destDesc.height));
}
#endregion
#region MotionBlur
private class MotionBlurPassData
{
internal TextureHandle sourceTexture;
internal TextureHandle motionVectors;
internal Material material;
internal int passIndex;
internal Camera camera;
internal XRPass xr;
internal float intensity;
internal float clamp;
internal bool enableAlphaOutput;
}
public void RenderMotionBlur(RenderGraph renderGraph, UniversalResourceData resourceData, UniversalCameraData cameraData, in TextureHandle source, out TextureHandle destination)
{
var material = m_Materials.cameraMotionBlur;
destination = CreateCompatibleTexture(renderGraph, source, "_MotionBlurTarget", true, FilterMode.Bilinear);
TextureHandle motionVectorColor = resourceData.motionVectorColor;
TextureHandle cameraDepthTexture = resourceData.cameraDepthTexture;
var mode = m_MotionBlur.mode.value;
int passIndex = (int)m_MotionBlur.quality.value;
passIndex += (mode == MotionBlurMode.CameraAndObjects) ? 3 : 0;
using (var builder = renderGraph.AddRasterRenderPass<MotionBlurPassData>("Motion Blur", out var passData, ProfilingSampler.Get(URPProfileId.RG_MotionBlur)))
{
builder.AllowGlobalStateModification(true);
builder.SetRenderAttachment(destination, 0, AccessFlags.Write);
passData.sourceTexture = source;
builder.UseTexture(source, AccessFlags.Read);
if (mode == MotionBlurMode.CameraAndObjects)
{
Debug.Assert(ScriptableRenderer.current.SupportsMotionVectors(), "Current renderer does not support motion vectors.");
Debug.Assert(motionVectorColor.IsValid(), "Motion vectors are invalid. Per-object motion blur requires a motion vector texture.");
passData.motionVectors = motionVectorColor;
builder.UseTexture(motionVectorColor, AccessFlags.Read);
}
else
{
passData.motionVectors = TextureHandle.nullHandle;
}
Debug.Assert(cameraDepthTexture.IsValid(), "Camera depth texture is invalid. Per-camera motion blur requires a depth texture.");
builder.UseTexture(cameraDepthTexture, AccessFlags.Read);
passData.material = material;
passData.passIndex = passIndex;
passData.camera = cameraData.camera;
passData.xr = cameraData.xr;
passData.enableAlphaOutput = cameraData.isAlphaOutputEnabled;
passData.intensity = m_MotionBlur.intensity.value;
passData.clamp = m_MotionBlur.clamp.value;
builder.SetRenderFunc(static (MotionBlurPassData data, RasterGraphContext context) =>
{
var cmd = context.cmd;
RTHandle sourceTextureHdl = data.sourceTexture;
UpdateMotionBlurMatrices(ref data.material, data.camera, data.xr);
data.material.SetFloat("_Intensity", data.intensity);
data.material.SetFloat("_Clamp", data.clamp);
CoreUtils.SetKeyword(data.material, ShaderKeywordStrings._ENABLE_ALPHA_OUTPUT, data.enableAlphaOutput);
PostProcessUtils.SetSourceSize(cmd, data.sourceTexture);
Vector2 viewportScale = sourceTextureHdl.useScaling ? new Vector2(sourceTextureHdl.rtHandleProperties.rtHandleScale.x, sourceTextureHdl.rtHandleProperties.rtHandleScale.y) : Vector2.one;
Blitter.BlitTexture(cmd, sourceTextureHdl, viewportScale, data.material, data.passIndex);
});
return;
}
}
#endregion
#region LensFlareDataDriven
private class LensFlarePassData
{
internal TextureHandle destinationTexture;
internal UniversalCameraData cameraData;
internal Material material;
internal Rect viewport;
internal float paniniDistance;
internal float paniniCropToFit;
internal float width;
internal float height;
internal bool usePanini;
}
void LensFlareDataDrivenComputeOcclusion(RenderGraph renderGraph, UniversalResourceData resourceData, UniversalCameraData cameraData, in TextureDesc srcDesc)
{
if (!LensFlareCommonSRP.IsOcclusionRTCompatible())
return;
using (var builder = renderGraph.AddUnsafePass<LensFlarePassData>("Lens Flare Compute Occlusion", out var passData, ProfilingSampler.Get(URPProfileId.LensFlareDataDrivenComputeOcclusion)))
{
RTHandle occH = LensFlareCommonSRP.occlusionRT;
TextureHandle occlusionHandle = renderGraph.ImportTexture(LensFlareCommonSRP.occlusionRT);
passData.destinationTexture = occlusionHandle;
builder.UseTexture(occlusionHandle, AccessFlags.Write);
passData.cameraData = cameraData;
passData.viewport = cameraData.pixelRect;
passData.material = m_Materials.lensFlareDataDriven;
passData.width = (float)srcDesc.width;
passData.height = (float)srcDesc.height;
if (m_PaniniProjection.IsActive())
{
passData.usePanini = true;
passData.paniniDistance = m_PaniniProjection.distance.value;
passData.paniniCropToFit = m_PaniniProjection.cropToFit.value;
}
else
{
passData.usePanini = false;
passData.paniniDistance = 1.0f;
passData.paniniCropToFit = 1.0f;
}
builder.UseTexture(resourceData.cameraDepthTexture, AccessFlags.Read);
builder.SetRenderFunc(
static (LensFlarePassData data, UnsafeGraphContext ctx) =>
{
Camera camera = data.cameraData.camera;
XRPass xr = data.cameraData.xr;
Matrix4x4 nonJitteredViewProjMatrix0;
int xrId0;
#if ENABLE_VR && ENABLE_XR_MODULE
// Not VR or Multi-Pass
if (xr.enabled)
{
if (xr.singlePassEnabled)
{
nonJitteredViewProjMatrix0 = GL.GetGPUProjectionMatrix(data.cameraData.GetProjectionMatrixNoJitter(0), true) * data.cameraData.GetViewMatrix(0);
xrId0 = 0;
}
else
{
var gpuNonJitteredProj = GL.GetGPUProjectionMatrix(camera.projectionMatrix, true);
nonJitteredViewProjMatrix0 = gpuNonJitteredProj * camera.worldToCameraMatrix;
xrId0 = data.cameraData.xr.multipassId;
}
}
else
{
nonJitteredViewProjMatrix0 = GL.GetGPUProjectionMatrix(data.cameraData.GetProjectionMatrixNoJitter(0), true) * data.cameraData.GetViewMatrix(0);
xrId0 = 0;
}
#else
var gpuNonJitteredProj = GL.GetGPUProjectionMatrix(camera.projectionMatrix, true);
nonJitteredViewProjMatrix0 = gpuNonJitteredProj * camera.worldToCameraMatrix;
xrId0 = xr.multipassId;
#endif
LensFlareCommonSRP.ComputeOcclusion(
data.material, camera, xr, xr.multipassId,
data.width, data.height,
data.usePanini, data.paniniDistance, data.paniniCropToFit, true,
camera.transform.position,
nonJitteredViewProjMatrix0,
ctx.cmd,
false, false, null, null);
#if ENABLE_VR && ENABLE_XR_MODULE
if (xr.enabled && xr.singlePassEnabled)
{
//ctx.cmd.SetGlobalTexture(m_Depth.name, m_Depth.nameID);
for (int xrIdx = 1; xrIdx < xr.viewCount; ++xrIdx)
{
Matrix4x4 gpuVPXR = GL.GetGPUProjectionMatrix(data.cameraData.GetProjectionMatrixNoJitter(xrIdx), true) * data.cameraData.GetViewMatrix(xrIdx);
// Bypass single pass version
LensFlareCommonSRP.ComputeOcclusion(
data.material, camera, xr, xrIdx,
data.width, data.height,
data.usePanini, data.paniniDistance, data.paniniCropToFit, true,
camera.transform.position,
gpuVPXR,
ctx.cmd,
false, false, null, null);
}
}
#endif
});
}
}
public void RenderLensFlareDataDriven(RenderGraph renderGraph, UniversalResourceData resourceData, UniversalCameraData cameraData, in TextureHandle destination, in TextureDesc srcDesc)
{
using (var builder = renderGraph.AddUnsafePass<LensFlarePassData>("Lens Flare Data Driven Pass", out var passData, ProfilingSampler.Get(URPProfileId.LensFlareDataDriven)))
{
// Use WriteTexture here because DoLensFlareDataDrivenCommon will call SetRenderTarget internally.
// TODO RENDERGRAPH: convert SRP core lens flare to be rendergraph friendly
passData.destinationTexture = destination;
builder.UseTexture(destination, AccessFlags.Write);
passData.cameraData = cameraData;
passData.material = m_Materials.lensFlareDataDriven;
passData.width = (float)srcDesc.width;
passData.height = (float)srcDesc.height;
passData.viewport.x = 0.0f;
passData.viewport.y = 0.0f;
passData.viewport.width = (float)srcDesc.width;
passData.viewport.height = (float)srcDesc.height;
if (m_PaniniProjection.IsActive())
{
passData.usePanini = true;
passData.paniniDistance = m_PaniniProjection.distance.value;
passData.paniniCropToFit = m_PaniniProjection.cropToFit.value;
}
else
{
passData.usePanini = false;
passData.paniniDistance = 1.0f;
passData.paniniCropToFit = 1.0f;
}
if (LensFlareCommonSRP.IsOcclusionRTCompatible())
{
TextureHandle occlusionHandle = renderGraph.ImportTexture(LensFlareCommonSRP.occlusionRT);
builder.UseTexture(occlusionHandle, AccessFlags.Read);
}
else
{
builder.UseTexture(resourceData.cameraDepthTexture, AccessFlags.Read);
}
builder.SetRenderFunc(static (LensFlarePassData data, UnsafeGraphContext ctx) =>
{
Camera camera = data.cameraData.camera;
XRPass xr = data.cameraData.xr;
#if ENABLE_VR && ENABLE_XR_MODULE
// Not VR or Multi-Pass
if (!xr.enabled ||
(xr.enabled && !xr.singlePassEnabled))
#endif
{
var gpuNonJitteredProj = GL.GetGPUProjectionMatrix(camera.projectionMatrix, true);
Matrix4x4 nonJitteredViewProjMatrix0 = gpuNonJitteredProj * camera.worldToCameraMatrix;
LensFlareCommonSRP.DoLensFlareDataDrivenCommon(
data.material, data.cameraData.camera, data.viewport, xr, data.cameraData.xr.multipassId,
data.width, data.height,
data.usePanini, data.paniniDistance, data.paniniCropToFit,
true,
camera.transform.position,
nonJitteredViewProjMatrix0,
ctx.cmd,
false, false, null, null,
data.destinationTexture,
(Light light, Camera cam, Vector3 wo) => { return GetLensFlareLightAttenuation(light, cam, wo); },
false);
}
#if ENABLE_VR && ENABLE_XR_MODULE
else
{
for (int xrIdx = 0; xrIdx < xr.viewCount; ++xrIdx)
{
Matrix4x4 nonJitteredViewProjMatrix_k = GL.GetGPUProjectionMatrix(data.cameraData.GetProjectionMatrixNoJitter(xrIdx), true) * data.cameraData.GetViewMatrix(xrIdx);
LensFlareCommonSRP.DoLensFlareDataDrivenCommon(
data.material, data.cameraData.camera, data.viewport, xr, data.cameraData.xr.multipassId,
data.width, data.height,
data.usePanini, data.paniniDistance, data.paniniCropToFit,
true,
camera.transform.position,
nonJitteredViewProjMatrix_k,
ctx.cmd,
false, false, null, null,
data.destinationTexture,
(Light light, Camera cam, Vector3 wo) => { return GetLensFlareLightAttenuation(light, cam, wo); },
false);
}
}
#endif
});
}
}
#endregion
#region LensFlareScreenSpace
private class LensFlareScreenSpacePassData
{
internal TextureHandle streakTmpTexture;
internal TextureHandle streakTmpTexture2;
internal TextureHandle originalBloomTexture;
internal TextureHandle screenSpaceLensFlareBloomMipTexture;
internal TextureHandle result;
internal int actualWidth;
internal int actualHeight;
internal Camera camera;
internal Material material;
internal ScreenSpaceLensFlare lensFlareScreenSpace;
internal int downsample;
}
public TextureHandle RenderLensFlareScreenSpace(RenderGraph renderGraph, Camera camera, in TextureDesc srcDesc, TextureHandle originalBloomTexture, TextureHandle screenSpaceLensFlareBloomMipTexture, bool sameInputOutputTex)
{
var downsample = (int) m_LensFlareScreenSpace.resolution.value;
int flareRenderWidth = Math.Max( srcDesc.width / downsample, 1);
int flareRenderHeight = Math.Max( srcDesc.height / downsample, 1);
var streakTextureDesc = GetCompatibleDescriptor(srcDesc, flareRenderWidth, flareRenderHeight, m_DefaultColorFormat);
var streakTmpTexture = CreateCompatibleTexture(renderGraph, streakTextureDesc, "_StreakTmpTexture", true, FilterMode.Bilinear);
var streakTmpTexture2 = CreateCompatibleTexture(renderGraph, streakTextureDesc, "_StreakTmpTexture2", true, FilterMode.Bilinear);
// NOTE: Result texture is the result of the flares/streaks only. Not the final output which is "bloom + flares".
var resultTexture = CreateCompatibleTexture(renderGraph, streakTextureDesc, "_LensFlareScreenSpace", true, FilterMode.Bilinear);
using (var builder = renderGraph.AddUnsafePass<LensFlareScreenSpacePassData>("Blit Lens Flare Screen Space", out var passData, ProfilingSampler.Get(URPProfileId.LensFlareScreenSpace)))
{
// Use WriteTexture here because DoLensFlareScreenSpaceCommon will call SetRenderTarget internally.
// TODO RENDERGRAPH: convert SRP core lensflare to be rendergraph friendly
passData.streakTmpTexture = streakTmpTexture;
builder.UseTexture(streakTmpTexture, AccessFlags.ReadWrite);
passData.streakTmpTexture2 = streakTmpTexture2;
builder.UseTexture(streakTmpTexture2, AccessFlags.ReadWrite);
passData.screenSpaceLensFlareBloomMipTexture = screenSpaceLensFlareBloomMipTexture;
builder.UseTexture(screenSpaceLensFlareBloomMipTexture, AccessFlags.ReadWrite);
passData.originalBloomTexture = originalBloomTexture;
if(!sameInputOutputTex)
builder.UseTexture(originalBloomTexture, AccessFlags.ReadWrite);
passData.actualWidth = srcDesc.width;
passData.actualHeight = srcDesc.height;
passData.camera = camera;
passData.material = m_Materials.lensFlareScreenSpace;
passData.lensFlareScreenSpace = m_LensFlareScreenSpace; // NOTE: reference, assumed constant until executed.
passData.downsample = downsample;
passData.result = resultTexture;
builder.UseTexture(resultTexture, AccessFlags.ReadWrite);
builder.SetRenderFunc(static (LensFlareScreenSpacePassData data, UnsafeGraphContext context) =>
{
var cmd = context.cmd;
var camera = data.camera;
var lensFlareScreenSpace = data.lensFlareScreenSpace;
LensFlareCommonSRP.DoLensFlareScreenSpaceCommon(
data.material,
camera,
(float)data.actualWidth,
(float)data.actualHeight,
data.lensFlareScreenSpace.tintColor.value,
data.originalBloomTexture,
data.screenSpaceLensFlareBloomMipTexture,
null, // We don't have any spectral LUT in URP
data.streakTmpTexture,
data.streakTmpTexture2,
new Vector4(
lensFlareScreenSpace.intensity.value,
lensFlareScreenSpace.firstFlareIntensity.value,
lensFlareScreenSpace.secondaryFlareIntensity.value,
lensFlareScreenSpace.warpedFlareIntensity.value),
new Vector4(
lensFlareScreenSpace.vignetteEffect.value,
lensFlareScreenSpace.startingPosition.value,
lensFlareScreenSpace.scale.value,
0), // Free slot, not used
new Vector4(
lensFlareScreenSpace.samples.value,
lensFlareScreenSpace.sampleDimmer.value,
lensFlareScreenSpace.chromaticAbberationIntensity.value,
0), // No need to pass a chromatic aberration sample count, hardcoded at 3 in shader
new Vector4(
lensFlareScreenSpace.streaksIntensity.value,
lensFlareScreenSpace.streaksLength.value,
lensFlareScreenSpace.streaksOrientation.value,
lensFlareScreenSpace.streaksThreshold.value),
new Vector4(
data.downsample,
lensFlareScreenSpace.warpedFlareScale.value.x,
lensFlareScreenSpace.warpedFlareScale.value.y,
0), // Free slot, not used
cmd,
data.result,
false);
});
}
return originalBloomTexture;
}
#endregion
static private void ScaleViewport(RasterCommandBuffer cmd, RTHandle sourceTextureHdl, RTHandle dest, UniversalCameraData cameraData, bool hasFinalPass)
{
RenderTargetIdentifier cameraTarget = BuiltinRenderTextureType.CameraTarget;
#if ENABLE_VR && ENABLE_XR_MODULE
if (cameraData.xr.enabled)
cameraTarget = cameraData.xr.renderTarget;
#endif
if (dest.nameID == cameraTarget || cameraData.targetTexture != null)
{
if (hasFinalPass || !cameraData.resolveFinalTarget)
{
// Inside the camera stack the target is the shared intermediate target, which can be scaled with render scale.
// camera.pixelRect is the viewport of the final target in pixels, so it cannot be used for the intermediate target.
// On intermediate target allocation the viewport size is baked into the target size.
// Which means the intermediate target does not have a viewport rect. Its offset is always 0 and its size matches viewport size.
// The overlay cameras inherit the base viewport, so they cannot have a different viewport,
// a necessary limitation since the target covers only the base viewport area.
// The offsetting is finally done by the final output viewport-rect to the final target.
// Note: effectively this is setting a fullscreen viewport for the intermediate target.
var targetWidth = cameraData.cameraTargetDescriptor.width;
var targetHeight = cameraData.cameraTargetDescriptor.height;
var targetViewportInPixels = new Rect(
0,
0,
targetWidth,
targetHeight);
cmd.SetViewport(targetViewportInPixels);
}
else
cmd.SetViewport(cameraData.pixelRect);
}
}
static private void ScaleViewportAndBlit(RasterCommandBuffer cmd, RTHandle sourceTextureHdl, RTHandle dest, UniversalCameraData cameraData, Material material, bool hasFinalPass)
{
Vector4 scaleBias = RenderingUtils.GetFinalBlitScaleBias(sourceTextureHdl, dest, cameraData);
ScaleViewport(cmd, sourceTextureHdl, dest, cameraData, hasFinalPass);
Blitter.BlitTexture(cmd, sourceTextureHdl, scaleBias, material, 0);
}
static private void ScaleViewportAndDrawVisibilityMesh(RasterCommandBuffer cmd, RTHandle sourceTextureHdl, RTHandle dest, UniversalCameraData cameraData, Material material, bool hasFinalPass)
{
#if ENABLE_VR && ENABLE_XR_MODULE
Vector4 scaleBias = RenderingUtils.GetFinalBlitScaleBias(sourceTextureHdl, dest, cameraData);
ScaleViewport(cmd, sourceTextureHdl, dest, cameraData, hasFinalPass);
// Set property block for blit shader
MaterialPropertyBlock xrPropertyBlock = XRSystemUniversal.GetMaterialPropertyBlock();
xrPropertyBlock.SetVector(Shader.PropertyToID("_BlitScaleBias"), scaleBias);
xrPropertyBlock.SetTexture(Shader.PropertyToID("_BlitTexture"), sourceTextureHdl);
cameraData.xr.RenderVisibleMeshCustomMaterial(cmd, cameraData.xr.occlusionMeshScale, material, xrPropertyBlock, 1, cameraData.IsRenderTargetProjectionMatrixFlipped(dest));
#endif
}
#region FinalPass
private class PostProcessingFinalSetupPassData
{
internal TextureHandle destinationTexture;
internal TextureHandle sourceTexture;
internal Material material;
internal UniversalCameraData cameraData;
}
public void RenderFinalSetup(RenderGraph renderGraph, UniversalCameraData cameraData, in TextureHandle source, in TextureHandle destination, ref FinalBlitSettings settings)
{
// Scaled FXAA
using (var builder = renderGraph.AddRasterRenderPass<PostProcessingFinalSetupPassData>("Postprocessing Final Setup Pass", out var passData, ProfilingSampler.Get(URPProfileId.RG_FinalSetup)))
{
Material material = m_Materials.scalingSetup;
material.shaderKeywords = null;
if (settings.isFxaaEnabled)
material.EnableKeyword(ShaderKeywordStrings.Fxaa);
if (settings.isFsrEnabled)
material.EnableKeyword(settings.hdrOperations.HasFlag(HDROutputUtils.Operation.ColorEncoding) ? ShaderKeywordStrings.Gamma20AndHDRInput : ShaderKeywordStrings.Gamma20);
if (settings.hdrOperations.HasFlag(HDROutputUtils.Operation.ColorEncoding))
SetupHDROutput(cameraData.hdrDisplayInformation, cameraData.hdrDisplayColorGamut, material, settings.hdrOperations, cameraData.rendersOverlayUI);
if (settings.isAlphaOutputEnabled)
CoreUtils.SetKeyword(material, ShaderKeywordStrings._ENABLE_ALPHA_OUTPUT, settings.isAlphaOutputEnabled);
builder.AllowGlobalStateModification(true);
passData.destinationTexture = destination;
builder.SetRenderAttachment(destination, 0, AccessFlags.Write);
passData.sourceTexture = source;
builder.UseTexture(source, AccessFlags.Read);
passData.cameraData = cameraData;
passData.material = material;
builder.SetRenderFunc(static (PostProcessingFinalSetupPassData data, RasterGraphContext context) =>
{
var cmd = context.cmd;
RTHandle sourceTextureHdl = data.sourceTexture;
PostProcessUtils.SetSourceSize(cmd, sourceTextureHdl);
bool hasFinalPass = true; // This is a pass just before final pass. Viewport must match intermediate target.
ScaleViewportAndBlit(context.cmd, sourceTextureHdl, data.destinationTexture, data.cameraData, data.material, hasFinalPass);
});
return;
}
}
private class PostProcessingFinalFSRScalePassData
{
internal TextureHandle sourceTexture;
internal Material material;
internal bool enableAlphaOutput;
internal Vector2 fsrInputSize;
internal Vector2 fsrOutputSize;
}
public void RenderFinalFSRScale(RenderGraph renderGraph, in TextureHandle source, in TextureDesc srcDesc, in TextureHandle destination, in TextureDesc dstDesc, bool enableAlphaOutput)
{
// FSR upscale
m_Materials.easu.shaderKeywords = null;
using (var builder = renderGraph.AddRasterRenderPass<PostProcessingFinalFSRScalePassData>("Postprocessing Final FSR Scale Pass", out var passData, ProfilingSampler.Get(URPProfileId.RG_FinalFSRScale)))
{
builder.AllowGlobalStateModification(true);
builder.SetRenderAttachment(destination, 0, AccessFlags.Write);
passData.sourceTexture = source;
builder.UseTexture(source, AccessFlags.Read);
passData.material = m_Materials.easu;
passData.enableAlphaOutput = enableAlphaOutput;
passData.fsrInputSize = new Vector2(srcDesc.width, srcDesc.height);
passData.fsrOutputSize = new Vector2(dstDesc.width, dstDesc.height);
builder.SetRenderFunc(static (PostProcessingFinalFSRScalePassData data, RasterGraphContext context) =>
{
var cmd = context.cmd;
var sourceTex = data.sourceTexture;
var material = data.material;
var enableAlphaOutput = data.enableAlphaOutput;
RTHandle sourceHdl = (RTHandle)sourceTex;
FSRUtils.SetEasuConstants(cmd, data.fsrInputSize, data.fsrInputSize, data.fsrOutputSize);
CoreUtils.SetKeyword(material, ShaderKeywordStrings._ENABLE_ALPHA_OUTPUT, enableAlphaOutput);
Vector2 viewportScale = sourceHdl.useScaling ? new Vector2(sourceHdl.rtHandleProperties.rtHandleScale.x, sourceHdl.rtHandleProperties.rtHandleScale.y) : Vector2.one;
Blitter.BlitTexture(cmd, sourceHdl, viewportScale, material, 0);
});
return;
}
}
private class PostProcessingFinalBlitPassData
{
internal TextureHandle destinationTexture;
internal TextureHandle sourceTexture;
internal Material material;
internal UniversalCameraData cameraData;
internal FinalBlitSettings settings;
}
/// <summary>
/// Final blit settings.
/// </summary>
public struct FinalBlitSettings
{
/// <summary>Is FXAA enabled</summary>
public bool isFxaaEnabled;
/// <summary>Is FSR Enabled.</summary>
public bool isFsrEnabled;
/// <summary>Is TAA sharpening enabled.</summary>
public bool isTaaSharpeningEnabled;
/// <summary>True if final blit requires HDR output.</summary>
public bool requireHDROutput;
/// <summary>True if final blit needs to resolve to debug screen.</summary>
public bool resolveToDebugScreen;
/// <summary>True if final blit needs to output alpha channel.</summary>
public bool isAlphaOutputEnabled;
/// <summary>HDR Operations</summary>
public HDROutputUtils.Operation hdrOperations;
/// <summary>
/// Create FinalBlitSettings
/// </summary>
/// <returns>New FinalBlitSettings</returns>
public static FinalBlitSettings Create()
{
FinalBlitSettings s = new FinalBlitSettings();
s.isFxaaEnabled = false;
s.isFsrEnabled = false;
s.isTaaSharpeningEnabled = false;
s.requireHDROutput = false;
s.resolveToDebugScreen = false;
s.isAlphaOutputEnabled = false;
s.hdrOperations = HDROutputUtils.Operation.None;
return s;
}
};
public void RenderFinalBlit(RenderGraph renderGraph, UniversalCameraData cameraData, in TextureHandle source, in TextureHandle overlayUITexture, in TextureHandle postProcessingTarget, ref FinalBlitSettings settings)
{
using (var builder = renderGraph.AddRasterRenderPass<PostProcessingFinalBlitPassData>("Postprocessing Final Blit Pass", out var passData, ProfilingSampler.Get(URPProfileId.RG_FinalBlit)))
{
builder.AllowGlobalStateModification(true);
passData.destinationTexture = postProcessingTarget;
builder.SetRenderAttachment(postProcessingTarget, 0, AccessFlags.Write);
passData.sourceTexture = source;
builder.UseTexture(source, AccessFlags.Read);
passData.cameraData = cameraData;
passData.material = m_Materials.finalPass;
passData.settings = settings;
if (settings.requireHDROutput && m_EnableColorEncodingIfNeeded && cameraData.rendersOverlayUI)
builder.UseTexture(overlayUITexture, AccessFlags.Read);
#if ENABLE_VR && ENABLE_XR_MODULE
if (cameraData.xr.enabled)
{
// This is a screen-space pass, make sure foveated rendering is disabled for non-uniform renders
bool passSupportsFoveation = !XRSystem.foveatedRenderingCaps.HasFlag(FoveatedRenderingCaps.NonUniformRaster);
builder.EnableFoveatedRasterization(cameraData.xr.supportsFoveatedRendering && passSupportsFoveation);
}
#endif
builder.SetRenderFunc(static (PostProcessingFinalBlitPassData data, RasterGraphContext context) =>
{
var cmd = context.cmd;
var material = data.material;
var isFxaaEnabled = data.settings.isFxaaEnabled;
var isFsrEnabled = data.settings.isFsrEnabled;
var isRcasEnabled = data.settings.isTaaSharpeningEnabled;
var requireHDROutput = data.settings.requireHDROutput;
var resolveToDebugScreen = data.settings.resolveToDebugScreen;
var isAlphaOutputEnabled = data.settings.isAlphaOutputEnabled;
RTHandle sourceTextureHdl = data.sourceTexture;
RTHandle destinationTextureHdl = data.destinationTexture;
PostProcessUtils.SetSourceSize(cmd, data.sourceTexture);
if (isFxaaEnabled)
material.EnableKeyword(ShaderKeywordStrings.Fxaa);
if (isFsrEnabled)
{
// RCAS
// Use the override value if it's available, otherwise use the default.
float sharpness = data.cameraData.fsrOverrideSharpness ? data.cameraData.fsrSharpness : FSRUtils.kDefaultSharpnessLinear;
// Set up the parameters for the RCAS pass unless the sharpness value indicates that it wont have any effect.
if (data.cameraData.fsrSharpness > 0.0f)
{
// RCAS is performed during the final post blit, but we set up the parameters here for better logical grouping.
material.EnableKeyword(requireHDROutput ? ShaderKeywordStrings.EasuRcasAndHDRInput : ShaderKeywordStrings.Rcas);
FSRUtils.SetRcasConstantsLinear(cmd, sharpness);
}
}
else if (isRcasEnabled) // RCAS only
{
// Reuse RCAS as a standalone sharpening filter for TAA.
// If FSR is enabled then it overrides the sharpening/TAA setting and we skip it.
material.EnableKeyword(ShaderKeywordStrings.Rcas);
FSRUtils.SetRcasConstantsLinear(cmd, data.cameraData.taaSettings.contrastAdaptiveSharpening);
}
if (isAlphaOutputEnabled)
CoreUtils.SetKeyword(material, ShaderKeywordStrings._ENABLE_ALPHA_OUTPUT, isAlphaOutputEnabled);
bool isRenderToBackBufferTarget = !data.cameraData.isSceneViewCamera;
#if ENABLE_VR && ENABLE_XR_MODULE
if (data.cameraData.xr.enabled)
isRenderToBackBufferTarget = destinationTextureHdl == data.cameraData.xr.renderTarget;
#endif
// HDR debug views force-renders to DebugScreenTexture.
isRenderToBackBufferTarget &= !resolveToDebugScreen;
Vector2 viewportScale = sourceTextureHdl.useScaling ? new Vector2(sourceTextureHdl.rtHandleProperties.rtHandleScale.x, sourceTextureHdl.rtHandleProperties.rtHandleScale.y) : Vector2.one;
// We y-flip if
// 1) we are blitting from render texture to back buffer(UV starts at bottom) and
// 2) renderTexture starts UV at top
bool yflip = isRenderToBackBufferTarget && data.cameraData.targetTexture == null && SystemInfo.graphicsUVStartsAtTop;
Vector4 scaleBias = yflip ? new Vector4(viewportScale.x, -viewportScale.y, 0, viewportScale.y) : new Vector4(viewportScale.x, viewportScale.y, 0, 0);
cmd.SetViewport(data.cameraData.pixelRect);
#if ENABLE_VR && ENABLE_XR_MODULE
if (data.cameraData.xr.enabled && data.cameraData.xr.hasValidVisibleMesh)
{
MaterialPropertyBlock xrPropertyBlock = XRSystemUniversal.GetMaterialPropertyBlock();
xrPropertyBlock.SetVector(Shader.PropertyToID("_BlitScaleBias"), scaleBias);
xrPropertyBlock.SetTexture(Shader.PropertyToID("_BlitTexture"), sourceTextureHdl);
data.cameraData.xr.RenderVisibleMeshCustomMaterial(cmd, data.cameraData.xr.occlusionMeshScale, material, xrPropertyBlock, 1, !yflip);
}
else
#endif
Blitter.BlitTexture(cmd, sourceTextureHdl, scaleBias, material, 0);
});
return;
}
}
public void RenderFinalPassRenderGraph(RenderGraph renderGraph, ContextContainer frameData, in TextureHandle source, in TextureHandle overlayUITexture, in TextureHandle postProcessingTarget, bool enableColorEncodingIfNeeded)
{
var stack = VolumeManager.instance.stack;
m_Tonemapping = stack.GetComponent<Tonemapping>();
m_FilmGrain = stack.GetComponent<FilmGrain>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
var material = m_Materials.finalPass;
material.shaderKeywords = null;
FinalBlitSettings settings = FinalBlitSettings.Create();
var srcDesc = renderGraph.GetTextureDesc(source);
var upscaledDesc = srcDesc;
upscaledDesc.width = cameraData.pixelWidth;
upscaledDesc.height = cameraData.pixelHeight;
// TODO RENDERGRAPH: when we remove the old path we should review the naming of these variables...
// m_HasFinalPass is used to let FX passes know when they are not being called by the actual final pass, so they can skip any "final work"
m_HasFinalPass = false;
// m_IsFinalPass is used by effects called by RenderFinalPassRenderGraph, so we let them know that we are in a final PP pass
m_IsFinalPass = true;
m_EnableColorEncodingIfNeeded = enableColorEncodingIfNeeded;
if (m_FilmGrain.IsActive())
{
material.EnableKeyword(ShaderKeywordStrings.FilmGrain);
PostProcessUtils.ConfigureFilmGrain(
m_Data,
m_FilmGrain,
upscaledDesc.width, upscaledDesc.height,
material
);
}
if (cameraData.isDitheringEnabled)
{
material.EnableKeyword(ShaderKeywordStrings.Dithering);
m_DitheringTextureIndex = PostProcessUtils.ConfigureDithering(
m_Data,
m_DitheringTextureIndex,
upscaledDesc.width, upscaledDesc.height,
material
);
}
if (RequireSRGBConversionBlitToBackBuffer(cameraData.requireSrgbConversion))
material.EnableKeyword(ShaderKeywordStrings.LinearToSRGBConversion);
settings.hdrOperations = HDROutputUtils.Operation.None;
settings.requireHDROutput = RequireHDROutput(cameraData);
if (settings.requireHDROutput)
{
// If there is a final post process pass, it's always the final pass so do color encoding
settings.hdrOperations = m_EnableColorEncodingIfNeeded ? HDROutputUtils.Operation.ColorEncoding : HDROutputUtils.Operation.None;
// If the color space conversion wasn't applied by the uber pass, do it here
if (!cameraData.postProcessEnabled)
settings.hdrOperations |= HDROutputUtils.Operation.ColorConversion;
SetupHDROutput(cameraData.hdrDisplayInformation, cameraData.hdrDisplayColorGamut, material, settings.hdrOperations, cameraData.rendersOverlayUI);
}
DebugHandler debugHandler = GetActiveDebugHandler(cameraData);
bool resolveToDebugScreen = debugHandler != null && debugHandler.WriteToDebugScreenTexture(cameraData.resolveFinalTarget);
debugHandler?.UpdateShaderGlobalPropertiesForFinalValidationPass(renderGraph, cameraData, !m_HasFinalPass && !resolveToDebugScreen);
settings.resolveToDebugScreen = resolveToDebugScreen;
settings.isAlphaOutputEnabled = cameraData.isAlphaOutputEnabled;
settings.isFxaaEnabled = (cameraData.antialiasing == AntialiasingMode.FastApproximateAntialiasing);
settings.isFsrEnabled = ((cameraData.imageScalingMode == ImageScalingMode.Upscaling) && (cameraData.upscalingFilter == ImageUpscalingFilter.FSR));
// Reuse RCAS pass as an optional standalone post sharpening pass for TAA.
// This avoids the cost of EASU and is available for other upscaling options.
// If FSR is enabled then FSR settings override the TAA settings and we perform RCAS only once.
// If STP is enabled, then TAA sharpening has already been performed inside STP.
settings.isTaaSharpeningEnabled = (cameraData.IsTemporalAAEnabled() && cameraData.taaSettings.contrastAdaptiveSharpening > 0.0f) && !settings.isFsrEnabled && !cameraData.IsSTPEnabled();
var tempRtDesc = srcDesc;
// Select a UNORM format since we've already performed tonemapping. (Values are in 0-1 range)
// This improves precision and is required if we want to avoid excessive banding when FSR is in use.
if (!settings.requireHDROutput)
tempRtDesc.format = UniversalRenderPipeline.MakeUnormRenderTextureGraphicsFormat();
var scalingSetupTarget = CreateCompatibleTexture(renderGraph, tempRtDesc, "scalingSetupTarget", true, FilterMode.Point);
var upScaleTarget = CreateCompatibleTexture(renderGraph, upscaledDesc, "_UpscaledTexture", true, FilterMode.Point);
var currentSource = source;
if (cameraData.imageScalingMode != ImageScalingMode.None)
{
// When FXAA is enabled in scaled renders, we execute it in a separate blit since it's not designed to be used in
// situations where the input and output resolutions do not match.
// When FSR is active, we always need an additional pass since it has a very particular color encoding requirement.
// NOTE: An ideal implementation could inline this color conversion logic into the UberPost pass, but the current code structure would make
// this process very complex. Specifically, we'd need to guarantee that the uber post output is always written to a UNORM format render
// target in order to preserve the precision of specially encoded color data.
bool isSetupRequired = (settings.isFxaaEnabled || settings.isFsrEnabled);
// When FXAA is needed while scaling is active, we must perform it before the scaling takes place.
if (isSetupRequired)
{
RenderFinalSetup(renderGraph, cameraData, in currentSource, in scalingSetupTarget, ref settings);
currentSource = scalingSetupTarget;
// Indicate that we no longer need to perform FXAA in the final pass since it was already perfomed here.
settings.isFxaaEnabled = false;
}
switch (cameraData.imageScalingMode)
{
case ImageScalingMode.Upscaling:
{
switch (cameraData.upscalingFilter)
{
case ImageUpscalingFilter.Point:
{
// TAA post sharpening is an RCAS pass, avoid overriding it with point sampling.
if (!settings.isTaaSharpeningEnabled)
material.EnableKeyword(ShaderKeywordStrings.PointSampling);
break;
}
case ImageUpscalingFilter.Linear:
{
break;
}
case ImageUpscalingFilter.FSR:
{
RenderFinalFSRScale(renderGraph, in currentSource, in srcDesc, in upScaleTarget, in upscaledDesc, settings.isAlphaOutputEnabled);
currentSource = upScaleTarget;
break;
}
}
break;
}
case ImageScalingMode.Downscaling:
{
// In the downscaling case, we don't perform any sort of filter override logic since we always want linear filtering
// and it's already the default option in the shader.
// Also disable TAA post sharpening pass when downscaling.
settings.isTaaSharpeningEnabled = false;
break;
}
}
}
else if (settings.isFxaaEnabled)
{
// In unscaled renders, FXAA can be safely performed in the FinalPost shader
material.EnableKeyword(ShaderKeywordStrings.Fxaa);
}
RenderFinalBlit(renderGraph, cameraData, in currentSource, in overlayUITexture, in postProcessingTarget, ref settings);
}
#endregion
#region UberPost
private class UberPostPassData
{
internal TextureHandle destinationTexture;
internal TextureHandle sourceTexture;
internal TextureHandle lutTexture;
internal TextureHandle bloomTexture;
internal Vector4 lutParams;
internal TextureHandle userLutTexture;
internal Vector4 userLutParams;
internal Material material;
internal UniversalCameraData cameraData;
internal TonemappingMode toneMappingMode;
internal bool isHdrGrading;
internal bool isBackbuffer;
internal bool enableAlphaOutput;
internal bool hasFinalPass;
}
TextureHandle TryGetCachedUserLutTextureHandle(RenderGraph renderGraph)
{
if (m_ColorLookup.texture.value == null)
{
if (m_UserLut != null)
{
m_UserLut.Release();
m_UserLut = null;
}
}
else
{
if (m_UserLut == null || m_UserLut.externalTexture != m_ColorLookup.texture.value)
{
m_UserLut?.Release();
m_UserLut = RTHandles.Alloc(m_ColorLookup.texture.value);
}
}
return m_UserLut != null ? renderGraph.ImportTexture(m_UserLut) : TextureHandle.nullHandle;
}
public void RenderUberPost(RenderGraph renderGraph, ContextContainer frameData, UniversalCameraData cameraData, UniversalPostProcessingData postProcessingData,
in TextureHandle sourceTexture, in TextureHandle destTexture, in TextureHandle lutTexture, in TextureHandle bloomTexture, in TextureHandle overlayUITexture,
bool requireHDROutput, bool enableAlphaOutput, bool resolveToDebugScreen, bool hasFinalPass)
{
var material = m_Materials.uber;
bool hdrGrading = postProcessingData.gradingMode == ColorGradingMode.HighDynamicRange;
int lutHeight = postProcessingData.lutSize;
int lutWidth = lutHeight * lutHeight;
// Source material setup
float postExposureLinear = Mathf.Pow(2f, m_ColorAdjustments.postExposure.value);
Vector4 lutParams = new Vector4(1f / lutWidth, 1f / lutHeight, lutHeight - 1f, postExposureLinear);
TextureHandle userLutTexture = TryGetCachedUserLutTextureHandle(renderGraph);
Vector4 userLutParams = !m_ColorLookup.IsActive()
? Vector4.zero
: new Vector4(1f / m_ColorLookup.texture.value.width,
1f / m_ColorLookup.texture.value.height,
m_ColorLookup.texture.value.height - 1f,
m_ColorLookup.contribution.value);
using (var builder = renderGraph.AddRasterRenderPass<UberPostPassData>("Blit Post Processing", out var passData, ProfilingSampler.Get(URPProfileId.RG_UberPost)))
{
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
#if ENABLE_VR && ENABLE_XR_MODULE
if (cameraData.xr.enabled)
{
bool passSupportsFoveation = cameraData.xrUniversal.canFoveateIntermediatePasses || resourceData.isActiveTargetBackBuffer;
// This is a screen-space pass, make sure foveated rendering is disabled for non-uniform renders
passSupportsFoveation &= !XRSystem.foveatedRenderingCaps.HasFlag(FoveatedRenderingCaps.NonUniformRaster);
builder.EnableFoveatedRasterization(cameraData.xr.supportsFoveatedRendering && passSupportsFoveation);
}
#endif
builder.AllowGlobalStateModification(true);
passData.destinationTexture = destTexture;
builder.SetRenderAttachment(destTexture, 0, AccessFlags.Write);
passData.sourceTexture = sourceTexture;
builder.UseTexture(sourceTexture, AccessFlags.Read);
passData.lutTexture = lutTexture;
builder.UseTexture(lutTexture, AccessFlags.Read);
passData.lutParams = lutParams;
passData.userLutTexture = userLutTexture; // This can be null if ColorLookup is not active.
if (userLutTexture.IsValid())
builder.UseTexture(userLutTexture, AccessFlags.Read);
if (m_Bloom.IsActive())
{
builder.UseTexture(bloomTexture, AccessFlags.Read);
passData.bloomTexture = bloomTexture;
}
if (requireHDROutput && m_EnableColorEncodingIfNeeded && overlayUITexture.IsValid())
builder.UseTexture(overlayUITexture, AccessFlags.Read);
passData.userLutParams = userLutParams;
passData.cameraData = cameraData;
passData.material = material;
passData.toneMappingMode = m_Tonemapping.mode.value;
passData.isHdrGrading = hdrGrading;
passData.enableAlphaOutput = enableAlphaOutput;
passData.hasFinalPass = hasFinalPass;
builder.SetRenderFunc(static (UberPostPassData data, RasterGraphContext context) =>
{
var cmd = context.cmd;
var camera = data.cameraData.camera;
var material = data.material;
RTHandle sourceTextureHdl = data.sourceTexture;
material.SetTexture(ShaderConstants._InternalLut, data.lutTexture);
material.SetVector(ShaderConstants._Lut_Params, data.lutParams);
material.SetTexture(ShaderConstants._UserLut, data.userLutTexture);
material.SetVector(ShaderConstants._UserLut_Params, data.userLutParams);
if (data.bloomTexture.IsValid())
{
material.SetTexture(ShaderConstants._Bloom_Texture, data.bloomTexture);
}
if (data.isHdrGrading)
{
material.EnableKeyword(ShaderKeywordStrings.HDRGrading);
}
else
{
switch (data.toneMappingMode)
{
case TonemappingMode.Neutral: material.EnableKeyword(ShaderKeywordStrings.TonemapNeutral); break;
case TonemappingMode.ACES: material.EnableKeyword(ShaderKeywordStrings.TonemapACES); break;
default: break; // None
}
}
CoreUtils.SetKeyword(material, ShaderKeywordStrings._ENABLE_ALPHA_OUTPUT, data.enableAlphaOutput);
// Done with Uber, blit it
#if ENABLE_VR && ENABLE_XR_MODULE
if (data.cameraData.xr.enabled && data.cameraData.xr.hasValidVisibleMesh)
ScaleViewportAndDrawVisibilityMesh(cmd, sourceTextureHdl, data.destinationTexture, data.cameraData, material, data.hasFinalPass);
else
#endif
ScaleViewportAndBlit(cmd, sourceTextureHdl, data.destinationTexture, data.cameraData, material, data.hasFinalPass);
});
return;
}
}
#endregion
private class PostFXSetupPassData { }
public void RenderPostProcessingRenderGraph(RenderGraph renderGraph, ContextContainer frameData, in TextureHandle activeCameraColorTexture, in TextureHandle lutTexture, in TextureHandle overlayUITexture, in TextureHandle postProcessingTarget, bool hasFinalPass, bool resolveToDebugScreen, bool enableColorEndingIfNeeded)
{
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
UniversalRenderingData renderingData = frameData.Get<UniversalRenderingData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
UniversalPostProcessingData postProcessingData = frameData.Get<UniversalPostProcessingData>();
var stack = VolumeManager.instance.stack;
m_DepthOfField = stack.GetComponent<DepthOfField>();
m_MotionBlur = stack.GetComponent<MotionBlur>();
m_PaniniProjection = stack.GetComponent<PaniniProjection>();
m_Bloom = stack.GetComponent<Bloom>();
m_LensFlareScreenSpace = stack.GetComponent<ScreenSpaceLensFlare>();
m_LensDistortion = stack.GetComponent<LensDistortion>();
m_ChromaticAberration = stack.GetComponent<ChromaticAberration>();
m_Vignette = stack.GetComponent<Vignette>();
m_ColorLookup = stack.GetComponent<ColorLookup>();
m_ColorAdjustments = stack.GetComponent<ColorAdjustments>();
m_Tonemapping = stack.GetComponent<Tonemapping>();
m_FilmGrain = stack.GetComponent<FilmGrain>();
m_UseFastSRGBLinearConversion = postProcessingData.useFastSRGBLinearConversion;
m_SupportDataDrivenLensFlare = postProcessingData.supportDataDrivenLensFlare;
m_SupportScreenSpaceLensFlare = postProcessingData.supportScreenSpaceLensFlare;
m_HasFinalPass = hasFinalPass;
m_EnableColorEncodingIfNeeded = enableColorEndingIfNeeded;
ref ScriptableRenderer renderer = ref cameraData.renderer;
bool isSceneViewCamera = cameraData.isSceneViewCamera;
//We blit back and forth without msaa untill the last blit.
bool useStopNan = cameraData.isStopNaNEnabled && m_Materials.stopNaN != null;
bool useSubPixelMorpAA = cameraData.antialiasing == AntialiasingMode.SubpixelMorphologicalAntiAliasing;
var dofMaterial = m_DepthOfField.mode.value == DepthOfFieldMode.Gaussian ? m_Materials.gaussianDepthOfField : m_Materials.bokehDepthOfField;
bool useDepthOfField = m_DepthOfField.IsActive() && !isSceneViewCamera && dofMaterial != null;
bool useLensFlare = !LensFlareCommonSRP.Instance.IsEmpty() && m_SupportDataDrivenLensFlare;
bool useLensFlareScreenSpace = m_LensFlareScreenSpace.IsActive() && m_SupportScreenSpaceLensFlare;
bool useMotionBlur = m_MotionBlur.IsActive() && !isSceneViewCamera;
bool usePaniniProjection = m_PaniniProjection.IsActive() && !isSceneViewCamera;
bool isFsrEnabled = ((cameraData.imageScalingMode == ImageScalingMode.Upscaling) && (cameraData.upscalingFilter == ImageUpscalingFilter.FSR));
// Disable MotionBlur in EditMode, so that editing remains clear and readable.
// NOTE: HDRP does the same via CoreUtils::AreAnimatedMaterialsEnabled().
// Disable MotionBlurMode.CameraAndObjects on renderers that do not support motion vectors
useMotionBlur = useMotionBlur && Application.isPlaying;
if (useMotionBlur && m_MotionBlur.mode.value == MotionBlurMode.CameraAndObjects)
{
useMotionBlur &= renderer.SupportsMotionVectors();
if (!useMotionBlur)
{
var warning = "Disabling Motion Blur for Camera And Objects because the renderer does not implement motion vectors.";
const int warningThrottleFrames = 60 * 1; // 60 FPS * 1 sec
if (Time.frameCount % warningThrottleFrames == 0)
Debug.LogWarning(warning);
}
}
// Note that enabling jitters uses the same CameraData::IsTemporalAAEnabled(). So if we add any other kind of overrides (like
// disable useTemporalAA if another feature is disabled) then we need to put it in CameraData::IsTemporalAAEnabled() as opposed
// to tweaking the value here.
bool useTemporalAA = cameraData.IsTemporalAAEnabled();
// STP is only enabled when TAA is enabled and all of its runtime requirements are met.
// Using IsSTPRequested() vs IsSTPEnabled() for perf reason here, as we already know TAA status
bool isSTPRequested = cameraData.IsSTPRequested();
bool useSTP = useTemporalAA && isSTPRequested;
// Warn users if TAA and STP are disabled despite being requested
if (!useTemporalAA && cameraData.IsTemporalAARequested())
TemporalAA.ValidateAndWarn(cameraData, isSTPRequested);
using (var builder = renderGraph.AddRasterRenderPass<PostFXSetupPassData>("Setup PostFX passes", out var passData,
ProfilingSampler.Get(URPProfileId.RG_SetupPostFX)))
{
builder.AllowGlobalStateModification(true);
builder.SetRenderFunc(static (PostFXSetupPassData data, RasterGraphContext context) =>
{
// Setup projection matrix for cmd.DrawMesh()
context.cmd.SetGlobalMatrix(ShaderConstants._FullscreenProjMat, GL.GetGPUProjectionMatrix(Matrix4x4.identity, true));
});
}
TextureHandle currentSource = activeCameraColorTexture;
// Optional NaN killer before post-processing kicks in
// stopNaN may be null on Adreno 3xx. It doesn't support full shader level 3.5, but SystemInfo.graphicsShaderLevel is 35.
if (useStopNan)
{
RenderStopNaN(renderGraph, in currentSource, out var stopNaNTarget);
currentSource = stopNaNTarget;
}
if(useSubPixelMorpAA)
{
RenderSMAA(renderGraph, resourceData, cameraData.antialiasingQuality, in currentSource, out var SMAATarget);
currentSource = SMAATarget;
}
// Depth of Field
// Adreno 3xx SystemInfo.graphicsShaderLevel is 35, but instancing support is disabled due to buggy drivers.
// DOF shader uses #pragma target 3.5 which adds requirement for instancing support, thus marking the shader unsupported on those devices.
if (useDepthOfField)
{
RenderDoF(renderGraph, resourceData, cameraData, in currentSource, out var DoFTarget);
currentSource = DoFTarget;
}
// Temporal Anti Aliasing
if (useTemporalAA)
{
if (useSTP)
{
RenderSTP(renderGraph, resourceData, cameraData, ref currentSource, out var StpTarget);
currentSource = StpTarget;
}
else
{
RenderTemporalAA(renderGraph, resourceData, cameraData, ref currentSource, out var TemporalAATarget);
currentSource = TemporalAATarget;
}
}
if(useMotionBlur)
{
RenderMotionBlur(renderGraph, resourceData, cameraData, in currentSource, out var MotionBlurTarget);
currentSource = MotionBlurTarget;
}
if(usePaniniProjection)
{
RenderPaniniProjection(renderGraph, cameraData.camera, in currentSource, out var PaniniTarget);
currentSource = PaniniTarget;
}
// Uberpost
{
// Reset uber keywords
m_Materials.uber.shaderKeywords = null;
var srcDesc = currentSource.GetDescriptor(renderGraph);
// Bloom goes first
TextureHandle bloomTexture = TextureHandle.nullHandle;
bool bloomActive = m_Bloom.IsActive();
//Even if bloom is not active we need the texture if the lensFlareScreenSpace pass is active.
if (bloomActive || useLensFlareScreenSpace)
{
RenderBloomTexture(renderGraph, currentSource, out bloomTexture, cameraData.isAlphaOutputEnabled);
if (useLensFlareScreenSpace)
{
int maxBloomMip = Mathf.Clamp(m_LensFlareScreenSpace.bloomMip.value, 0, m_Bloom.maxIterations.value/2);
bool sameInputOutputTex = maxBloomMip == 0;
bloomTexture = RenderLensFlareScreenSpace(renderGraph, cameraData.camera, srcDesc, bloomTexture, _BloomMipUp[maxBloomMip], sameInputOutputTex);
}
UberPostSetupBloomPass(renderGraph, m_Materials.uber, srcDesc);
}
if (useLensFlare)
{
LensFlareDataDrivenComputeOcclusion(renderGraph, resourceData, cameraData, srcDesc);
RenderLensFlareDataDriven(renderGraph, resourceData, cameraData, in currentSource, in srcDesc);
}
// TODO RENDERGRAPH: Once we started removing the non-RG code pass in URP, we should move functions below to renderfunc so that material setup happens at
// the same timeline of executing the rendergraph. Keep them here for now so we cound reuse non-RG code to reduce maintainance cost.
SetupLensDistortion(m_Materials.uber, isSceneViewCamera);
SetupChromaticAberration(m_Materials.uber);
SetupVignette(m_Materials.uber, cameraData.xr, srcDesc.width, srcDesc.height);
SetupGrain(cameraData, m_Materials.uber);
SetupDithering(cameraData, m_Materials.uber);
if (RequireSRGBConversionBlitToBackBuffer(cameraData.requireSrgbConversion))
m_Materials.uber.EnableKeyword(ShaderKeywordStrings.LinearToSRGBConversion);
if (m_UseFastSRGBLinearConversion)
{
m_Materials.uber.EnableKeyword(ShaderKeywordStrings.UseFastSRGBLinearConversion);
}
bool requireHDROutput = RequireHDROutput(cameraData);
if (requireHDROutput)
{
// Color space conversion is already applied through color grading, do encoding if uber post is the last pass
// Otherwise encoding will happen in the final post process pass or the final blit pass
HDROutputUtils.Operation hdrOperations = !m_HasFinalPass && m_EnableColorEncodingIfNeeded ? HDROutputUtils.Operation.ColorEncoding : HDROutputUtils.Operation.None;
SetupHDROutput(cameraData.hdrDisplayInformation, cameraData.hdrDisplayColorGamut, m_Materials.uber, hdrOperations, cameraData.rendersOverlayUI);
}
bool enableAlphaOutput = cameraData.isAlphaOutputEnabled;
DebugHandler debugHandler = GetActiveDebugHandler(cameraData);
debugHandler?.UpdateShaderGlobalPropertiesForFinalValidationPass(renderGraph, cameraData, !m_HasFinalPass && !resolveToDebugScreen);
RenderUberPost(renderGraph, frameData, cameraData, postProcessingData, in currentSource, in postProcessingTarget, in lutTexture, in bloomTexture, in overlayUITexture, requireHDROutput, enableAlphaOutput, resolveToDebugScreen, hasFinalPass);
}
}
}
}