2110 lines
110 KiB
C#
2110 lines
110 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 local descriptor and the camera data descriptor to reflect post-upscaled sizes
|
|
m_Descriptor.width = newCameraTargetSize.x;
|
|
m_Descriptor.height = newCameraTargetSize.y;
|
|
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 so we need to set some special flags to ensure it isn't culled or optimized away
|
|
builder.AllowGlobalStateModification(true);
|
|
builder.AllowPassCulling(false);
|
|
|
|
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
|
|
)
|
|
);
|
|
});
|
|
}
|
|
}
|
|
|
|
#region StopNaNs
|
|
private class StopNaNsPassData
|
|
{
|
|
internal TextureHandle stopNaNTarget;
|
|
internal TextureHandle sourceTexture;
|
|
internal Material stopNaN;
|
|
}
|
|
|
|
public void RenderStopNaN(RenderGraph renderGraph, RenderTextureDescriptor cameraTargetDescriptor, in TextureHandle activeCameraColor, out TextureHandle stopNaNTarget)
|
|
{
|
|
var desc = PostProcessPass.GetCompatibleDescriptor(cameraTargetDescriptor,
|
|
cameraTargetDescriptor.width,
|
|
cameraTargetDescriptor.height,
|
|
cameraTargetDescriptor.graphicsFormat,
|
|
GraphicsFormat.None);
|
|
|
|
stopNaNTarget = UniversalRenderer.CreateRenderGraphTexture(renderGraph, desc, "_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 destinationTexture;
|
|
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 desc = PostProcessPass.GetCompatibleDescriptor(m_Descriptor,
|
|
m_Descriptor.width,
|
|
m_Descriptor.height,
|
|
m_Descriptor.graphicsFormat,
|
|
GraphicsFormat.None);
|
|
SMAATarget = UniversalRenderer.CreateRenderGraphTexture(renderGraph, desc, "_SMAATarget", true, FilterMode.Bilinear);
|
|
|
|
var edgeTextureDesc = PostProcessPass.GetCompatibleDescriptor(m_Descriptor,
|
|
m_Descriptor.width,
|
|
m_Descriptor.height,
|
|
m_SMAAEdgeFormat,
|
|
GraphicsFormat.None);
|
|
var edgeTexture = UniversalRenderer.CreateRenderGraphTexture(renderGraph, edgeTextureDesc, "_EdgeStencilTexture", true, FilterMode.Bilinear);
|
|
|
|
var edgeTextureStencilDesc = PostProcessPass.GetCompatibleDescriptor(m_Descriptor,
|
|
m_Descriptor.width,
|
|
m_Descriptor.height,
|
|
GraphicsFormat.None,
|
|
GraphicsFormatUtility.GetDepthStencilFormat(24));
|
|
var edgeTextureStencil = UniversalRenderer.CreateRenderGraphTexture(renderGraph, edgeTextureStencilDesc, "_EdgeTexture", true, FilterMode.Bilinear);
|
|
|
|
var blendTextureDesc = PostProcessPass.GetCompatibleDescriptor(m_Descriptor,
|
|
m_Descriptor.width,
|
|
m_Descriptor.height,
|
|
GraphicsFormat.R8G8B8A8_UNorm,
|
|
GraphicsFormat.None);
|
|
var blendTexture = UniversalRenderer.CreateRenderGraphTexture(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 / m_Descriptor.width, 1f / m_Descriptor.height, m_Descriptor.width, m_Descriptor.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)))
|
|
{
|
|
passData.destinationTexture = edgeTexture;
|
|
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)))
|
|
{
|
|
passData.destinationTexture = blendTexture;
|
|
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);
|
|
passData.destinationTexture = SMAATarget;
|
|
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, in TextureHandle bloomTexture, Material uberMaterial)
|
|
{
|
|
using (var builder = rendergraph.AddRasterRenderPass<UberSetupBloomPassData>("Setup Bloom Post Processing", out var passData, 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 = m_Descriptor.width / (float)m_Descriptor.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;
|
|
}
|
|
|
|
passData.bloomParams = bloomParams;
|
|
passData.dirtScaleOffset = dirtScaleOffset;
|
|
passData.dirtIntensity = dirtIntensity;
|
|
passData.dirtTexture = dirtTexture;
|
|
passData.highQualityFilteringValue = m_Bloom.highQualityFiltering.value;
|
|
|
|
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 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)
|
|
{
|
|
// 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, 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, 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(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>("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;
|
|
|
|
var desc = PostProcessPass.GetCompatibleDescriptor(m_Descriptor,
|
|
m_Descriptor.width,
|
|
m_Descriptor.height,
|
|
m_Descriptor.graphicsFormat,
|
|
GraphicsFormat.None);
|
|
destination = UniversalRenderer.CreateRenderGraphTexture(renderGraph, desc, "_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 material = dofMaterial;
|
|
int downSample = 2;
|
|
int wh = m_Descriptor.width / downSample;
|
|
int hh = m_Descriptor.height / downSample;
|
|
|
|
// Pass Textures
|
|
var fullCoCTextureDesc = PostProcessPass.GetCompatibleDescriptor(m_Descriptor, m_Descriptor.width, m_Descriptor.height, m_GaussianCoCFormat);
|
|
var fullCoCTexture = UniversalRenderer.CreateRenderGraphTexture(renderGraph, fullCoCTextureDesc, "_FullCoCTexture", true, FilterMode.Bilinear);
|
|
var halfCoCTextureDesc = PostProcessPass.GetCompatibleDescriptor(m_Descriptor, wh, hh, m_GaussianCoCFormat);
|
|
var halfCoCTexture = UniversalRenderer.CreateRenderGraphTexture(renderGraph, halfCoCTextureDesc, "_HalfCoCTexture", true, FilterMode.Bilinear);
|
|
var pingTextureDesc = PostProcessPass.GetCompatibleDescriptor(m_Descriptor, wh, hh, m_DefaultColorFormat);
|
|
var pingTexture = UniversalRenderer.CreateRenderGraphTexture(renderGraph, pingTextureDesc, "_PingTexture", true, FilterMode.Bilinear);
|
|
var pongTextureDesc = PostProcessPass.GetCompatibleDescriptor(m_Descriptor, wh, hh, m_DefaultColorFormat);
|
|
var pongTexture = UniversalRenderer.CreateRenderGraphTexture(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)
|
|
{
|
|
int downSample = 2;
|
|
var material = dofMaterial;
|
|
int wh = m_Descriptor.width / downSample;
|
|
int hh = m_Descriptor.height / downSample;
|
|
|
|
// Pass Textures
|
|
var fullCoCTextureDesc = PostProcessPass.GetCompatibleDescriptor(m_Descriptor, m_Descriptor.width, m_Descriptor.height, GraphicsFormat.R8_UNorm);
|
|
var fullCoCTexture = UniversalRenderer.CreateRenderGraphTexture(renderGraph, fullCoCTextureDesc, "_FullCoCTexture", true, FilterMode.Bilinear);
|
|
var pingTextureDesc = PostProcessPass.GetCompatibleDescriptor(m_Descriptor, wh, hh, GraphicsFormat.R16G16B16A16_SFloat);
|
|
var pingTexture = UniversalRenderer.CreateRenderGraphTexture(renderGraph, pingTextureDesc, "_PingTexture", true, FilterMode.Bilinear);
|
|
var pongTextureDesc = PostProcessPass.GetCompatibleDescriptor(m_Descriptor, wh, hh, GraphicsFormat.R16G16B16A16_SFloat);
|
|
var pongTexture = UniversalRenderer.CreateRenderGraphTexture(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(m_Descriptor.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 / m_Descriptor.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 RenderTextureDescriptor sourceTextureDesc;
|
|
internal Material material;
|
|
internal Vector4 paniniParams;
|
|
internal bool isPaniniGeneric;
|
|
}
|
|
|
|
public void RenderPaniniProjection(RenderGraph renderGraph, Camera camera, in TextureHandle source, out TextureHandle destination)
|
|
{
|
|
var desc = PostProcessPass.GetCompatibleDescriptor(m_Descriptor,
|
|
m_Descriptor.width,
|
|
m_Descriptor.height,
|
|
m_Descriptor.graphicsFormat,
|
|
GraphicsFormat.None);
|
|
|
|
destination = UniversalRenderer.CreateRenderGraphTexture(renderGraph, desc, "_PaniniProjectionTarget", true, FilterMode.Bilinear);
|
|
|
|
float distance = m_PaniniProjection.distance.value;
|
|
var viewExtents = CalcViewExtents(camera);
|
|
var cropExtents = CalcCropExtents(camera, distance);
|
|
|
|
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;
|
|
passData.sourceTextureDesc = m_Descriptor;
|
|
|
|
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)
|
|
{
|
|
var desc = PostProcessPass.GetCompatibleDescriptor(m_Descriptor,
|
|
m_Descriptor.width,
|
|
m_Descriptor.height,
|
|
m_Descriptor.graphicsFormat,
|
|
GraphicsFormat.None);
|
|
destination = UniversalRenderer.CreateRenderGraphTexture(renderGraph, desc, _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 = "_UpscaledColorTarget";
|
|
|
|
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 desc = GetCompatibleDescriptor(cameraData.cameraTargetDescriptor,
|
|
cameraData.pixelWidth,
|
|
cameraData.pixelHeight,
|
|
cameraData.cameraTargetDescriptor.graphicsFormat);
|
|
|
|
// STP uses compute shaders so all render textures must enable random writes
|
|
desc.enableRandomWrite = true;
|
|
|
|
// Avoid enabling sRGB because STP works with compute shaders which can't output sRGB automatically.
|
|
desc.sRGB = false;
|
|
|
|
destination = UniversalRenderer.CreateRenderGraphTexture(renderGraph, desc, _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(desc.width, desc.height));
|
|
}
|
|
#endregion
|
|
|
|
#region MotionBlur
|
|
private class MotionBlurPassData
|
|
{
|
|
internal TextureHandle destinationTexture;
|
|
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;
|
|
var desc = PostProcessPass.GetCompatibleDescriptor(m_Descriptor,
|
|
m_Descriptor.width,
|
|
m_Descriptor.height,
|
|
m_Descriptor.graphicsFormat,
|
|
GraphicsFormat.None);
|
|
|
|
destination = UniversalRenderer.CreateRenderGraphTexture(renderGraph, desc, "_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);
|
|
passData.destinationTexture = destination;
|
|
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 RenderTextureDescriptor sourceDescriptor;
|
|
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)
|
|
{
|
|
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)m_Descriptor.width;
|
|
passData.height = (float)m_Descriptor.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)
|
|
{
|
|
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.sourceDescriptor = m_Descriptor;
|
|
passData.cameraData = cameraData;
|
|
passData.material = m_Materials.lensFlareDataDriven;
|
|
passData.width = (float)m_Descriptor.width;
|
|
passData.height = (float)m_Descriptor.height;
|
|
passData.viewport.x = 0.0f;
|
|
passData.viewport.y = 0.0f;
|
|
passData.viewport.width = (float)m_Descriptor.width;
|
|
passData.viewport.height = (float)m_Descriptor.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 destinationTexture;
|
|
internal TextureHandle streakTmpTexture;
|
|
internal TextureHandle streakTmpTexture2;
|
|
internal TextureHandle originalBloomTexture;
|
|
internal TextureHandle screenSpaceLensFlareBloomMipTexture;
|
|
internal TextureHandle result;
|
|
internal RenderTextureDescriptor sourceDescriptor;
|
|
internal Camera camera;
|
|
internal Material material;
|
|
internal ScreenSpaceLensFlare lensFlareScreenSpace;
|
|
internal int downsample;
|
|
}
|
|
|
|
public TextureHandle RenderLensFlareScreenSpace(RenderGraph renderGraph, Camera camera, in TextureHandle destination, TextureHandle originalBloomTexture, TextureHandle screenSpaceLensFlareBloomMipTexture, bool enableXR)
|
|
{
|
|
var downsample = (int) m_LensFlareScreenSpace.resolution.value;
|
|
|
|
int width = m_Descriptor.width / downsample;
|
|
int height = m_Descriptor.height / downsample;
|
|
|
|
var streakTextureDesc = GetCompatibleDescriptor(m_Descriptor, width, height, m_DefaultColorFormat);
|
|
var streakTmpTexture = UniversalRenderer.CreateRenderGraphTexture(renderGraph, streakTextureDesc, "_StreakTmpTexture", true, FilterMode.Bilinear);
|
|
var streakTmpTexture2 = UniversalRenderer.CreateRenderGraphTexture(renderGraph, streakTextureDesc, "_StreakTmpTexture2", true, FilterMode.Bilinear);
|
|
var resultTexture = UniversalRenderer.CreateRenderGraphTexture(renderGraph, streakTextureDesc, "Lens Flare Screen Space Result", true, FilterMode.Bilinear);
|
|
|
|
using (var builder = renderGraph.AddUnsafePass<LensFlareScreenSpacePassData>("Lens Flare Screen Space Pass", 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.destinationTexture = destination;
|
|
builder.UseTexture(destination, AccessFlags.Write);
|
|
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;
|
|
builder.UseTexture(originalBloomTexture, AccessFlags.ReadWrite);
|
|
passData.sourceDescriptor = m_Descriptor;
|
|
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.Write);
|
|
|
|
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.sourceDescriptor.width,
|
|
(float)data.sourceDescriptor.height,
|
|
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 passData.originalBloomTexture;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
static private void ScaleViewportAndBlit(RasterCommandBuffer cmd, RTHandle sourceTextureHdl, RTHandle dest, UniversalCameraData cameraData, Material material)
|
|
{
|
|
Vector4 scaleBias = RenderingUtils.GetFinalBlitScaleBias(sourceTextureHdl, dest, cameraData);
|
|
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)
|
|
cmd.SetViewport(cameraData.pixelRect);
|
|
|
|
Blitter.BlitTexture(cmd, sourceTextureHdl, scaleBias, material, 0);
|
|
}
|
|
|
|
#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;
|
|
|
|
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);
|
|
|
|
ScaleViewportAndBlit(context.cmd, sourceTextureHdl, data.destinationTexture, data.cameraData, data.material);
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
|
|
private class PostProcessingFinalFSRScalePassData
|
|
{
|
|
internal TextureHandle destinationTexture;
|
|
internal TextureHandle sourceTexture;
|
|
internal Material material;
|
|
internal bool enableAlphaOutput;
|
|
}
|
|
|
|
public void RenderFinalFSRScale(RenderGraph renderGraph, in TextureHandle source, in TextureHandle destination, 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);
|
|
passData.destinationTexture = destination;
|
|
builder.SetRenderAttachment(destination, 0, AccessFlags.Write);
|
|
passData.sourceTexture = source;
|
|
builder.UseTexture(source, AccessFlags.Read);
|
|
passData.material = m_Materials.easu;
|
|
passData.enableAlphaOutput = enableAlphaOutput;
|
|
|
|
builder.SetRenderFunc(static (PostProcessingFinalFSRScalePassData data, RasterGraphContext context) =>
|
|
{
|
|
var cmd = context.cmd;
|
|
var sourceTex = data.sourceTexture;
|
|
var destTex = data.destinationTexture;
|
|
var material = data.material;
|
|
var enableAlphaOutput = data.enableAlphaOutput;
|
|
RTHandle sourceHdl = (RTHandle)sourceTex;
|
|
RTHandle destHdl = (RTHandle)destTex;
|
|
|
|
var fsrInputSize = new Vector2(sourceHdl.referenceSize.x, sourceHdl.referenceSize.y);
|
|
var fsrOutputSize = new Vector2(destHdl.referenceSize.x, destHdl.referenceSize.y);
|
|
FSRUtils.SetEasuConstants(cmd, fsrInputSize, fsrInputSize, 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)
|
|
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);
|
|
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>();
|
|
m_Tonemapping = stack.GetComponent<Tonemapping>();
|
|
|
|
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
|
|
|
|
var material = m_Materials.finalPass;
|
|
|
|
material.shaderKeywords = null;
|
|
|
|
FinalBlitSettings settings = FinalBlitSettings.Create();
|
|
|
|
// 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,
|
|
cameraData.pixelWidth, cameraData.pixelHeight,
|
|
material
|
|
);
|
|
}
|
|
|
|
if (cameraData.isDitheringEnabled)
|
|
{
|
|
material.EnableKeyword(ShaderKeywordStrings.Dithering);
|
|
m_DitheringTextureIndex = PostProcessUtils.ConfigureDithering(
|
|
m_Data,
|
|
m_DitheringTextureIndex,
|
|
cameraData.pixelWidth, cameraData.pixelHeight,
|
|
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.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 = cameraData.cameraTargetDescriptor;
|
|
tempRtDesc.msaaSamples = 1;
|
|
tempRtDesc.depthStencilFormat = GraphicsFormat.None;
|
|
|
|
// 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.graphicsFormat = UniversalRenderPipeline.MakeUnormRenderTextureGraphicsFormat();
|
|
|
|
var scalingSetupTarget = UniversalRenderer.CreateRenderGraphTexture(renderGraph, tempRtDesc, "scalingSetupTarget", true, FilterMode.Point);
|
|
|
|
var upscaleRtDesc = cameraData.cameraTargetDescriptor;
|
|
upscaleRtDesc.msaaSamples = 1;
|
|
upscaleRtDesc.depthStencilFormat = GraphicsFormat.None;
|
|
upscaleRtDesc.width = cameraData.pixelWidth;
|
|
upscaleRtDesc.height = cameraData.pixelHeight;
|
|
|
|
var upScaleTarget = UniversalRenderer.CreateRenderGraphTexture(renderGraph, upscaleRtDesc, "_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 upScaleTarget, 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 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;
|
|
}
|
|
|
|
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 overlayUITexture, bool requireHDROutput, bool enableAlphaOutput, bool resolveToDebugScreen)
|
|
{
|
|
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;
|
|
if (userLutTexture.IsValid())
|
|
{
|
|
passData.userLutTexture = userLutTexture;
|
|
builder.UseTexture(userLutTexture, AccessFlags.Read);
|
|
}
|
|
|
|
if (m_Bloom.IsActive())
|
|
builder.UseTexture(_BloomMipUp[0], AccessFlags.Read);
|
|
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;
|
|
|
|
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.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
|
|
ScaleViewportAndBlit(cmd, sourceTextureHdl, data.destinationTexture, data.cameraData, material);
|
|
});
|
|
|
|
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_Descriptor = cameraData.cameraTargetDescriptor;
|
|
m_Descriptor.useMipMap = false;
|
|
m_Descriptor.autoGenerateMips = false;
|
|
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();
|
|
if (cameraData.antialiasing == AntialiasingMode.TemporalAntiAliasing && !useTemporalAA)
|
|
TemporalAA.ValidateAndWarn(cameraData);
|
|
|
|
// STP is only supported when TAA is enabled and all of its runtime requirements are met.
|
|
// See the comments for IsSTPEnabled() for more information.
|
|
bool useSTP = useTemporalAA && cameraData.IsSTPEnabled();
|
|
|
|
using (var builder = renderGraph.AddRasterRenderPass<PostFXSetupPassData>("Setup PostFX passes", out var passData,
|
|
ProfilingSampler.Get(URPProfileId.RG_SetupPostFX)))
|
|
{
|
|
// TODO RENDERGRAPH: properly setup dependencies between passes
|
|
builder.AllowPassCulling(false);
|
|
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, cameraData.cameraTargetDescriptor, 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;
|
|
|
|
// Bloom goes first
|
|
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 var BloomTexture, cameraData.isAlphaOutputEnabled);
|
|
|
|
if (useLensFlareScreenSpace)
|
|
{
|
|
int maxBloomMip = Mathf.Clamp(m_LensFlareScreenSpace.bloomMip.value, 0, m_Bloom.maxIterations.value/2);
|
|
BloomTexture = RenderLensFlareScreenSpace(renderGraph, cameraData.camera, in currentSource, _BloomMipUp[0], _BloomMipUp[maxBloomMip], cameraData.xr.enabled);
|
|
}
|
|
|
|
UberPostSetupBloomPass(renderGraph, in BloomTexture, m_Materials.uber);
|
|
}
|
|
|
|
if (useLensFlare)
|
|
{
|
|
LensFlareDataDrivenComputeOcclusion(renderGraph, resourceData, cameraData);
|
|
RenderLensFlareDataDriven(renderGraph, resourceData, cameraData, in currentSource);
|
|
}
|
|
|
|
// 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);
|
|
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 overlayUITexture, requireHDROutput, enableAlphaOutput, resolveToDebugScreen);
|
|
}
|
|
}
|
|
}
|
|
}
|