Adaug proiectul Unity

This commit is contained in:
2026-11-01 23:39:55 +02:00
parent cb14d9d37d
commit 863eac8f2c
23463 changed files with 5412601 additions and 0 deletions
@@ -0,0 +1,372 @@
using System;
using System.Collections.Generic;
using Unity.Collections;
namespace UnityEngine.Rendering.Universal
{
internal struct AdditionalLightsShadowAtlasLayout
{
internal struct ShadowResolutionRequest
{
public ushort visibleLightIndex;
public ushort perLightShadowSliceIndex;
public ushort requestedResolution;
public ushort offsetX; // x coordinate of the square area allocated in the atlas for this shadow map
public ushort offsetY; // y coordinate of the square area allocated in the atlas for this shadow map
public ushort allocatedResolution; // width of the square area allocated in the atlas for this shadow map
[Flags]
private enum SettingsOptions : ushort
{
None = 0,
SoftShadow = (1 << 0),
PointLightShadow = (1 << 1),
All = 0xFFFF
}
private SettingsOptions m_ShadowProperties;
public bool softShadow
{
get => m_ShadowProperties.HasFlag(SettingsOptions.SoftShadow); // otherwise it's hard-shadow (no filtering)
set
{
if (value)
m_ShadowProperties |= SettingsOptions.SoftShadow;
else
m_ShadowProperties &= ~SettingsOptions.SoftShadow;
}
}
public bool pointLightShadow
{
get => m_ShadowProperties.HasFlag(SettingsOptions.PointLightShadow); // otherwise it's spot light shadow (1 shadow slice instead of 6)
set
{
if (value)
m_ShadowProperties |= SettingsOptions.PointLightShadow;
else
m_ShadowProperties &= ~SettingsOptions.PointLightShadow;
}
}
}
// Static fields used to avoid GC allocs of intermediate computations
static List<RectInt> s_UnusedAtlasSquareAreas; // This list tracks space available in the atlas
static List<ShadowResolutionRequest> s_ShadowResolutionRequests; // intermediate array used to compute the final resolution of each shadow slice rendered in the frame
static float[] s_VisibleLightIndexToCameraSquareDistance; // stores for each shadowed additional light its (squared) distance to camera ; used to sub-sort shadow requests according to how close their casting light is
static Func<ShadowResolutionRequest, ShadowResolutionRequest, int> s_CompareShadowResolutionRequest;
static ShadowResolutionRequest[] s_SortedShadowResolutionRequests;
#if UNITY_EDITOR
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
static void ResetStaticsOnLoad()
{
s_UnusedAtlasSquareAreas = new List<RectInt>();
s_ShadowResolutionRequests = new List<ShadowResolutionRequest>();
s_VisibleLightIndexToCameraSquareDistance = null; // sized to numberOfVisibleLights at runtime
s_CompareShadowResolutionRequest = CreateCompareShadowResolutionRequesPredicate();
s_SortedShadowResolutionRequests = null; // sized to totalShadowResolutionRequestsCount at runtime
}
#endif
NativeArray<ShadowResolutionRequest> m_SortedShadowResolutionRequests;
NativeArray<int> m_VisibleLightIndexToSortedShadowResolutionRequestsFirstSliceIndex; // for each visible light, store the index of its first shadow slice in m_SortedShadowResolutionRequests (for quicker access)
int m_TotalShadowSlicesCount;
int m_TotalShadowResolutionRequestCount;
bool m_TooManyShadowMaps;
int m_ShadowSlicesScaleFactor;
int m_AtlasSize;
public AdditionalLightsShadowAtlasLayout(UniversalLightData lightData, UniversalShadowData shadowData, UniversalCameraData cameraData)
{
bool useStructuredBuffer = RenderingUtils.useStructuredBuffer;
NativeArray<VisibleLight> visibleLights = lightData.visibleLights;
int numberOfVisibleLights = visibleLights.Length;
if (s_UnusedAtlasSquareAreas == null)
s_UnusedAtlasSquareAreas = new List<RectInt>();
if (s_ShadowResolutionRequests == null)
s_ShadowResolutionRequests = new List<ShadowResolutionRequest>();
if (s_VisibleLightIndexToCameraSquareDistance == null || s_VisibleLightIndexToCameraSquareDistance.Length < numberOfVisibleLights)
s_VisibleLightIndexToCameraSquareDistance = new float[numberOfVisibleLights];
if (s_CompareShadowResolutionRequest == null)
s_CompareShadowResolutionRequest = CreateCompareShadowResolutionRequesPredicate();
if (!useStructuredBuffer)
{
int newCapacity = UniversalRenderPipeline.maxVisibleAdditionalLights;
if (s_UnusedAtlasSquareAreas.Capacity < newCapacity)
s_UnusedAtlasSquareAreas.Capacity = newCapacity;
if (s_ShadowResolutionRequests.Count < numberOfVisibleLights)
{
s_ShadowResolutionRequests.Capacity = numberOfVisibleLights;
int diff = numberOfVisibleLights - s_ShadowResolutionRequests.Count + 1;
for (int i = 0; i < diff; i++)
s_ShadowResolutionRequests.Add(new ShadowResolutionRequest());
}
}
s_UnusedAtlasSquareAreas.Clear();
ushort totalShadowResolutionRequestsCount = 0; // Number of shadow slices that we would need for all shadowed additional (punctual) lights in the scene. We might have to ignore some of those requests if they do not fit in the shadow atlas.
for (int visibleLightIndex = 0; visibleLightIndex < visibleLights.Length; ++visibleLightIndex)
{
// Skip main directional light as it is not packed into the shadow atlas
if (visibleLightIndex == lightData.mainLightIndex)
{
s_VisibleLightIndexToCameraSquareDistance[visibleLightIndex] = float.MaxValue;
continue;
}
ref VisibleLight vl = ref visibleLights.UnsafeElementAt(visibleLightIndex);
Light light = vl.light;
LightType lightType = vl.lightType;
LightShadows lightShadows = light.shadows;
float shadowStrength = light.shadowStrength;
if (!ShadowUtils.IsValidShadowCastingLight(lightData, visibleLightIndex, lightType, lightShadows, shadowStrength))
{
s_VisibleLightIndexToCameraSquareDistance[visibleLightIndex] = float.MaxValue;
continue;
}
bool softShadows = (lightShadows == LightShadows.Soft);
bool pointLightShadow = (lightType == LightType.Point);
ushort visibleLightIndexUshort = (ushort)visibleLightIndex;
ushort requestedResolution = (ushort)shadowData.resolution[visibleLightIndex];
int shadowSlicesCountForThisLight = ShadowUtils.GetPunctualLightShadowSlicesCount(lightType);
for (ushort perLightShadowSliceIndex = 0; perLightShadowSliceIndex < shadowSlicesCountForThisLight; ++perLightShadowSliceIndex)
{
if (totalShadowResolutionRequestsCount >= s_ShadowResolutionRequests.Count)
s_ShadowResolutionRequests.Add(new ShadowResolutionRequest());
ShadowResolutionRequest request = s_ShadowResolutionRequests[totalShadowResolutionRequestsCount];
request.visibleLightIndex = visibleLightIndexUshort;
request.perLightShadowSliceIndex = perLightShadowSliceIndex;
request.requestedResolution = requestedResolution;
request.softShadow = softShadows;
request.pointLightShadow = pointLightShadow;
s_ShadowResolutionRequests[totalShadowResolutionRequestsCount] = request;
totalShadowResolutionRequestsCount++;
}
// mark this light as casting shadows
s_VisibleLightIndexToCameraSquareDistance[visibleLightIndex] = (cameraData.worldSpaceCameraPos - light.transform.position).sqrMagnitude;
}
if (s_SortedShadowResolutionRequests == null || s_SortedShadowResolutionRequests.Length < totalShadowResolutionRequestsCount)
s_SortedShadowResolutionRequests = new ShadowResolutionRequest[totalShadowResolutionRequestsCount];
for (int i = 0; i < totalShadowResolutionRequestsCount; ++i)
s_SortedShadowResolutionRequests[i] = s_ShadowResolutionRequests[i];
using (new ProfilingScope(Sorting.s_QuickSortSampler))
{
Sorting.QuickSort(s_SortedShadowResolutionRequests, 0, totalShadowResolutionRequestsCount - 1, s_CompareShadowResolutionRequest);
}
m_SortedShadowResolutionRequests = new NativeArray<ShadowResolutionRequest>(s_SortedShadowResolutionRequests, Allocator.Temp);
// To avoid visual artifacts when there is not enough place in the atlas, we remove shadow slices that would be allocated a too small resolution.
// When not using structured buffers, m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix.Length maps to _AdditionalLightsWorldToShadow in Shadows.hlsl
// In that case we have to limit its size because uniform buffers cannot be higher than 64kb for some platforms.
int totalShadowSlicesCount = useStructuredBuffer ? totalShadowResolutionRequestsCount : Math.Min(totalShadowResolutionRequestsCount, UniversalRenderPipeline.maxVisibleAdditionalLights); // Number of shadow slices that we will actually be able to fit in the shadow atlas without causing visual artifacts.
int atlasSize = shadowData.additionalLightsShadowmapWidth;
// Find biggest end index in m_SortedShadowResolutionRequests array, under which all shadow requests can be allocated a big enough shadow atlas slot, to not cause rendering artifacts
bool allShadowsAfterStartIndexHaveEnoughResolution = false;
int estimatedScaleFactor = 1;
while (!allShadowsAfterStartIndexHaveEnoughResolution && totalShadowSlicesCount > 0)
{
ShadowResolutionRequest request = m_SortedShadowResolutionRequests[totalShadowSlicesCount - 1];
estimatedScaleFactor = EstimateScaleFactorNeededToFitAllShadowsInAtlas(m_SortedShadowResolutionRequests, totalShadowSlicesCount, atlasSize);
// check if resolution of the least priority shadow slice request would be acceptable
if (request.requestedResolution >= estimatedScaleFactor * ShadowUtils.MinimalPunctualLightShadowResolution(request.softShadow))
allShadowsAfterStartIndexHaveEnoughResolution = true;
else // Skip shadow requests for this light ; their resolution is too small to look any good
totalShadowSlicesCount -= ShadowUtils.GetPunctualLightShadowSlicesCount(request.pointLightShadow ? LightType.Point : LightType.Spot);
}
for (int sortedArrayIndex = totalShadowSlicesCount; sortedArrayIndex < m_SortedShadowResolutionRequests.Length; ++sortedArrayIndex)
m_SortedShadowResolutionRequests[sortedArrayIndex] = default; // Reset entries that we cannot fit in the atlas
m_VisibleLightIndexToSortedShadowResolutionRequestsFirstSliceIndex = new NativeArray<int>(visibleLights.Length, Allocator.Temp);
// Reset the reverse lookup array
for (int visibleLightIndex = 0; visibleLightIndex < m_VisibleLightIndexToSortedShadowResolutionRequestsFirstSliceIndex.Length; ++visibleLightIndex)
m_VisibleLightIndexToSortedShadowResolutionRequestsFirstSliceIndex[visibleLightIndex] = -1;
// Update the reverse lookup array (starting from the end of the array, in order to use index of slice#0 in case a same visibleLight has several shadowSlices)
for (int sortedArrayIndex = totalShadowSlicesCount - 1; sortedArrayIndex >= 0; --sortedArrayIndex)
{
int visibleLightIndex = s_SortedShadowResolutionRequests[sortedArrayIndex].visibleLightIndex;
m_VisibleLightIndexToSortedShadowResolutionRequestsFirstSliceIndex[visibleLightIndex] = sortedArrayIndex;
}
// Assigns to each of the first totalShadowSlicesCount items in m_SortedShadowResolutionRequests a location in the shadow atlas based on requested resolutions.
// If necessary, scales down shadow maps active in the frame, to make all of them fit in the atlas.
bool allShadowSlicesFitInAtlas = false;
bool tooManyShadows = false;
int shadowSlicesScaleFactor = estimatedScaleFactor;
while (!allShadowSlicesFitInAtlas && !tooManyShadows)
{
s_UnusedAtlasSquareAreas.Clear();
s_UnusedAtlasSquareAreas.Add(new RectInt(0, 0, atlasSize, atlasSize));
allShadowSlicesFitInAtlas = true;
for (int shadowRequestIndex = 0; shadowRequestIndex < totalShadowSlicesCount; ++shadowRequestIndex)
{
var resolution = m_SortedShadowResolutionRequests[shadowRequestIndex].requestedResolution / shadowSlicesScaleFactor;
if (resolution < ShadowUtils.MinimalPunctualLightShadowResolution(m_SortedShadowResolutionRequests[shadowRequestIndex].softShadow))
{
tooManyShadows = true;
break;
}
bool foundSpaceInAtlas = false;
// Try to find free space in the atlas
for (int unusedAtlasSquareAreaIndex = 0; unusedAtlasSquareAreaIndex < s_UnusedAtlasSquareAreas.Count; ++unusedAtlasSquareAreaIndex)
{
RectInt atlasArea = s_UnusedAtlasSquareAreas[unusedAtlasSquareAreaIndex];
int atlasAreaWidth = atlasArea.width;
if (atlasAreaWidth < resolution)
continue;
int atlasAreaHeight = atlasArea.height;
int atlasAreaX = atlasArea.x;
int atlasAreaY = atlasArea.y;
// we can use this atlas area for the shadow request
ref ShadowResolutionRequest shadowRequest = ref m_SortedShadowResolutionRequests.UnsafeElementAtMutable(shadowRequestIndex);
shadowRequest.offsetX = (ushort)atlasAreaX;
shadowRequest.offsetY = (ushort)atlasAreaY;
shadowRequest.allocatedResolution = (ushort) resolution;
// this atlas space is not available anymore, so remove it from the list
s_UnusedAtlasSquareAreas.RemoveAt(unusedAtlasSquareAreaIndex);
// make sure to split space so that the rest of this square area can be used
int remainingShadowRequestsCount = totalShadowSlicesCount - shadowRequestIndex - 1; // (no need to add more than that)
int newSquareAreasCount = 0;
int newSquareAreaWidth = resolution; // we split the area in squares of same size
int newSquareAreaHeight = resolution;
int newSquareAreaX = atlasAreaX;
int newSquareAreaY = atlasAreaY;
while (newSquareAreasCount < remainingShadowRequestsCount)
{
newSquareAreaX += newSquareAreaWidth;
if (newSquareAreaX + newSquareAreaWidth > (atlasAreaX + atlasAreaWidth))
{
newSquareAreaX = atlasAreaX;
newSquareAreaY += newSquareAreaHeight;
if (newSquareAreaY + newSquareAreaHeight > (atlasAreaY + atlasAreaHeight))
break;
}
// replace the space we removed previously by new smaller squares (inserting them in this order ensures shadow maps will be packed at the side of the atlas, without gaps)
s_UnusedAtlasSquareAreas.Insert(unusedAtlasSquareAreaIndex + newSquareAreasCount, new RectInt(newSquareAreaX, newSquareAreaY, newSquareAreaWidth, newSquareAreaHeight));
++newSquareAreasCount;
}
foundSpaceInAtlas = true;
break;
}
if (!foundSpaceInAtlas)
{
allShadowSlicesFitInAtlas = false;
break;
}
}
if (!allShadowSlicesFitInAtlas && !tooManyShadows)
shadowSlicesScaleFactor *= 2;
}
m_TooManyShadowMaps = tooManyShadows;
m_ShadowSlicesScaleFactor = shadowSlicesScaleFactor;
m_TotalShadowSlicesCount = totalShadowSlicesCount;
m_TotalShadowResolutionRequestCount = totalShadowResolutionRequestsCount;
m_AtlasSize = atlasSize;
}
public int GetTotalShadowSlicesCount() => m_TotalShadowSlicesCount;
public int GetTotalShadowResolutionRequestCount() => m_TotalShadowResolutionRequestCount;
public bool HasTooManyShadowMaps() => m_TooManyShadowMaps;
public int GetShadowSlicesScaleFactor() => m_ShadowSlicesScaleFactor;
public int GetAtlasSize() => m_AtlasSize;
public bool HasSpaceForLight(int originalVisibleLightIndex)
{
return m_VisibleLightIndexToSortedShadowResolutionRequestsFirstSliceIndex[originalVisibleLightIndex] != -1;
}
public ShadowResolutionRequest GetSortedShadowResolutionRequest(int sortedShadowResolutionRequestIndex)
{
return m_SortedShadowResolutionRequests[sortedShadowResolutionRequestIndex];
}
public ShadowResolutionRequest GetSliceShadowResolutionRequest(int originalVisibleLightIndex, int sliceIndex)
{
int sortedShadowResolutionRequestIndex = m_VisibleLightIndexToSortedShadowResolutionRequestsFirstSliceIndex[originalVisibleLightIndex];
return m_SortedShadowResolutionRequests[sortedShadowResolutionRequestIndex + sliceIndex];
}
public static void ClearStaticCaches()
{
s_UnusedAtlasSquareAreas = null;
s_ShadowResolutionRequests = null;
s_VisibleLightIndexToCameraSquareDistance = null;
s_CompareShadowResolutionRequest = null;
s_SortedShadowResolutionRequests = null;
}
static int EstimateScaleFactorNeededToFitAllShadowsInAtlas(in NativeArray<ShadowResolutionRequest> shadowResolutionRequests, int endIndex, int atlasSize)
{
long totalTexelsInShadowAtlas = atlasSize * atlasSize;
long totalTexelsInShadowRequests = 0;
for (int shadowRequestIndex = 0; shadowRequestIndex < endIndex; ++shadowRequestIndex)
totalTexelsInShadowRequests += shadowResolutionRequests[shadowRequestIndex].requestedResolution * shadowResolutionRequests[shadowRequestIndex].requestedResolution;
int estimatedScaleFactor = 1;
while (totalTexelsInShadowRequests > totalTexelsInShadowAtlas * estimatedScaleFactor * estimatedScaleFactor)
estimatedScaleFactor *= 2;
return estimatedScaleFactor;
}
// Sort array in decreasing requestedResolution order,
// sub-sorting in "HardShadow > SoftShadow",
// i.e place last requests that will be removed in priority to make room for the others,
// because their resolution is too small to produce good-looking shadows ; or because they take relatively more space in the atlas )
// sub-sub-sorting in light distance to camera
// then grouping in increasing visibleIndex (and sub-sorting each group in ShadowSliceIndex order)
static Func<ShadowResolutionRequest, ShadowResolutionRequest, int> CreateCompareShadowResolutionRequesPredicate()
{
return (ShadowResolutionRequest curr, ShadowResolutionRequest other) =>
{
return (((curr.requestedResolution > other.requestedResolution)
|| (curr.requestedResolution == other.requestedResolution && !curr.softShadow && other.softShadow)
|| (curr.requestedResolution == other.requestedResolution && curr.softShadow == other.softShadow && s_VisibleLightIndexToCameraSquareDistance[curr.visibleLightIndex] < s_VisibleLightIndexToCameraSquareDistance[other.visibleLightIndex])
|| (curr.requestedResolution == other.requestedResolution && curr.softShadow == other.softShadow && s_VisibleLightIndexToCameraSquareDistance[curr.visibleLightIndex] == s_VisibleLightIndexToCameraSquareDistance[other.visibleLightIndex] && curr.visibleLightIndex < other.visibleLightIndex)
|| (curr.requestedResolution == other.requestedResolution && curr.softShadow == other.softShadow && s_VisibleLightIndexToCameraSquareDistance[curr.visibleLightIndex] == s_VisibleLightIndexToCameraSquareDistance[other.visibleLightIndex] && curr.visibleLightIndex == other.visibleLightIndex && curr.perLightShadowSliceIndex < other.perLightShadowSliceIndex)))
? -1 : 1;
};
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6fa1a543f4721444d94e0b3200fa22a8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f46c1a0c3e3a98848a0efbf4e1fd5675
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using UnityEngine.Rendering.RenderGraphModule;
namespace UnityEngine.Rendering.Universal
{
/// <summary>
/// Let customizable actions inject commands to capture the camera output.
///
/// You can use this pass to inject capture commands into a command buffer
/// with the goal of having camera capture happening in external code.
/// </summary>
internal class CapturePass : ScriptableRenderPass
{
public CapturePass(RenderPassEvent evt)
{
profilingSampler = new ProfilingSampler("Capture Camera output");
renderPassEvent = evt;
}
private class UnsafePassData
{
internal TextureHandle source;
public IEnumerator<Action<RenderTargetIdentifier, CommandBuffer>> captureActions;
}
// This function needs to add an unsafe render pass to Render Graph because a raster render pass, which is typically
// used for rendering with Render Graph, cannot perform the texture readback operations performed with the command
// buffer in CameraTextureProvider. Unsafe passes can do certain operations that raster render passes cannot do and
// have access to the full command buffer API.
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
using (var builder = renderGraph.AddUnsafePass<UnsafePassData>(passName, out var passData, profilingSampler))
{
// Setup up the pass data with activeColorTexture, which has the correct orientation and position in a built player
// In most cases, it will be resolved to cameraColor as the source since we cannot sample the backbuffer directly.
// However, activeColorTexture allows us to support offscreen rendering scenarios where URP renders
// to a fake backbuffer (an output texture with no final blit). When using a real backbuffer,
// Camera Capture forces an intermediate attachment, ensuring we can still sample activeColorTexture.
passData.source = resourceData.activeColorTexture;
passData.captureActions = cameraData.captureActions;
// Setup up the builder
builder.AllowPassCulling(false);
builder.UseTexture(resourceData.activeColorTexture, AccessFlags.Read);
builder.SetRenderFunc(static (UnsafePassData data, UnsafeGraphContext unsafeContext) =>
{
var nativeCommandBuffer = CommandBufferHelpers.GetNativeCommandBuffer(unsafeContext.cmd);
var captureActions = data.captureActions;
for (data.captureActions.Reset(); data.captureActions.MoveNext();)
captureActions.Current(data.source, nativeCommandBuffer);
});
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0f5a9bd148c9a4a3198fdd2365b6e514
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,323 @@
using System;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
namespace UnityEngine.Rendering.Universal.Internal
{
// Note: this pass can't be done at the same time as post-processing as it needs to be done in
// advance in case we're doing on-tile color grading.
/// <summary>
/// Renders a color grading LUT texture.
/// </summary>
public partial class ColorGradingLutPass : ScriptableRenderPass
{
const string k_InternalColorLutName = "_InternalColorGradingLut";
readonly Material m_LutBuilderLdr;
readonly Material m_LutBuilderHdr;
internal readonly GraphicsFormat m_HdrLutFormat;
internal readonly GraphicsFormat m_LdrLutFormat;
bool m_AllowColorGradingACESHDR = true;
/// <summary>
/// Creates a new <c>ColorGradingLutPass</c> instance.
/// </summary>
/// <param name="evt">The <c>RenderPassEvent</c> to use.</param>
/// <param name="data">The <c>PostProcessData</c> resources to use.</param>
/// <seealso cref="RenderPassEvent"/>
/// <seealso cref="PostProcessData"/>
public ColorGradingLutPass(RenderPassEvent evt, PostProcessData data)
{
profilingSampler = new ProfilingSampler("Blit Color LUT");
renderPassEvent = evt;
m_LutBuilderLdr = PostProcessUtils.LoadShader(data.shaders.lutBuilderLdrPS, passName);
m_LutBuilderHdr = PostProcessUtils.LoadShader(data.shaders.lutBuilderHdrPS, passName);
// Warm up lut format as IsFormatSupported adds GC pressure...
// UUM-41070: We require `Linear | Render` but with the deprecated FormatUsage this was checking `Blend`
// For now, we keep checking for `Blend` until the performance hit of doing the correct checks is evaluated
const GraphicsFormatUsage kFlags = GraphicsFormatUsage.Blend;
if (SystemInfo.IsFormatSupported(GraphicsFormat.R16G16B16A16_SFloat, kFlags))
m_HdrLutFormat = GraphicsFormat.R16G16B16A16_SFloat;
else if (SystemInfo.IsFormatSupported(GraphicsFormat.B10G11R11_UFloatPack32, kFlags))
// Precision can be too low, if FP16 primary renderTarget is requested by the user.
// But it's better than falling back to R8G8B8A8_UNorm in the worst case.
m_HdrLutFormat = GraphicsFormat.B10G11R11_UFloatPack32;
else
// Obviously using this for log lut encoding is a very bad idea for precision but we
// need it for compatibility reasons and avoid black screens on platforms that don't
// support floating point formats. Expect banding and posterization artifact if this
// ends up being used.
m_HdrLutFormat = GraphicsFormat.R8G8B8A8_UNorm;
m_LdrLutFormat = GraphicsFormat.R8G8B8A8_UNorm;
if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLES3 && Graphics.minOpenGLESVersion <= OpenGLESVersion.OpenGLES30 && SystemInfo.graphicsDeviceName.StartsWith("Adreno (TM) 3"))
m_AllowColorGradingACESHDR = false;
}
/// <summary>
/// Sets up the pass.
/// </summary>
/// <param name="internalLut">The RTHandle to use to render to.</param>
/// <seealso cref="RTHandle"/>
public void Setup(in RTHandle internalLut)
{
}
/// <summary>
/// Get a descriptor and filter mode for the required texture for this pass
/// </summary>
/// <param name="postProcessingData">The pass will use settings from <c>PostProcessingData</c> for the pass.</param>
/// <param name="descriptor">The <c>RenderTextureDescriptor</c> used by the pass.</param>
/// <param name="filterMode">The <c>FilterMode</c> used by the pass.</param>
public void ConfigureDescriptor(in PostProcessingData postProcessingData, out RenderTextureDescriptor descriptor, out FilterMode filterMode)
{
ConfigureDescriptor(postProcessingData.universalPostProcessingData, out descriptor, out filterMode);
}
/// <summary>
/// Get a descriptor and filter mode for the required texture for this pass
/// </summary>
/// <param name="postProcessingData">The pass will use settings from <c>PostProcessingData</c> for the pass.</param>
/// <param name="descriptor">The <c>RenderTextureDescriptor</c> used by the pass.</param>
/// <param name="filterMode">The <c>FilterMode</c> used by the pass.</param>
public void ConfigureDescriptor(in UniversalPostProcessingData postProcessingData, out RenderTextureDescriptor descriptor, out FilterMode filterMode)
{
bool hdr = postProcessingData.gradingMode == ColorGradingMode.HighDynamicRange;
int lutHeight = postProcessingData.lutSize;
int lutWidth = lutHeight * lutHeight;
var format = hdr ? m_HdrLutFormat : m_LdrLutFormat;
descriptor = new RenderTextureDescriptor(lutWidth, lutHeight, format, 0);
descriptor.vrUsage = VRTextureUsage.None; // We only need one for both eyes in VR
filterMode = FilterMode.Bilinear;
}
private class PassData
{
internal UniversalCameraData cameraData;
internal Material lutBuilderLdr;
internal Material lutBuilderHdr;
internal TextureHandle internalColorLut;
internal int lutSize;
internal bool hdrGrading;
internal bool allowColorGradingACESHDR;
}
private static void ExecutePass(RasterCommandBuffer cmd, PassData passData, RTHandle internalLutTarget)
{
var lutBuilderLdr = passData.lutBuilderLdr;
var lutBuilderHdr = passData.lutBuilderHdr;
var allowColorGradingACESHDR = passData.allowColorGradingACESHDR;
using (new ProfilingScope(cmd, URPProfilingSamplers.ColorGradingLUT, passData.hdrGrading ? lutBuilderHdr : lutBuilderLdr))
{
// TODO: should these components be set instead?
// Fetch all color grading settings
var stack = VolumeManager.instance.stack;
var channelMixer = stack.GetComponent<ChannelMixer>();
var colorAdjustments = stack.GetComponent<ColorAdjustments>();
var curves = stack.GetComponent<ColorCurves>();
var liftGammaGain = stack.GetComponent<LiftGammaGain>();
var shadowsMidtonesHighlights = stack.GetComponent<ShadowsMidtonesHighlights>();
var splitToning = stack.GetComponent<SplitToning>();
var tonemapping = stack.GetComponent<Tonemapping>();
var whiteBalance = stack.GetComponent<WhiteBalance>();
bool hdr = passData.hdrGrading;
// Prepare texture & material
var material = hdr ? lutBuilderHdr : lutBuilderLdr;
// Prepare data
var lmsColorBalance = ColorUtils.ColorBalanceToLMSCoeffs(whiteBalance.temperature.value, whiteBalance.tint.value);
var hueSatCon = new Vector4(colorAdjustments.hueShift.value / 360f, colorAdjustments.saturation.value / 100f + 1f, colorAdjustments.contrast.value / 100f + 1f, 0f);
var channelMixerR = new Vector4(channelMixer.redOutRedIn.value / 100f, channelMixer.redOutGreenIn.value / 100f, channelMixer.redOutBlueIn.value / 100f, 0f);
var channelMixerG = new Vector4(channelMixer.greenOutRedIn.value / 100f, channelMixer.greenOutGreenIn.value / 100f, channelMixer.greenOutBlueIn.value / 100f, 0f);
var channelMixerB = new Vector4(channelMixer.blueOutRedIn.value / 100f, channelMixer.blueOutGreenIn.value / 100f, channelMixer.blueOutBlueIn.value / 100f, 0f);
var shadowsHighlightsLimits = new Vector4(
shadowsMidtonesHighlights.shadowsStart.value,
shadowsMidtonesHighlights.shadowsEnd.value,
shadowsMidtonesHighlights.highlightsStart.value,
shadowsMidtonesHighlights.highlightsEnd.value
);
var (shadows, midtones, highlights) = ColorUtils.PrepareShadowsMidtonesHighlights(
shadowsMidtonesHighlights.shadows.value,
shadowsMidtonesHighlights.midtones.value,
shadowsMidtonesHighlights.highlights.value
);
var (lift, gamma, gain) = ColorUtils.PrepareLiftGammaGain(
liftGammaGain.lift.value,
liftGammaGain.gamma.value,
liftGammaGain.gain.value
);
var (splitShadows, splitHighlights) = ColorUtils.PrepareSplitToning(
splitToning.shadows.value,
splitToning.highlights.value,
splitToning.balance.value
);
int lutHeight = passData.lutSize;
int lutWidth = lutHeight * lutHeight;
var lutParameters = new Vector4(lutHeight, 0.5f / lutWidth, 0.5f / lutHeight,
lutHeight / (lutHeight - 1f));
// Fill in constants
material.SetVector(ShaderConstants._Lut_Params, lutParameters);
material.SetVector(ShaderConstants._ColorBalance, lmsColorBalance);
material.SetVector(ShaderConstants._ColorFilter, colorAdjustments.colorFilter.value.linear);
material.SetVector(ShaderConstants._ChannelMixerRed, channelMixerR);
material.SetVector(ShaderConstants._ChannelMixerGreen, channelMixerG);
material.SetVector(ShaderConstants._ChannelMixerBlue, channelMixerB);
material.SetVector(ShaderConstants._HueSatCon, hueSatCon);
material.SetVector(ShaderConstants._Lift, lift);
material.SetVector(ShaderConstants._Gamma, gamma);
material.SetVector(ShaderConstants._Gain, gain);
material.SetVector(ShaderConstants._Shadows, shadows);
material.SetVector(ShaderConstants._Midtones, midtones);
material.SetVector(ShaderConstants._Highlights, highlights);
material.SetVector(ShaderConstants._ShaHiLimits, shadowsHighlightsLimits);
material.SetVector(ShaderConstants._SplitShadows, splitShadows);
material.SetVector(ShaderConstants._SplitHighlights, splitHighlights);
// YRGB curves
material.SetTexture(ShaderConstants._CurveMaster, curves.master.value.GetTexture());
material.SetTexture(ShaderConstants._CurveRed, curves.red.value.GetTexture());
material.SetTexture(ShaderConstants._CurveGreen, curves.green.value.GetTexture());
material.SetTexture(ShaderConstants._CurveBlue, curves.blue.value.GetTexture());
// Secondary curves
material.SetTexture(ShaderConstants._CurveHueVsHue, curves.hueVsHue.value.GetTexture());
material.SetTexture(ShaderConstants._CurveHueVsSat, curves.hueVsSat.value.GetTexture());
material.SetTexture(ShaderConstants._CurveLumVsSat, curves.lumVsSat.value.GetTexture());
material.SetTexture(ShaderConstants._CurveSatVsSat, curves.satVsSat.value.GetTexture());
// Tonemapping (baked into the lut for HDR)
if (hdr)
{
material.shaderKeywords = null;
switch (tonemapping.mode.value)
{
case TonemappingMode.Neutral: material.EnableKeyword(ShaderKeywordStrings.TonemapNeutral); break;
case TonemappingMode.ACES: material.EnableKeyword(allowColorGradingACESHDR ? ShaderKeywordStrings.TonemapACES : ShaderKeywordStrings.TonemapNeutral); break;
default: break; // None
}
// HDR output is active
if (passData.cameraData.isHDROutputActive)
{
Vector4 hdrOutputLuminanceParams;
Vector4 hdrOutputGradingParams;
UniversalRenderPipeline.GetHDROutputLuminanceParameters(passData.cameraData.hdrDisplayInformation, passData.cameraData.hdrDisplayColorGamut, tonemapping, out hdrOutputLuminanceParams);
UniversalRenderPipeline.GetHDROutputGradingParameters(tonemapping, out hdrOutputGradingParams);
material.SetVector(ShaderPropertyId.hdrOutputLuminanceParams, hdrOutputLuminanceParams);
material.SetVector(ShaderPropertyId.hdrOutputGradingParams, hdrOutputGradingParams);
HDROutputUtils.ConfigureHDROutput(material, passData.cameraData.hdrDisplayColorGamut, HDROutputUtils.Operation.ColorConversion);
}
}
passData.cameraData.xr.StopSinglePass(cmd);
// Render the lut.
Blitter.BlitTexture(cmd, internalLutTarget, Vector2.one, material, 0);
passData.cameraData.xr.StartSinglePass(cmd);
}
}
internal void Render(RenderGraph renderGraph, ContextContainer frameData, out TextureHandle internalColorLut)
{
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
UniversalPostProcessingData postProcessingData = frameData.Get<UniversalPostProcessingData>();
this.ConfigureDescriptor(in postProcessingData, out var lutDesc, out var filterMode);
internalColorLut = UniversalRenderer.CreateRenderGraphTexture(renderGraph, lutDesc, k_InternalColorLutName, true, filterMode);
using (var builder = renderGraph.AddRasterRenderPass<PassData>(passName, out var passData, profilingSampler))
{
passData.cameraData = cameraData;
passData.internalColorLut = internalColorLut;
builder.SetRenderAttachment(internalColorLut, 0, AccessFlags.WriteAll);
passData.lutBuilderLdr = m_LutBuilderLdr;
passData.lutBuilderHdr = m_LutBuilderHdr;
passData.allowColorGradingACESHDR = m_AllowColorGradingACESHDR;
passData.lutSize = postProcessingData.lutSize;
passData.hdrGrading = postProcessingData.gradingMode == ColorGradingMode.HighDynamicRange;
// TODO RENDERGRAPH: culling? force culling off for testing
builder.AllowPassCulling(false);
builder.SetRenderFunc(static (PassData data, RasterGraphContext context) =>
{
ExecutePass(context.cmd, data, data.internalColorLut);
});
return;
}
}
/// <inheritdoc cref="IRenderGraphRecorder.RecordRenderGraph"/>
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
var resourceData = frameData.Get<UniversalResourceData>();
TextureHandle colorLut;
Render(renderGraph, frameData, out colorLut);
resourceData.internalColorLut = colorLut;
}
/// <summary>
/// Cleans up resources used by the pass.
/// </summary>
public void Cleanup()
{
CoreUtils.Destroy(m_LutBuilderLdr);
CoreUtils.Destroy(m_LutBuilderHdr);
}
// Precomputed shader ids to same some CPU cycles (mostly affects mobile)
static class ShaderConstants
{
public static readonly int _Lut_Params = Shader.PropertyToID("_Lut_Params");
public static readonly int _ColorBalance = Shader.PropertyToID("_ColorBalance");
public static readonly int _ColorFilter = Shader.PropertyToID("_ColorFilter");
public static readonly int _ChannelMixerRed = Shader.PropertyToID("_ChannelMixerRed");
public static readonly int _ChannelMixerGreen = Shader.PropertyToID("_ChannelMixerGreen");
public static readonly int _ChannelMixerBlue = Shader.PropertyToID("_ChannelMixerBlue");
public static readonly int _HueSatCon = Shader.PropertyToID("_HueSatCon");
public static readonly int _Lift = Shader.PropertyToID("_Lift");
public static readonly int _Gamma = Shader.PropertyToID("_Gamma");
public static readonly int _Gain = Shader.PropertyToID("_Gain");
public static readonly int _Shadows = Shader.PropertyToID("_Shadows");
public static readonly int _Midtones = Shader.PropertyToID("_Midtones");
public static readonly int _Highlights = Shader.PropertyToID("_Highlights");
public static readonly int _ShaHiLimits = Shader.PropertyToID("_ShaHiLimits");
public static readonly int _SplitShadows = Shader.PropertyToID("_SplitShadows");
public static readonly int _SplitHighlights = Shader.PropertyToID("_SplitHighlights");
public static readonly int _CurveMaster = Shader.PropertyToID("_CurveMaster");
public static readonly int _CurveRed = Shader.PropertyToID("_CurveRed");
public static readonly int _CurveGreen = Shader.PropertyToID("_CurveGreen");
public static readonly int _CurveBlue = Shader.PropertyToID("_CurveBlue");
public static readonly int _CurveHueVsHue = Shader.PropertyToID("_CurveHueVsHue");
public static readonly int _CurveHueVsSat = Shader.PropertyToID("_CurveHueVsSat");
public static readonly int _CurveLumVsSat = Shader.PropertyToID("_CurveLumVsSat");
public static readonly int _CurveSatVsSat = Shader.PropertyToID("_CurveSatVsSat");
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4ab9de92acc9d124bbc8f9b8e240d9c4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,224 @@
using System;
using UnityEngine.Rendering.RenderGraphModule;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.RenderGraphModule.Util;
namespace UnityEngine.Rendering.Universal.Internal
{
/// <summary>
/// Copy the given color buffer to the given destination color buffer.
///
/// You can use this pass to copy a color buffer to the destination,
/// so you can use it later in rendering. For example, you can copy
/// the opaque texture to use it for distortion effects.
/// </summary>
public partial class CopyColorPass : ScriptableRenderPass
{
int m_SampleOffsetShaderHandle;
Material m_SamplingMaterial;
Downsampling m_DownsamplingMethod;
Material m_CopyColorMaterial;
/// <summary>
/// Creates a new <c>CopyColorPass</c> instance.
/// </summary>
/// <param name="evt">The <c>RenderPassEvent</c> to use.</param>
/// <param name="samplingMaterial">The <c>Material</c> to use for downsampling quarter-resolution image with box filtering.</param>
/// <param name="copyColorMaterial">The <c>Material</c> to use for other downsampling options.</param>
/// <param name="customPassName">An optional custom profiling name to disambiguate multiple copy passes.</param>
/// <seealso cref="RenderPassEvent"/>
/// <seealso cref="Downsampling"/>
public CopyColorPass(RenderPassEvent evt, Material samplingMaterial, Material copyColorMaterial = null, string customPassName = null)
{
profilingSampler = customPassName != null ? new ProfilingSampler(customPassName) : URPProfilingSamplers.CopyColor;
m_SamplingMaterial = samplingMaterial;
m_CopyColorMaterial = copyColorMaterial;
m_SampleOffsetShaderHandle = Shader.PropertyToID("_SampleOffset");
renderPassEvent = evt;
m_DownsamplingMethod = Downsampling.None;
}
/// <summary>
/// Get a descriptor and filter mode for the required texture for this pass
/// </summary>
/// <param name="downsamplingMethod"></param>
/// <param name="descriptor"></param>
/// <param name="filterMode"></param>
/// <seealso cref="Downsampling"/>
/// <seealso cref="RenderTextureDescriptor"/>
/// <seealso cref="FilterMode"/>
public static void ConfigureDescriptor(Downsampling downsamplingMethod, ref RenderTextureDescriptor descriptor, out FilterMode filterMode)
{
descriptor.msaaSamples = 1;
descriptor.depthStencilFormat = GraphicsFormat.None;
if (downsamplingMethod == Downsampling._2xBilinear)
{
descriptor.width = Mathf.Max(1, descriptor.width / 2);
descriptor.height = Mathf.Max(1, descriptor.height / 2);
}
else if (downsamplingMethod == Downsampling._4xBox || downsamplingMethod == Downsampling._4xBilinear)
{
descriptor.width = Mathf.Max(1, descriptor.width / 4);
descriptor.height = Mathf.Max(1, descriptor.height / 4);
}
filterMode = downsamplingMethod == Downsampling.None ? FilterMode.Point : FilterMode.Bilinear;
}
internal static void ConfigureDescriptor(Downsampling downsamplingMethod, ref TextureDesc descriptor, out FilterMode filterMode)
{
descriptor.msaaSamples = MSAASamples.None;
if (downsamplingMethod == Downsampling._2xBilinear)
{
descriptor.width = Mathf.Max(1, descriptor.width / 2);
descriptor.height = Mathf.Max(1, descriptor.height / 2);
}
else if (downsamplingMethod == Downsampling._4xBox || downsamplingMethod == Downsampling._4xBilinear)
{
descriptor.width = Mathf.Max(1, descriptor.width / 4);
descriptor.height = Mathf.Max(1, descriptor.height / 4);
}
filterMode = downsamplingMethod == Downsampling.None ? FilterMode.Point : FilterMode.Bilinear;
}
/// <summary>
/// Configure the pass with the source and destination to execute on.
/// </summary>
/// <param name="source">Source render target.</param>
/// <param name="destination">Destination render target.</param>
/// <param name="downsampling">The downsampling method to use.</param>
[Obsolete("Use RTHandles for source and destination #from(2022.1) #breakingFrom(2023.1).", true)]
public void Setup(RenderTargetIdentifier source, RenderTargetHandle destination, Downsampling downsampling)
{
throw new NotSupportedException("Setup with RenderTargetIdentifier has been deprecated. Use it with RTHandles instead.");
}
/// <summary>
/// Configure the pass with the source and destination to execute on.
/// </summary>
/// <param name="source">Source render target.</param>
/// <param name="destination">Destination render target.</param>
/// <param name="downsampling">The downsampling method to use.</param>
public void Setup(RTHandle source, RTHandle destination, Downsampling downsampling)
{
m_DownsamplingMethod = downsampling;
}
private static void ExecutePass(RasterCommandBuffer cmd, PassData passData, RTHandle source, bool useDrawProceduralBlit)
{
var samplingMaterial = passData.samplingMaterial;
var copyColorMaterial = passData.copyColorMaterial;
var downsamplingMethod = passData.downsamplingMethod;
var sampleOffsetShaderHandle = passData.sampleOffsetShaderHandle;
if (samplingMaterial == null)
{
Debug.LogErrorFormat(
"Missing {0}. Copy Color render pass will not execute. Check for missing reference in the renderer resources.",
samplingMaterial);
return;
}
Vector2 viewportScale = source.useScaling ? new Vector2(source.rtHandleProperties.rtHandleScale.x, source.rtHandleProperties.rtHandleScale.y) : Vector2.one;
switch (downsamplingMethod)
{
case Downsampling.None:
Blitter.BlitTexture(cmd, source, viewportScale, copyColorMaterial, 0);
break;
case Downsampling._2xBilinear:
Blitter.BlitTexture(cmd, source, viewportScale, copyColorMaterial, 1);
break;
case Downsampling._4xBox:
samplingMaterial.SetFloat(sampleOffsetShaderHandle, 2);
Blitter.BlitTexture(cmd, source, viewportScale, samplingMaterial, 0);
break;
case Downsampling._4xBilinear:
Blitter.BlitTexture(cmd, source, viewportScale, copyColorMaterial, 1);
break;
}
}
private class PassData
{
internal TextureHandle source;
internal TextureHandle destination;
internal bool useProceduralBlit;
internal Material samplingMaterial;
internal Material copyColorMaterial;
internal Downsampling downsamplingMethod;
internal int sampleOffsetShaderHandle;
}
internal TextureHandle Render(RenderGraph renderGraph, ContextContainer frameData, out TextureHandle destination, in TextureHandle source, Downsampling downsampling)
{
m_DownsamplingMethod = downsampling;
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
var srcDesc = source.GetDescriptor(renderGraph);
ConfigureDescriptor(downsampling, ref srcDesc, out var filterMode);
destination = UniversalRenderer.CreateRenderGraphTexture(renderGraph, srcDesc, "_CameraOpaqueTexture", true, srcDesc.clearColor, filterMode);
RenderInternal(renderGraph, destination, source, cameraData.xr.enabled);
return destination;
}
// This will not create a new texture, but will reuse an existing one as destination.
// Typical use case is a persistent texture imported to the render graph. For example history textures.
// Note that the amount of downsampling is determined by the destination size.
// Therefore, the downsampling param controls only the algorithm (shader) used for the downsampling, not size.
internal void RenderToExistingTexture(RenderGraph renderGraph, ContextContainer frameData, in TextureHandle destination, in TextureHandle source, Downsampling downsampling = Downsampling.None)
{
m_DownsamplingMethod = downsampling;
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
RenderInternal(renderGraph, destination, source, cameraData.xr.enabled);
}
static readonly string k_CopyColorPassName = "Copy Color";
static readonly string k_DownsampleAndCopyPassName = "Downsample Color";
private void RenderInternal(RenderGraph renderGraph, in TextureHandle destination, in TextureHandle source, bool useProceduralBlit)
{
if (m_DownsamplingMethod != Downsampling.None)
{
AddDownsampleAndCopyColorRenderPass(renderGraph, destination, source, useProceduralBlit, k_DownsampleAndCopyPassName);
}
else
{
using (var builder = renderGraph.AddBlitPass(source, destination, Vector2.one, Vector2.zero, returnBuilder: true, passName: k_CopyColorPassName))
{
builder.SetGlobalTextureAfterPass(destination, Shader.PropertyToID("_CameraOpaqueTexture"));
}
}
}
private void AddDownsampleAndCopyColorRenderPass(RenderGraph renderGraph, in TextureHandle destination, in TextureHandle source, bool useProceduralBlit, string passName)
{
using (var builder = renderGraph.AddRasterRenderPass<PassData>(passName, out var passData, profilingSampler))
{
builder.SetRenderAttachment(destination, 0, AccessFlags.WriteAll);
passData.source = source;
builder.UseTexture(source, AccessFlags.Read);
passData.useProceduralBlit = useProceduralBlit;
passData.samplingMaterial = m_SamplingMaterial;
passData.copyColorMaterial = m_CopyColorMaterial;
passData.downsamplingMethod = m_DownsamplingMethod;
passData.sampleOffsetShaderHandle = m_SampleOffsetShaderHandle;
builder.SetGlobalTextureAfterPass(destination, Shader.PropertyToID("_CameraOpaqueTexture"));
builder.SetRenderFunc(static (PassData data, RasterGraphContext context) =>
{
ExecutePass(context.cmd, data, data.source, data.useProceduralBlit);
});
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7e18083ec47e7446fac286a4ecd439fc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,294 @@
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
namespace UnityEngine.Rendering.Universal.Internal
{
/// <summary>
/// Copy the given depth buffer into the given destination depth buffer.
///
/// You can use this pass to copy a depth buffer to a destination,
/// so you can use it later in rendering. If the source texture has MSAA
/// enabled, the pass uses a custom MSAA resolve. If the source texture
/// does not have MSAA enabled, the pass uses a Blit or a Copy Texture
/// operation, depending on what the current platform supports.
/// </summary>
public partial class CopyDepthPass : ScriptableRenderPass
{
// In XR CopyDepth, we need a special workaround to handle dummy color issue in RenderGraph.
internal bool CopyToDepthXR { get; set; }
Material m_CopyDepthMaterial;
/// <summary>
/// Shader resource ids used to communicate with the shader implementation
/// </summary>
static class ShaderConstants
{
public static readonly int _CameraDepthAttachment = Shader.PropertyToID("_CameraDepthAttachment");
public static readonly int _CameraDepthTexture = Shader.PropertyToID("_CameraDepthTexture");
public static readonly int _ZWriteShaderHandle = Shader.PropertyToID("_ZWrite");
}
/// <summary>
/// Creates a new <c>CopyDepthPass</c> instance.
/// </summary>
/// <param name="evt">The <c>RenderPassEvent</c> to use.</param>
/// <param name="copyDepthShader">The <c>Shader</c> to use for copying the depth.</param>
/// <param name="shouldClear">Controls whether it should do a clear before copying the depth.</param>
/// <param name="copyToDepth">Deprecated, the parameter is ignored. This is now automatically derived from the source and destination TextureHandle.</param>
/// <param name="copyResolvedDepth">Deprecated, the parameter is ignored. This is now automatically derived from the source and destination TextureHandle.</param>
/// <param name="customPassName">An optional custom profiling name to disambiguate multiple copy passes.</param>
/// <seealso cref="RenderPassEvent"/>
public CopyDepthPass(RenderPassEvent evt, Shader copyDepthShader, bool shouldClear = false, bool copyToDepth = false, bool copyResolvedDepth = false, string customPassName = null)
{
profilingSampler = customPassName != null ? new ProfilingSampler(customPassName) : URPProfilingSamplers.CopyDepth;
m_CopyDepthMaterial = copyDepthShader != null ? CoreUtils.CreateEngineMaterial(copyDepthShader) : null;
renderPassEvent = evt;
CopyToDepthXR = false;
}
/// <summary>
/// Configure the pass with the source and destination to execute on.
/// </summary>
/// <param name="source">Source Render Target</param>
/// <param name="destination">Destination Render Target</param>
public void Setup(RTHandle source, RTHandle destination)
{
}
/// <summary>
/// Cleans up resources used by the pass.
/// </summary>
public void Dispose()
{
CoreUtils.Destroy(m_CopyDepthMaterial);
}
private class PassData
{
internal TextureHandle source;
internal TextureHandle destination;
internal UniversalCameraData cameraData;
internal Material copyDepthMaterial;
internal bool copyResolvedDepth;
internal bool copyToDepth;
internal bool setViewport;
}
private static void ExecutePass(RasterCommandBuffer cmd, PassData passData, RTHandle source, Vector4 scaleBias)
{
var copyDepthMaterial = passData.copyDepthMaterial;
if (copyDepthMaterial == null)
{
Debug.LogErrorFormat("Missing {0}. Copy Depth render pass will not execute. Check for missing reference in the renderer resources.", copyDepthMaterial);
return;
}
int cameraSamples;
if (passData.copyResolvedDepth)
{
cameraSamples = 1;
}
else
{
cameraSamples = source.rt.antiAliasing;
}
switch (cameraSamples)
{
case 8:
cmd.SetKeyword(ShaderGlobalKeywords.DepthMsaa2, false);
cmd.SetKeyword(ShaderGlobalKeywords.DepthMsaa4, false);
cmd.SetKeyword(ShaderGlobalKeywords.DepthMsaa8, true);
break;
case 4:
cmd.SetKeyword(ShaderGlobalKeywords.DepthMsaa2, false);
cmd.SetKeyword(ShaderGlobalKeywords.DepthMsaa4, true);
cmd.SetKeyword(ShaderGlobalKeywords.DepthMsaa8, false);
break;
case 2:
cmd.SetKeyword(ShaderGlobalKeywords.DepthMsaa2, true);
cmd.SetKeyword(ShaderGlobalKeywords.DepthMsaa4, false);
cmd.SetKeyword(ShaderGlobalKeywords.DepthMsaa8, false);
break;
// MSAA disabled, auto resolve supported, resolve texture requested, or ms textures not supported
default:
cmd.SetKeyword(ShaderGlobalKeywords.DepthMsaa2, false);
cmd.SetKeyword(ShaderGlobalKeywords.DepthMsaa4, false);
cmd.SetKeyword(ShaderGlobalKeywords.DepthMsaa8, false);
break;
}
cmd.SetKeyword(ShaderGlobalKeywords._OUTPUT_DEPTH, passData.copyToDepth);
// When we render to the backbuffer, we update the viewport to cover the entire screen just in case it hasn't been updated already.
if (passData.setViewport)
cmd.SetViewport(passData.cameraData.pixelRect);
copyDepthMaterial.SetTexture(ShaderConstants._CameraDepthAttachment, source);
copyDepthMaterial.SetFloat(ShaderConstants._ZWriteShaderHandle, passData.copyToDepth ? 1.0f : 0.0f);
Blitter.BlitTexture(cmd, source, scaleBias, copyDepthMaterial, 0);
}
/// <summary>
/// Sets up the Copy Depth pass for RenderGraph execution
/// </summary>
/// <param name="renderGraph">The current RenderGraph used for recording and execution of a frame.</param>
/// <param name="frameData">The renderer settings containing rendering data of the current frame.</param>
/// <param name="destination"><c>TextureHandle</c> of the destination it will copy to.</param>
/// <param name="source"><c>TextureHandle</c> of the source it will copy from.</param>
/// <param name="bindAsCameraDepth">If this is true, the destination texture is bound as _CameraDepthTexture after the copy pass</param>
/// <param name="passName">The pass name used for debug and identifying the pass.</param>
public void Render(RenderGraph renderGraph, ContextContainer frameData, TextureHandle destination, TextureHandle source, bool bindAsCameraDepth = false, string passName = "Copy Depth")
{
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
Render(renderGraph, destination, source, resourceData, cameraData, bindAsCameraDepth, passName);
}
/// <summary>
/// Sets up the Copy Depth pass for RenderGraph execution
/// </summary>
/// <param name="renderGraph">The current RenderGraph used for recording and execution of a frame.</param>
/// <param name="destination"><c>TextureHandle</c> of the destination it will copy to.</param>
/// <param name="source"><c>TextureHandle</c> of the source it will copy from.</param>
/// <param name="resourceData">URP texture handles for the current frame.</param>
/// <param name="cameraData">Camera settings for the current frame.</param>
/// <param name="bindAsCameraDepth">If this is true, the destination texture is bound as _CameraDepthTexture after the copy pass</param>
/// <param name="passName">The pass name used for debug and identifying the pass.</param>
public void Render(RenderGraph renderGraph, TextureHandle destination, TextureHandle source, UniversalResourceData resourceData, UniversalCameraData cameraData, bool bindAsCameraDepth = false, string passName = "Copy Depth")
{
Debug.Assert(source.IsValid(), "CopyDepthPass source is not a valid texture.");
Debug.Assert(destination.IsValid(), "CopyDepthPass destination is not a valid texture.");
var sourceDesc = renderGraph.GetTextureDesc(source);
var destinationDesc = renderGraph.GetRenderTargetInfo(destination);
bool dstHasDepthFormat = GraphicsFormatUtility.IsDepthFormat(destinationDesc.format);
bool hasMSAA = sourceDesc.msaaSamples != MSAASamples.None;
var canUseResolvedDepth = !sourceDesc.bindTextureMS && RenderingUtils.MultisampleDepthResolveSupported();
var canSampleMSAADepth = sourceDesc.bindTextureMS && SystemInfo.supportsMultisampledTextures != 0;
Debug.Assert(!hasMSAA || canUseResolvedDepth || canSampleMSAADepth || !dstHasDepthFormat
, "Can't copy depth to destination with depth format due to MSAA and platform/API limitations: no resolved depth resource (bindMS), depth resolve unsupported, and MSAA depth sampling unsupported.");
// Having a different pass name than profilingSampler.name is bad practice but this method was public before we cleaned up this naming
using (var builder = renderGraph.AddRasterRenderPass<PassData>(passName, out var passData, profilingSampler))
{
passData.copyDepthMaterial = m_CopyDepthMaterial;
passData.cameraData = cameraData;
//When we can't resolve depth and can't sample MSAA depth, we should have set the target to color. This works on GLES for example.
//Perhaps we need to check for !dstHasDepthFormat but we keep to original check to avoid any issues for now.
passData.copyResolvedDepth = canUseResolvedDepth || !canSampleMSAADepth;
passData.copyToDepth = dstHasDepthFormat;
passData.setViewport = CopyToDepthXR;
if (cameraData.xr.enabled)
{
// Multiview render regions are incompatible with the inner (foveal) pass in Quad View
if (!cameraData.xr.isQuadViewInnerPass)
{
builder.SetExtendedFeatureFlags(ExtendedFeatureFlags.MultiviewRenderRegionsCompatible);
}
}
if (CopyToDepthXR)
{
// Writes depth using custom depth output
builder.SetRenderAttachmentDepth(destination, AccessFlags.WriteAll);
#if ENABLE_VR && ENABLE_XR_MODULE
// binding a dummy color target as a workaround to NRP depth only rendering limitation:
// "Attempting to render to a depth only surface with no dummy color attachment"
if (cameraData.xr.enabled && cameraData.xr.copyDepth)
{
RenderTargetInfo backBufferDesc = renderGraph.GetRenderTargetInfo(resourceData.backBufferColor);
// In the case where MSAA is enabled, we have to bind a different dummy texture
// This is to ensure that we don't render black in the resolve result of the color backbuffer
// This also makes this pass unmergeable in this case, potentially impacting performance
if (backBufferDesc.msaaSamples > 1)
{
TextureHandle dummyXRRenderTarget = renderGraph.CreateTexture(new TextureDesc(backBufferDesc.width, backBufferDesc.height, false, true)
{
name = "XR Copy Depth Dummy Render Target",
slices = backBufferDesc.volumeDepth,
format = backBufferDesc.format,
msaaSamples = (MSAASamples)backBufferDesc.msaaSamples,
clearBuffer = false,
bindTextureMS = backBufferDesc.bindMS
});
builder.SetRenderAttachment(dummyXRRenderTarget, 0);
}
else
builder.SetRenderAttachment(resourceData.backBufferColor, 0);
}
#endif
}
else if (passData.copyToDepth)
{
// Writes depth using custom depth output
builder.SetRenderAttachmentDepth(destination, AccessFlags.WriteAll);
#if UNITY_EDITOR
// binding a dummy color target as a workaround to an OSX issue in Editor scene view (UUM-47698).
// Also required for preview camera rendering for grid drawn with builtin RP (UUM-55171).
// Also required for render gizmos (UUM-91335).
// When MSAA is enabled with Dbuffer can cause sample count mismatches between active color and depth target; create a dummy color target to resolve. (UUM-131330)
if (cameraData.isSceneViewCamera || cameraData.isPreviewCamera || UnityEditor.Handles.ShouldRenderGizmos())
{
// get info for the active color
var activeColorInfo = renderGraph.GetRenderTargetInfo(resourceData.activeColorTexture);
// destination depth info (the texture we created earlier)
var destInfo = renderGraph.GetRenderTargetInfo(destination);
// if samples mismatch, create a dummy color RT with dest's samples
if (activeColorInfo.msaaSamples != destInfo.msaaSamples)
{
TextureHandle dummyColor = renderGraph.CreateTexture(new TextureDesc(activeColorInfo.width, activeColorInfo.height, false, true)
{
name = "Copy Depth Editor Dummy Color",
slices = activeColorInfo.volumeDepth,
format = activeColorInfo.format,
msaaSamples = (MSAASamples)destInfo.msaaSamples, // match the depth target
clearBuffer = false,
bindTextureMS = activeColorInfo.bindMS
});
builder.SetRenderAttachment(dummyColor, 0);
}
else
{
builder.SetRenderAttachment(resourceData.activeColorTexture, 0);
}
}
#endif
}
else
{
// Writes depth as "grayscale color" output
builder.SetRenderAttachment(destination, 0, AccessFlags.WriteAll);
}
passData.source = source;
passData.destination = destination;
builder.UseTexture(source, AccessFlags.Read);
if (bindAsCameraDepth)
builder.SetGlobalTextureAfterPass(destination, ShaderConstants._CameraDepthTexture);
builder.AllowGlobalStateModification(true);
builder.SetRenderFunc(static (PassData data, RasterGraphContext context) =>
{
Vector4 scaleBias = RenderingUtils.GetFinalBlitScaleBias(context, data.source, data.destination);
ExecutePass(context.cmd, data, data.source, scaleBias);
});
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d6268b3babfc1004c82ace3c407f46ef
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,79 @@
using System;
using UnityEngine.Rendering.RenderGraphModule;
// cleanup code
// listMinDepth and maxDepth should be stored in a different uniform block?
// Point lights stored as vec4
// RelLightIndices should be stored in ushort instead of uint.
// TODO use Unity.Mathematics
// TODO Check if there is a bitarray structure (with dynamic size) available in Unity
namespace UnityEngine.Rendering.Universal.Internal
{
// Render all tiled-based deferred lights.
internal class DeferredPass : ScriptableRenderPass
{
DeferredLights m_DeferredLights;
public DeferredPass(RenderPassEvent evt, DeferredLights deferredLights)
{
profilingSampler = new ProfilingSampler("Render Deferred Lighting");
base.renderPassEvent = evt;
m_DeferredLights = deferredLights;
}
private class PassData
{
internal UniversalCameraData cameraData;
internal UniversalLightData lightData;
internal UniversalShadowData shadowData;
internal TextureHandle[] gbuffer;
internal DeferredLights deferredLights;
}
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
var cameraData = frameData.Get<UniversalCameraData>();
var resourceData = frameData.Get<UniversalResourceData>();
var lightData = frameData.Get<UniversalLightData>();
var shadowData = frameData.Get<UniversalShadowData>();
var color = resourceData.activeColorTexture;
var depth = resourceData.activeDepthTexture;
var gbuffer = resourceData.gBuffer;
using (var builder = renderGraph.AddRasterRenderPass<PassData>(passName, out var passData, profilingSampler))
{
passData.cameraData = cameraData;
passData.lightData = lightData;
passData.shadowData = shadowData;
builder.SetRenderAttachment(color, 0, AccessFlags.Write);
builder.SetRenderAttachmentDepth(depth, AccessFlags.ReadWrite);
passData.deferredLights = m_DeferredLights;
for (int i = 0, idx = 0; i < gbuffer.Length; ++i)
{
if (i == m_DeferredLights.GBufferLightingIndex)
continue;
builder.SetInputAttachment(gbuffer[i], idx++);
}
builder.AllowGlobalStateModification(true);
builder.SetRenderFunc(static (PassData data, RasterGraphContext context) =>
{
data.deferredLights.ExecuteDeferredPass(context.cmd, data.cameraData, data.lightData, data.shadowData, data.gbuffer);
});
}
}
// ScriptableRenderPass
public override void OnCameraCleanup(CommandBuffer cmd)
{
m_DeferredLights.OnCameraCleanup(cmd);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1d258bcfb9f91794090920274896e493
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,211 @@
using System;
using System.Collections.Generic;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
namespace UnityEngine.Rendering.Universal.Internal
{
/// <summary>
/// Render all objects that have a 'DepthNormals' and/or 'DepthNormalsOnly' pass into the given depth and normal buffers.
/// </summary>
public partial class DepthNormalOnlyPass : ScriptableRenderPass
{
internal List<ShaderTagId> shaderTagIds { get; set; }
internal bool enableRenderingLayers { get; set; } = false;
internal RenderingLayerUtils.MaskSize renderingLayersMaskSize { get; set; }
private FilteringSettings m_FilteringSettings;
// Statics
private static readonly List<ShaderTagId> k_DepthNormals = new List<ShaderTagId> { new ShaderTagId("DepthNormals"), new ShaderTagId("DepthNormalsOnly") };
private static readonly List<ShaderTagId> k_DepthNormalsOnly = new List<ShaderTagId> { new ShaderTagId("DepthNormalsOnly") };
internal static readonly string k_CameraNormalsTextureName = "_CameraNormalsTexture";
private static readonly int s_CameraDepthTextureID = Shader.PropertyToID("_CameraDepthTexture");
private static readonly int s_CameraNormalsTextureID = Shader.PropertyToID(k_CameraNormalsTextureName);
private static readonly int s_CameraRenderingLayersTextureID = Shader.PropertyToID("_CameraRenderingLayersTexture");
/// <summary>
/// Creates a new <c>DepthNormalOnlyPass</c> instance.
/// </summary>
/// <param name="evt">The <c>RenderPassEvent</c> to use.</param>
/// <param name="renderQueueRange">The <c>RenderQueueRange</c> to use for creating filtering settings that control what objects get rendered.</param>
/// <param name="layerMask">The layer mask to use for creating filtering settings that control what objects get rendered.</param>
/// <param name="sampler">The ProfilingSampler to use in the profiler and frame debugger for this pass.</param>
/// <seealso cref="RenderPassEvent"/>
/// <seealso cref="RenderQueueRange"/>
/// <seealso cref="LayerMask"/>
public DepthNormalOnlyPass(RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask, ProfilingSampler sampler = null)
{
profilingSampler = sampler ?? URPProfilingSamplers.DrawDepthNormalPrepass;
m_FilteringSettings = new FilteringSettings(renderQueueRange, layerMask);
renderPassEvent = evt;
shaderTagIds = k_DepthNormals;
}
/// <summary>
/// Finds the format to use for the normals texture.
/// </summary>
/// <returns>The GraphicsFormat to use with the Normals texture.</returns>
public static GraphicsFormat GetGraphicsFormat()
{
if (SystemInfo.IsFormatSupported(GraphicsFormat.R8G8B8A8_SNorm, GraphicsFormatUsage.Render))
return GraphicsFormat.R8G8B8A8_SNorm; // Preferred format
else if (SystemInfo.IsFormatSupported(GraphicsFormat.R16G16B16A16_SFloat, GraphicsFormatUsage.Render))
return GraphicsFormat.R16G16B16A16_SFloat; // fallback
else
return GraphicsFormat.R32G32B32A32_SFloat; // fallback
}
/// <summary>
/// Configures the pass.
/// </summary>
/// <param name="depthHandle">The <c>RTHandle</c> used to render depth to.</param>
/// <param name="normalHandle">The <c>RTHandle</c> used to render normals.</param>
/// <seealso cref="RTHandle"/>
public void Setup(RTHandle depthHandle, RTHandle normalHandle)
{
enableRenderingLayers = false;
}
/// <summary>
/// Configure the pass
/// </summary>
/// <param name="depthHandle">The <c>RTHandle</c> used to render depth to.</param>
/// <param name="normalHandle">The <c>RTHandle</c> used to render normals.</param>
/// <param name="decalLayerHandle">The <c>RTHandle</c> used to render decals.</param>
public void Setup(RTHandle depthHandle, RTHandle normalHandle, RTHandle decalLayerHandle)
{
Setup(depthHandle, normalHandle);
enableRenderingLayers = true;
}
private static void ExecutePass(RasterCommandBuffer cmd, PassData passData, RendererList rendererList)
{
// Enable Rendering Layers
if (passData.enableRenderingLayers)
cmd.SetKeyword(ShaderGlobalKeywords.WriteRenderingLayers, true);
if (passData.outputSmoothness)
cmd.SetKeyword(ShaderGlobalKeywords.WriteSmoothness, true);
// Draw
cmd.DrawRendererList(rendererList);
// Clean up
if (passData.enableRenderingLayers)
cmd.SetKeyword(ShaderGlobalKeywords.WriteRenderingLayers, false);
if (passData.outputSmoothness)
cmd.SetKeyword(ShaderGlobalKeywords.WriteSmoothness, false);
}
/// <inheritdoc/>
public override void OnCameraCleanup(CommandBuffer cmd)
{
if (cmd == null)
{
throw new ArgumentNullException("cmd");
}
// This needs to be reset as the renderer might change this in runtime (UUM-36069)
shaderTagIds = k_DepthNormals;
}
/// <summary>
/// Shared pass data
/// </summary>
private class PassData
{
internal bool enableRenderingLayers;
internal bool outputSmoothness;
internal RenderingLayerUtils.MaskSize maskSize;
internal RendererListHandle rendererList;
}
private RendererListParams InitRendererListParams(UniversalRenderingData renderingData, UniversalCameraData cameraData, UniversalLightData lightData)
{
var drawSettings = RenderingUtils.CreateDrawingSettings(this.shaderTagIds, renderingData, cameraData, lightData, GetSortingCriteria(cameraData));
drawSettings.perObjectData = PerObjectData.None;
return new RendererListParams(renderingData.cullResults, drawSettings, m_FilteringSettings);
}
internal void Render(RenderGraph renderGraph, ContextContainer frameData, in TextureHandle cameraNormalsTexture, in TextureHandle depthTexture, in TextureHandle renderingLayersTexture, uint batchLayerMask, bool setGlobalDepth, bool setGlobalNormalAndRenderingLayers, bool allowPartialPass)
{
if (allowPartialPass)
{
this.shaderTagIds = k_DepthNormalsOnly;
}
else
{
this.shaderTagIds = k_DepthNormals;
}
UniversalRenderingData renderingData = frameData.Get<UniversalRenderingData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
UniversalLightData lightData = frameData.Get<UniversalLightData>();
using (var builder = renderGraph.AddRasterRenderPass<PassData>(passName, out var passData, profilingSampler))
{
builder.SetRenderAttachment(cameraNormalsTexture, 0, AccessFlags.Write);
builder.SetRenderAttachmentDepth(depthTexture, AccessFlags.ReadWrite);
passData.enableRenderingLayers = enableRenderingLayers;
#if URP_SCREEN_SPACE_REFLECTION
passData.outputSmoothness = renderingData.writesSmoothnessToDepthNormalsAlpha;
#else
passData.outputSmoothness = false;
#endif
if (passData.enableRenderingLayers)
{
builder.SetRenderAttachment(renderingLayersTexture, 1, AccessFlags.Write);
passData.maskSize = renderingLayersMaskSize;
}
var param = InitRendererListParams(renderingData, cameraData, lightData);
param.filteringSettings.batchLayerMask = batchLayerMask;
passData.rendererList = renderGraph.CreateRendererList(param);
builder.UseRendererList(passData.rendererList);
if (cameraData.xr.enabled)
{
builder.EnableFoveatedRasterization(cameraData.xr.supportsFoveatedRendering && cameraData.xrUniversal.canFoveateIntermediatePasses);
// Multiview render regions are incompatible with the inner (foveal) pass in Quad View
if (!cameraData.xr.isQuadViewInnerPass)
{
builder.SetExtendedFeatureFlags(ExtendedFeatureFlags.MultiviewRenderRegionsCompatible);
}
}
if (setGlobalNormalAndRenderingLayers)
{
builder.SetGlobalTextureAfterPass(cameraNormalsTexture, s_CameraNormalsTextureID);
if (passData.enableRenderingLayers)
builder.SetGlobalTextureAfterPass(renderingLayersTexture, s_CameraRenderingLayersTextureID);
}
if (setGlobalDepth)
builder.SetGlobalTextureAfterPass(depthTexture, s_CameraDepthTextureID);
// Required here because of RenderingLayerUtils.SetupProperties, and for setting keywords in ExecutePass
if (passData.enableRenderingLayers || passData.outputSmoothness)
builder.AllowGlobalStateModification(true);
builder.SetRenderFunc(static (PassData data, RasterGraphContext context) =>
{
if (data.enableRenderingLayers)
RenderingLayerUtils.SetupProperties(context.cmd, data.maskSize);
ExecutePass(context.cmd, data, data.rendererList);
});
}
}
/// <summary>
/// Gets the sorting criteria to use for a given camera. Override to customize behavior.
/// </summary>
/// <param name="cameraData">The UniversalCameraData of the camera to get sorting criteria for.</param>
/// <returns>The sorting criteria to use.</returns>
protected virtual SortingCriteria GetSortingCriteria(UniversalCameraData cameraData)
{
return cameraData.defaultOpaqueSortFlags;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6dfa2b31659ab4047b6bc33be7d8f07e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,112 @@
using System;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
namespace UnityEngine.Rendering.Universal.Internal
{
/// <summary>
/// Render all objects that have a 'DepthOnly' pass into the given depth buffer.
///
/// You can use this pass to prime a depth buffer for subsequent rendering.
/// Use it as a z-prepass, or use it to generate a depth buffer.
/// </summary>
public partial class DepthOnlyPass : ScriptableRenderPass
{
internal ShaderTagId shaderTagId { get; set; } = k_ShaderTagId;
FilteringSettings m_FilteringSettings;
// Statics
private static readonly ShaderTagId k_ShaderTagId = new ShaderTagId("DepthOnly");
private static readonly int s_CameraDepthTextureID = Shader.PropertyToID("_CameraDepthTexture");
/// <summary>
/// Creates a new <c>DepthOnlyPass</c> instance.
/// </summary>
/// <param name="evt">The <c>RenderPassEvent</c> to use.</param>
/// <param name="renderQueueRange">The <c>RenderQueueRange</c> to use for creating filtering settings that control what objects get rendered.</param>
/// <param name="layerMask">The layer mask to use for creating filtering settings that control what objects get rendered.</param>
/// <seealso cref="RenderPassEvent"/>
/// <seealso cref="RenderQueueRange"/>
/// <seealso cref="LayerMask"/>
public DepthOnlyPass(RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask)
{
profilingSampler = new ProfilingSampler("Draw Depth Only");
m_FilteringSettings = new FilteringSettings(renderQueueRange, layerMask);
renderPassEvent = evt;
shaderTagId = k_ShaderTagId;
}
/// <summary>
/// Configures the pass.
/// </summary>
/// <param name="baseDescriptor">The <c>RenderTextureDescriptor</c> used for the depthStencilFormat.</param>
/// <param name="depthAttachmentHandle">The <c>RTHandle</c> used to render to.</param>
/// <seealso cref="RenderTextureDescriptor"/>
/// <seealso cref="RTHandle"/>
/// <seealso cref="GraphicsFormat"/>
public void Setup(
RenderTextureDescriptor baseDescriptor,
RTHandle depthAttachmentHandle)
{
}
private static void ExecutePass(RasterCommandBuffer cmd, RendererList rendererList)
{
using (new ProfilingScope(cmd, URPProfilingSamplers.DepthPrepass))
{
cmd.DrawRendererList(rendererList);
}
}
private class PassData
{
internal RendererListHandle rendererList;
}
private RendererListParams InitRendererListParams(UniversalRenderingData renderingData, UniversalCameraData cameraData, UniversalLightData lightData)
{
var sortFlags = cameraData.defaultOpaqueSortFlags;
var drawSettings = RenderingUtils.CreateDrawingSettings(this.shaderTagId, renderingData, cameraData, lightData, sortFlags);
drawSettings.perObjectData = PerObjectData.None;
drawSettings.lodCrossFadeStencilMask = 0; // For stencil-based Lod, we use texture dither instead of stencil testing because we have the same shader variants for cross-fade shadow.
return new RendererListParams(renderingData.cullResults, drawSettings, m_FilteringSettings);
}
internal void Render(RenderGraph renderGraph, ContextContainer frameData, in TextureHandle depthTexture, uint batchLayerMask, bool setGlobalDepth)
{
UniversalRenderingData renderingData = frameData.Get<UniversalRenderingData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
UniversalLightData lightData = frameData.Get<UniversalLightData>();
using (var builder = renderGraph.AddRasterRenderPass<PassData>(passName, out var passData, profilingSampler))
{
var param = InitRendererListParams(renderingData, cameraData, lightData);
param.filteringSettings.batchLayerMask = batchLayerMask;
passData.rendererList = renderGraph.CreateRendererList(param);
builder.UseRendererList(passData.rendererList);
builder.SetRenderAttachmentDepth(depthTexture, AccessFlags.ReadWrite);
if (setGlobalDepth)
builder.SetGlobalTextureAfterPass(depthTexture, s_CameraDepthTextureID);
builder.AllowGlobalStateModification(true);
if (cameraData.xr.enabled)
{
builder.EnableFoveatedRasterization(cameraData.xr.supportsFoveatedRendering && cameraData.xrUniversal.canFoveateIntermediatePasses);
// Multiview render regions are incompatible with the inner (foveal) pass in Quad View
if (!cameraData.xr.isQuadViewInnerPass)
{
builder.SetExtendedFeatureFlags(ExtendedFeatureFlags.MultiviewRenderRegionsCompatible);
}
}
builder.SetRenderFunc(static (PassData data, RasterGraphContext context) =>
{
ExecutePass(context.cmd, data.rendererList);
});
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2b327960b30da614ca5f44f2fef0137a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,488 @@
using System;
using System.Collections.Generic;
using UnityEngine.Rendering.RenderGraphModule;
namespace UnityEngine.Rendering.Universal.Internal
{
/// <summary>
/// Draw objects into the given color and depth target
///
/// You can use this pass to render objects that have a material and/or shader
/// with the pass names UniversalForward or SRPDefaultUnlit.
/// </summary>
public partial class DrawObjectsPass : ScriptableRenderPass
{
FilteringSettings m_FilteringSettings;
RenderStateBlock m_RenderStateBlock;
List<ShaderTagId> m_ShaderTagIdList = new List<ShaderTagId>();
bool m_IsOpaque;
/// <summary>
/// Used to indicate whether transparent objects should receive shadows or not.
/// </summary>
public bool m_ShouldTransparentsReceiveShadows;
#if URP_SCREEN_SPACE_REFLECTION
/// <summary>
/// Used to indicate whether transparent objects should receive screen space reflections or not.
/// </summary>
public bool shouldTransparentsReceiveSSR { get; set; }
#endif
static readonly int s_DrawObjectPassDataPropID = Shader.PropertyToID("_DrawObjectPassData");
/// <summary>
/// Creates a new <c>DrawObjectsPass</c> instance.
/// </summary>
/// <param name="profilerTag">The profiler tag used with the pass.</param>
/// <param name="shaderTagIds"></param>
/// <param name="opaque">Marks whether the objects are opaque or transparent.</param>
/// <param name="evt">The <c>RenderPassEvent</c> to use.</param>
/// <param name="renderQueueRange">The <c>RenderQueueRange</c> to use for creating filtering settings that control what objects get rendered.</param>
/// <param name="layerMask">The layer mask to use for creating filtering settings that control what objects get rendered.</param>
/// <param name="stencilState">The stencil settings to use with this poss.</param>
/// <param name="stencilReference">The stencil reference value to use with this pass.</param>
/// <seealso cref="ShaderTagId"/>
/// <seealso cref="RenderPassEvent"/>
/// <seealso cref="RenderQueueRange"/>
/// <seealso cref="LayerMask"/>
/// <seealso cref="StencilState"/>
public DrawObjectsPass(string profilerTag, ShaderTagId[] shaderTagIds, bool opaque, RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask, StencilState stencilState, int stencilReference)
{
Init(opaque, evt, renderQueueRange, layerMask, stencilState, stencilReference, shaderTagIds);
profilingSampler = new ProfilingSampler(profilerTag);
}
/// <summary>
/// Creates a new <c>DrawObjectsPass</c> instance.
/// </summary>
/// <param name="profilerTag">The profiler tag used with the pass.</param>
/// <param name="opaque">Marks whether the objects are opaque or transparent.</param>
/// <param name="evt">The <c>RenderPassEvent</c> to use.</param>
/// <param name="renderQueueRange">The <c>RenderQueueRange</c> to use for creating filtering settings that control what objects get rendered.</param>
/// <param name="layerMask">The layer mask to use for creating filtering settings that control what objects get rendered.</param>
/// <param name="stencilState">The stencil settings to use with this poss.</param>
/// <param name="stencilReference">The stencil reference value to use with this pass.</param>
/// <seealso cref="RenderPassEvent"/>
/// <seealso cref="RenderQueueRange"/>
/// <seealso cref="LayerMask"/>
/// <seealso cref="StencilState"/>
public DrawObjectsPass(string profilerTag, bool opaque, RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask, StencilState stencilState, int stencilReference)
: this(profilerTag, null, opaque, evt, renderQueueRange, layerMask, stencilState, stencilReference)
{
}
internal DrawObjectsPass(ProfilingSampler sampler, bool opaque, RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask, StencilState stencilState, int stencilReference)
{
Init(opaque, evt, renderQueueRange, layerMask, stencilState, stencilReference);
profilingSampler = sampler;
}
internal void Init(bool opaque, RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask, StencilState stencilState, int stencilReference, ShaderTagId[] shaderTagIds = null)
{
if (shaderTagIds == null)
shaderTagIds = new ShaderTagId[] { new ShaderTagId("SRPDefaultUnlit"), new ShaderTagId("UniversalForward"), new ShaderTagId("UniversalForwardOnly") };
foreach (ShaderTagId sid in shaderTagIds)
m_ShaderTagIdList.Add(sid);
renderPassEvent = evt;
m_FilteringSettings = new FilteringSettings(renderQueueRange, layerMask);
m_RenderStateBlock = new RenderStateBlock(RenderStateMask.Nothing);
m_IsOpaque = opaque;
m_ShouldTransparentsReceiveShadows = false;
#if URP_SCREEN_SPACE_REFLECTION
shouldTransparentsReceiveSSR = false;
#endif
if (stencilState.enabled)
{
m_RenderStateBlock.stencilReference = stencilReference;
m_RenderStateBlock.mask = RenderStateMask.Stencil;
m_RenderStateBlock.stencilState = stencilState;
}
}
internal static void ExecutePass(RasterCommandBuffer cmd, PassData data, RendererList rendererList, RendererList objectsWithErrorRendererList, bool yFlip)
{
// Global render pass data containing various settings.
// x,y,z are currently unused
// w is used for knowing whether the object is opaque(1) or alpha blended(0)
Vector4 drawObjectPassData = new Vector4(0.0f, 0.0f, 0.0f, (data.isOpaque) ? 1.0f : 0.0f);
cmd.SetGlobalVector(s_DrawObjectPassDataPropID, drawObjectPassData);
if (data.cameraData.xr.enabled && data.isActiveTargetBackBuffer)
{
cmd.SetViewport(data.cameraData.xr.GetViewport());
}
bool useScreenSpaceIrradiance = data.screenSpaceIrradianceHdl.IsValid();
cmd.SetKeyword(ShaderGlobalKeywords.ScreenSpaceIrradiance, useScreenSpaceIrradiance);
if (useScreenSpaceIrradiance)
{
cmd.SetGlobalTexture(ShaderPropertyId.screenSpaceIrradiance, data.screenSpaceIrradianceHdl);
}
// scaleBias.x = flipSign
// scaleBias.y = scale
// scaleBias.z = bias
// scaleBias.w = unused
float flipSign = yFlip ? -1.0f : 1.0f;
Vector4 scaleBias = (flipSign < 0.0f)
? new Vector4(flipSign, 1.0f, -1.0f, 1.0f)
: new Vector4(flipSign, 0.0f, 1.0f, 1.0f);
cmd.SetGlobalVector(ShaderPropertyId.scaleBiasRt, scaleBias);
// Set a value that can be used by shaders to identify when AlphaToMask functionality may be active
// The material shader alpha clipping logic requires this value in order to function correctly in all cases.
float alphaToMaskAvailable = ((data.cameraData.cameraTargetDescriptor.msaaSamples > 1) && data.isOpaque) ? 1.0f : 0.0f;
cmd.SetGlobalFloat(ShaderPropertyId.alphaToMaskAvailable, alphaToMaskAvailable);
var activeDebugHandler = GetActiveDebugHandler(data.cameraData);
if (activeDebugHandler != null)
{
data.debugRendererLists.DrawWithRendererList(cmd);
}
else
{
cmd.DrawRendererList(rendererList);
// Render objects that did not match any shader pass with error shader
RenderingUtils.DrawRendererListObjectsWithError(cmd, ref objectsWithErrorRendererList);
}
}
/// <summary>
/// Shared pass data
/// </summary>
internal class PassData
{
internal TextureHandle albedoHdl;
internal TextureHandle depthHdl;
internal TextureHandle screenSpaceIrradianceHdl;
#if URP_SCREEN_SPACE_REFLECTION
internal TextureHandle screenSpaceReflectionHdl;
#endif
internal UniversalCameraData cameraData;
internal bool isOpaque;
internal bool shouldTransparentsReceiveShadows;
#if URP_SCREEN_SPACE_REFLECTION
internal bool shouldTransparentsReceiveSSR;
#endif
internal uint batchLayerMask;
internal bool isActiveTargetBackBuffer;
internal RendererListHandle rendererListHdl;
internal RendererListHandle objectsWithErrorRendererListHdl;
internal DebugRendererLists debugRendererLists;
// Required for code sharing purpose between RG and non-RG.
internal RendererList rendererList;
internal RendererList objectsWithErrorRendererList;
}
/// <summary>
/// Initialize the shared pass data.
/// </summary>
/// <param name="passData"></param>
internal void InitPassData(UniversalCameraData cameraData, ref PassData passData, uint batchLayerMask, bool isActiveTargetBackBuffer = false)
{
passData.cameraData = cameraData;
passData.isOpaque = m_IsOpaque;
passData.shouldTransparentsReceiveShadows = m_ShouldTransparentsReceiveShadows;
#if URP_SCREEN_SPACE_REFLECTION
passData.shouldTransparentsReceiveSSR = shouldTransparentsReceiveSSR;
#endif
passData.batchLayerMask = batchLayerMask;
passData.isActiveTargetBackBuffer = isActiveTargetBackBuffer;
}
internal void InitRendererLists(UniversalRenderingData renderingData, UniversalCameraData cameraData, UniversalLightData lightData, ref PassData passData, RenderGraph renderGraph, bool zWriteOff)
{
ref Camera camera = ref cameraData.camera;
var sortFlags = (m_IsOpaque) ? cameraData.defaultOpaqueSortFlags : SortingCriteria.CommonTransparent;
if (cameraData.renderer.useDepthPriming && m_IsOpaque && (cameraData.renderType == CameraRenderType.Base || cameraData.clearDepth))
sortFlags = SortingCriteria.SortingLayer | SortingCriteria.RenderQueue | SortingCriteria.OptimizeStateChanges | SortingCriteria.CanvasOrder;
var filterSettings = m_FilteringSettings;
filterSettings.batchLayerMask = passData.batchLayerMask;
#if UNITY_EDITOR
// When rendering the preview camera, we want the layer mask to be forced to Everything
if (cameraData.isPreviewCamera)
{
filterSettings.layerMask = -1;
}
#endif
DrawingSettings drawSettings = RenderingUtils.CreateDrawingSettings(m_ShaderTagIdList, renderingData, cameraData, lightData, sortFlags);
if (zWriteOff)
{
m_RenderStateBlock.depthState = new DepthState(false, CompareFunction.Equal);
m_RenderStateBlock.mask |= RenderStateMask.Depth;
}
else
{
m_RenderStateBlock.depthState = DepthState.defaultValue;
m_RenderStateBlock.mask &= ~RenderStateMask.Depth;
}
var activeDebugHandler = GetActiveDebugHandler(cameraData);
if (activeDebugHandler != null)
{
passData.debugRendererLists = activeDebugHandler.CreateRendererListsWithDebugRenderState(renderGraph, ref renderingData.cullResults, ref drawSettings, ref filterSettings, ref m_RenderStateBlock);
}
else
{
RenderingUtils.CreateRendererListWithRenderStateBlock(renderGraph, ref renderingData.cullResults, drawSettings, filterSettings, m_RenderStateBlock, ref passData.rendererListHdl);
RenderingUtils.CreateRendererListObjectsWithError(renderGraph, ref renderingData.cullResults, camera, filterSettings, sortFlags, ref passData.objectsWithErrorRendererListHdl);
}
}
internal static bool CanDisableZWrite(UniversalCameraData cameraData, bool isOpaque)
{
return cameraData.renderer.useDepthPriming && isOpaque && (cameraData.renderType == CameraRenderType.Base || cameraData.clearDepth);
}
internal void Render(RenderGraph renderGraph, ContextContainer frameData, in TextureHandle colorTarget, in TextureHandle depthTarget, in TextureHandle mainShadowsTexture, in TextureHandle additionalShadowsTexture, uint batchLayerMask = uint.MaxValue, bool isMainOpaquePass = false)
{
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
UniversalRenderingData renderingData = frameData.Get<UniversalRenderingData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
UniversalLightData lightData = frameData.Get<UniversalLightData>();
bool disableZWrite = CanDisableZWrite(cameraData, m_IsOpaque);
using (var builder = renderGraph.AddRasterRenderPass<PassData>(passName, out var passData, profilingSampler))
{
builder.UseAllGlobalTextures(true);
InitPassData(cameraData, ref passData, batchLayerMask, resourceData.isActiveTargetBackBuffer);
if (colorTarget.IsValid())
{
passData.albedoHdl = colorTarget;
builder.SetRenderAttachment(colorTarget, 0, AccessFlags.Write);
}
if (depthTarget.IsValid())
{
var depthAccessFlags = (disableZWrite) ? AccessFlags.Read : AccessFlags.ReadWrite;
passData.depthHdl = depthTarget;
builder.SetRenderAttachmentDepth(depthTarget, depthAccessFlags);
}
if (mainShadowsTexture.IsValid())
builder.UseTexture(mainShadowsTexture, AccessFlags.Read);
if (additionalShadowsTexture.IsValid())
builder.UseTexture(additionalShadowsTexture, AccessFlags.Read);
TextureHandle ssaoTexture = resourceData.ssaoTexture;
if (ssaoTexture.IsValid())
builder.UseTexture(ssaoTexture, AccessFlags.Read);
TextureHandle irradianceTexture = resourceData.irradianceTexture;
if (irradianceTexture.IsValid())
{
passData.screenSpaceIrradianceHdl = irradianceTexture;
builder.UseTexture(irradianceTexture, AccessFlags.Read);
}
#if URP_SCREEN_SPACE_REFLECTION
TextureHandle ssrTexture = resourceData.ssrTexture;
if (ssrTexture.IsValid())
{
passData.screenSpaceReflectionHdl = ssrTexture;
builder.UseTexture(ssrTexture, AccessFlags.Read);
}
else
{
passData.screenSpaceReflectionHdl = TextureHandle.nullHandle;
}
#endif
RenderGraphUtils.UseDBufferIfValid(builder, resourceData);
InitRendererLists(renderingData, cameraData, lightData, ref passData, renderGraph, disableZWrite);
var activeDebugHandler = GetActiveDebugHandler(cameraData);
if (activeDebugHandler != null)
{
passData.debugRendererLists.PrepareRendererListForRasterPass(builder);
}
else
{
builder.UseRendererList(passData.rendererListHdl);
builder.UseRendererList(passData.objectsWithErrorRendererListHdl);
}
builder.AllowGlobalStateModification(true);
if (cameraData.xr.enabled)
{
bool passSupportsFoveation = cameraData.xrUniversal.canFoveateIntermediatePasses || resourceData.isActiveTargetBackBuffer;
builder.EnableFoveatedRasterization(cameraData.xr.supportsFoveatedRendering && passSupportsFoveation);
// Multiview render regions are incompatible with the inner (foveal) pass in Quad View
if (!cameraData.xr.isQuadViewInnerPass)
{
builder.SetExtendedFeatureFlags(ExtendedFeatureFlags.MultiviewRenderRegionsCompatible);
}
#if ENABLE_VR && ENABLE_XR_MODULE && PLATFORM_ANDROID
if (isMainOpaquePass)
{
builder.SetExtendedFeatureFlags(ExtendedFeatureFlags.TileProperties);
}
#endif
}
builder.SetRenderFunc(static (PassData data, RasterGraphContext context) =>
{
// Currently we only need to call this additional pass when the user
// doesn't want transparent objects to receive shadows
if (!data.isOpaque && !data.shouldTransparentsReceiveShadows)
TransparentSettingsPass.ExecutePass(context.cmd);
bool yFlip = RenderingUtils.IsHandleYFlipped(context, in (data.albedoHdl.IsValid() ? ref data.albedoHdl : ref data.depthHdl));
bool useScreenSpaceIrradiance = data.screenSpaceIrradianceHdl.IsValid();
context.cmd.SetKeyword(ShaderGlobalKeywords.ScreenSpaceIrradiance, useScreenSpaceIrradiance);
if (useScreenSpaceIrradiance)
{
context.cmd.SetGlobalTexture(ShaderPropertyId.screenSpaceIrradiance, data.screenSpaceIrradianceHdl);
}
#if URP_SCREEN_SPACE_REFLECTION
bool useSSR = data.screenSpaceReflectionHdl.IsValid() && (data.isOpaque || data.shouldTransparentsReceiveSSR);
context.cmd.SetKeyword(ShaderGlobalKeywords.ScreenSpaceReflection, useSSR);
if (useSSR)
{
context.cmd.SetGlobalTexture(ShaderPropertyId.screenSpaceReflection, data.screenSpaceReflectionHdl);
}
#endif
ExecutePass(context.cmd, data, data.rendererListHdl, data.objectsWithErrorRendererListHdl, yFlip);
});
}
}
}
/// <summary>
/// Extension of DrawObjectPass that also output Rendering Layers Texture as second render target.
/// </summary>
internal class DrawObjectsWithRenderingLayersPass : DrawObjectsPass
{
/// <summary>
/// Creates a new <c>DrawObjectsWithRenderingLayersPass</c> instance.
/// </summary>
/// <param name="sampler">The profiling sampler used with the pass.</param>
/// <param name="opaque">Marks whether the objects are opaque or transparent.</param>
/// <param name="evt">The <c>RenderPassEvent</c> to use.</param>
/// <param name="renderQueueRange">The <c>RenderQueueRange</c> to use for creating filtering settings that control what objects get rendered.</param>
/// <param name="layerMask">The layer mask to use for creating filtering settings that control what objects get rendered.</param>
/// <param name="stencilState">The stencil settings to use with this poss.</param>
/// <param name="stencilReference">The stencil reference value to use with this pass.</param>
internal DrawObjectsWithRenderingLayersPass(ProfilingSampler sampler, bool opaque, RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask, StencilState stencilState,
int stencilReference) :
base(sampler, opaque, evt, renderQueueRange, layerMask, stencilState, stencilReference)
{
}
private class RenderingLayersPassData
{
internal PassData basePassData;
internal RenderingLayerUtils.MaskSize maskSize;
public RenderingLayersPassData()
{
basePassData = new PassData();
}
}
internal void Render(RenderGraph renderGraph, ContextContainer frameData, in TextureHandle colorTarget, in TextureHandle renderingLayersTexture, in TextureHandle depthTarget,
in TextureHandle mainShadowsTexture, in TextureHandle additionalShadowsTexture, RenderingLayerUtils.MaskSize maskSize, uint batchLayerMask = uint.MaxValue)
{
using (var builder = renderGraph.AddRasterRenderPass<RenderingLayersPassData>(passName, out var passData, profilingSampler))
{
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
UniversalRenderingData renderingData = frameData.Get<UniversalRenderingData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
UniversalLightData lightData = frameData.Get<UniversalLightData>();
InitPassData(cameraData, ref passData.basePassData, batchLayerMask);
passData.maskSize = maskSize;
passData.basePassData.albedoHdl = colorTarget;
builder.SetRenderAttachment(colorTarget, 0, AccessFlags.Write);
builder.SetRenderAttachment(renderingLayersTexture, 1, AccessFlags.Write);
bool disableZWrite = CanDisableZWrite(cameraData, passData.basePassData.isOpaque);
var depthAccessFlags = (disableZWrite) ? AccessFlags.Read : AccessFlags.ReadWrite;
passData.basePassData.depthHdl = depthTarget;
builder.SetRenderAttachmentDepth(depthTarget, depthAccessFlags);
if (mainShadowsTexture.IsValid())
builder.UseTexture(mainShadowsTexture, AccessFlags.Read);
if (additionalShadowsTexture.IsValid())
builder.UseTexture(additionalShadowsTexture, AccessFlags.Read);
UniversalRenderer renderer = cameraData.renderer as UniversalRenderer;
if (renderer != null)
{
TextureHandle ssaoTexture = resourceData.ssaoTexture;
if (ssaoTexture.IsValid())
builder.UseTexture(ssaoTexture, AccessFlags.Read);
RenderGraphUtils.UseDBufferIfValid(builder, resourceData);
}
InitRendererLists(renderingData, cameraData, lightData, ref passData.basePassData, renderGraph, disableZWrite);
var activeDebugHandler = GetActiveDebugHandler(cameraData);
if (activeDebugHandler != null)
{
passData.basePassData.debugRendererLists.PrepareRendererListForRasterPass(builder);
}
else
{
builder.UseRendererList(passData.basePassData.rendererListHdl);
builder.UseRendererList(passData.basePassData.objectsWithErrorRendererListHdl);
}
// Required here because of RenderingLayerUtils.SetupProperties
builder.AllowGlobalStateModification(true);
if (cameraData.xr.enabled)
{
bool passSupportsFoveation = cameraData.xrUniversal.canFoveateIntermediatePasses || resourceData.isActiveTargetBackBuffer;
builder.EnableFoveatedRasterization(cameraData.xr.supportsFoveatedRendering && passSupportsFoveation);
// Multiview render regions are incompatible with the inner (foveal) pass in Quad View
if (!cameraData.xr.isQuadViewInnerPass)
{
builder.SetExtendedFeatureFlags(ExtendedFeatureFlags.MultiviewRenderRegionsCompatible);
}
}
builder.SetRenderFunc(static (RenderingLayersPassData data, RasterGraphContext context) =>
{
// Enable Rendering Layers
context.cmd.SetKeyword(ShaderGlobalKeywords.WriteRenderingLayers, true);
RenderingLayerUtils.SetupProperties(context.cmd, data.maskSize);
// Currently we only need to call this additional pass when the user
// doesn't want transparent objects to receive shadows
if (!data.basePassData.isOpaque && !data.basePassData.shouldTransparentsReceiveShadows)
TransparentSettingsPass.ExecutePass(context.cmd);
bool yFlip = RenderingUtils.IsHandleYFlipped(context, in (data.basePassData.albedoHdl.IsValid() ? ref data.basePassData.albedoHdl : ref data.basePassData.depthHdl));
// Execute
ExecutePass(context.cmd, data.basePassData, data.basePassData.rendererListHdl, data.basePassData.objectsWithErrorRendererListHdl, yFlip);
// Clean up
context.cmd.SetKeyword(ShaderGlobalKeywords.WriteRenderingLayers, false);
});
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d2138a73081d8c34d901cf7a321f1099
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,401 @@
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
#if ENABLE_UIELEMENTS_MODULE
using UnityEngine.UIElements;
#endif
namespace UnityEngine.Rendering.Universal
{
/// <summary>
/// Draw screen space overlay UI into the given color and depth target
/// </summary>
internal class DrawScreenSpaceUIPass : ScriptableRenderPass
{
// Renderer-owned materials (created and destroyed by the renderer, not this pass).
readonly Material m_UIBackdropFilterCompositeMaterial;
// Blits each camera's post-FX color into the composite buffer.
readonly Material m_SeedBlitMaterial;
const int k_CompositePassIndex = 0;
bool m_RequiresComposition;
// Backdrop-filter overlay buffers, owned by this pass; null on passes that never composite (e.g. 2D).
RTHandle m_OverlayCompositeBuffer; // overlay UI renders here, then blits to the backbuffer
RTHandle m_OverlayCompositeBufferBefore; // pre-UI snapshot the composite diffs against
RTHandle m_OverlayDepthStencilBuffer; // depth/stencil for the overlay UI (stencil clipping)
// Per-frame render-graph handles, re-imported each frame (or null when the composite path is skipped).
TextureHandle m_OverlayCompositeBufferHandle = TextureHandle.nullHandle;
TextureHandle m_OverlayCompositeBufferBeforeHandle = TextureHandle.nullHandle;
TextureHandle m_OverlayDepthStencilBufferHandle = TextureHandle.nullHandle;
/// <summary>
/// Creates a new <c>DrawScreenSpaceUIPass</c> instance.
/// </summary>
/// <param name="evt">The <c>RenderPassEvent</c> to use.</param>
/// <param name="uiBackdropFilterCompositeMaterial">Material for the diff composite blit, created and owned by the renderer. Pass null when this pass instance never runs the unsafe overlay path.</param>
/// <param name="seedBlitMaterial">URP blit material for the per-camera scene-to-buffer seed step, created and owned by the renderer. Pass null when this pass instance never runs the unsafe overlay path.</param>
/// <seealso cref="RenderPassEvent"/>
public DrawScreenSpaceUIPass(RenderPassEvent evt, Material uiBackdropFilterCompositeMaterial = null, Material seedBlitMaterial = null)
{
profilingSampler = URPProfilingSamplers.DrawScreenSpaceUI;
renderPassEvent = evt;
m_UIBackdropFilterCompositeMaterial = uiBackdropFilterCompositeMaterial;
m_SeedBlitMaterial = seedBlitMaterial;
}
// Computes this frame's composite need; the renderer calls it each frame like other passes' Setup().
internal void Setup(UniversalCameraData cameraData)
{
#if ENABLE_UIELEMENTS_MODULE
m_RequiresComposition = cameraData.rendersOverlayUI
&& UIElementsRuntimeUtility.AnyOverlayPanelHasBackdropFilter()
&& !cameraData.stackLastCameraOutputToHDR;
#else
m_RequiresComposition = false;
#endif
}
internal bool RequiresComposition() => m_RequiresComposition;
/// <summary>
/// Get a descriptor for the required color texture for offscreen UI pass.
/// </summary>
internal static void ConfigureOffscreenUITextureDesc(ref TextureDesc textureDesc)
{
textureDesc.format = GraphicsFormat.R8G8B8A8_SRGB;
textureDesc.depthBufferBits = 0;
textureDesc.width = Screen.width;
textureDesc.height = Screen.height;
}
/// <summary>
/// Get a descriptor for the required depth texture for this pass.
/// </summary>
/// <param name="descriptor">Camera target descriptor.</param>
/// <param name="depthStencilFormat">Depth stencil format required.</param>
/// <param name="screenWidth">The full screen width.</param>
/// <param name="screenHeight">The full screen height.</param>
/// <seealso cref="RenderTextureDescriptor"/>
private static void ConfigureDepthDescriptor(ref RenderTextureDescriptor descriptor, GraphicsFormat depthStencilFormat, int screenWidth, int screenHeight)
{
descriptor.graphicsFormat = GraphicsFormat.None;
descriptor.depthStencilFormat = depthStencilFormat;
descriptor.width = screenWidth;
descriptor.height = screenHeight;
}
private static void ExecutePass(RasterCommandBuffer commandBuffer, PassData passData, RendererList rendererList)
{
commandBuffer.DrawRendererList(rendererList);
}
// Specific to RG cases which have to go through Unsafe commands
private static void ExecutePass(UnsafeCommandBuffer commandBuffer, UnsafePassData passData, RendererList rendererList)
{
commandBuffer.DrawRendererList(rendererList);
}
public void Dispose()
{
// Materials are owned by the renderer; this pass only releases the buffers it allocates.
m_OverlayCompositeBuffer?.Release();
m_OverlayCompositeBuffer = null;
m_OverlayCompositeBufferBefore?.Release();
m_OverlayCompositeBufferBefore = null;
m_OverlayDepthStencilBuffer?.Release();
m_OverlayDepthStencilBuffer = null;
}
//RenderGraph path
private class PassData
{
internal RendererListHandle rendererList;
}
// Specific to RG cases which have to go through Unsafe commands
private class UnsafePassData
{
internal RendererListHandle rendererList;
internal TextureHandle colorTarget;
// Bound with color so stencil clipping works.
internal TextureHandle depthTarget;
// Pre-UI snapshot the composite diffs against.
internal TextureHandle bufferBefore;
}
private class ViewportBlitPassData
{
internal TextureHandle source;
internal TextureHandle destination;
internal Rect viewport;
internal Material material;
internal int passIndex;
}
// Runs before the final blit — afterwards cameraColor is the backbuffer and can't be read.
internal void BlitCameraColorToOverlayCompositeBuffer(RenderGraph renderGraph, UniversalResourceData resourceData, Rect viewport, GraphicsFormat cameraDepthAttachmentFormat)
{
ImportOverlayCompositeTextures(renderGraph, resourceData, cameraDepthAttachmentFormat);
AddViewportBlitPass(renderGraph, resourceData.cameraColor, m_OverlayCompositeBufferHandle,
viewport, sampler: null, passName: "Overlay Composite Viewport Blit");
}
void ImportOverlayCompositeTextures(RenderGraph renderGraph, UniversalResourceData resourceData, GraphicsFormat cameraDepthAttachmentFormat)
{
var bbInfo = renderGraph.GetRenderTargetInfo(resourceData.backBufferColor);
var compositeDesc = new RenderTextureDescriptor(bbInfo.width, bbInfo.height, bbInfo.format, depthBufferBits: 0)
{
msaaSamples = 1,
useMipMap = false,
autoGenerateMips = false,
};
RenderingUtils.ReAllocateHandleIfNeeded(ref m_OverlayCompositeBuffer, compositeDesc,
FilterMode.Bilinear, TextureWrapMode.Clamp, name: "_OverlayCompositeBuffer");
RenderingUtils.ReAllocateHandleIfNeeded(ref m_OverlayCompositeBufferBefore, compositeDesc,
FilterMode.Bilinear, TextureWrapMode.Clamp, name: "_OverlayCompositeBufferBefore");
// Dedicated buffer: the camera depth can be a placeholder or wrong size here.
var depthStencilDesc = new RenderTextureDescriptor(bbInfo.width, bbInfo.height, GraphicsFormat.None, cameraDepthAttachmentFormat)
{
msaaSamples = 1,
useMipMap = false,
autoGenerateMips = false,
};
RenderingUtils.ReAllocateHandleIfNeeded(ref m_OverlayDepthStencilBuffer, depthStencilDesc,
FilterMode.Point, TextureWrapMode.Clamp, name: "_OverlayDepthStencilBuffer");
// Each base camera writes only its viewport; earlier cameras' regions must survive the import.
var importParams = new ImportResourceParams
{
clearOnFirstUse = false,
discardOnLastUse = false,
};
m_OverlayCompositeBufferHandle = renderGraph.ImportTexture(m_OverlayCompositeBuffer, importParams);
m_OverlayCompositeBufferBeforeHandle = renderGraph.ImportTexture(m_OverlayCompositeBufferBefore, importParams);
m_OverlayDepthStencilBufferHandle = renderGraph.ImportTexture(m_OverlayDepthStencilBuffer, importParams);
}
// No Y-flip here: source and destination share the same orientation (unlike a final blit to screen).
internal void AddViewportBlitPass(RenderGraph renderGraph, TextureHandle source, TextureHandle destination, Rect viewport, ProfilingSampler sampler, string passName)
{
if (m_SeedBlitMaterial == null)
return;
using (var builder = renderGraph.AddRasterRenderPass<ViewportBlitPassData>(passName, out var passData, sampler))
{
passData.source = source;
passData.destination = destination;
passData.viewport = viewport;
passData.material = m_SeedBlitMaterial;
passData.passIndex = 0;
builder.UseTexture(source, AccessFlags.Read);
builder.SetRenderAttachment(destination, 0, AccessFlags.Write);
builder.SetRenderFunc(static (ViewportBlitPassData data, RasterGraphContext context) =>
{
context.cmd.SetViewport(data.viewport);
RTHandle srcRTHandle = data.source;
Vector2 scale = (srcRTHandle != null && srcRTHandle.useScaling)
? new Vector2(srcRTHandle.rtHandleProperties.rtHandleScale.x, srcRTHandle.rtHandleProperties.rtHandleScale.y)
: Vector2.one;
Vector4 scaleBias = new Vector4(scale.x, scale.y, 0f, 0f);
Blitter.BlitTexture(context.cmd, data.source, scaleBias, data.material, data.passIndex);
});
}
}
private class CompositePassData
{
internal TextureHandle source;
internal TextureHandle sourceBefore;
internal TextureHandle destination;
internal Material material;
internal int passIndex;
}
static readonly int s_BlitTextureBeforeId = Shader.PropertyToID("_BlitTexture_Before");
// Composites the OverlayCompositeBuffer onto the backbuffer: pixels matching the pre-UI snapshot
// (`sourceBefore`) are discarded (keeping FinalPost output); changed pixels write the post-UI RGB.
internal void AddUIBackdropFilterCompositePass(RenderGraph renderGraph, TextureHandle source, TextureHandle sourceBefore, TextureHandle destination, ProfilingSampler sampler, string passName)
{
if (m_UIBackdropFilterCompositeMaterial == null)
return;
using (var builder = renderGraph.AddRasterRenderPass<CompositePassData>(passName, out var passData, sampler))
{
passData.source = source;
passData.sourceBefore = sourceBefore;
passData.destination = destination;
passData.material = m_UIBackdropFilterCompositeMaterial;
passData.passIndex = k_CompositePassIndex;
builder.UseTexture(source, AccessFlags.Read);
builder.UseTexture(sourceBefore, AccessFlags.Read);
builder.SetRenderAttachment(destination, 0, AccessFlags.Write);
builder.SetRenderFunc(static (CompositePassData data, RasterGraphContext context) =>
{
// Bind the pre-UI snapshot; Blitter sets _BlitTexture from data.source.
data.material.SetTexture(s_BlitTextureBeforeId, data.sourceBefore);
Vector4 scaleBias = RenderingUtils.GetFinalBlitScaleBias(context, in data.source, in data.destination);
Blitter.BlitTexture(context.cmd, data.source, scaleBias, data.material, data.passIndex);
});
}
}
internal void RenderOffscreen(RenderGraph renderGraph, ContextContainer frameData, GraphicsFormat depthStencilFormat, TextureHandle overlayUITexture)
{
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
RenderTextureDescriptor depthDescriptor = cameraData.cameraTargetDescriptor;
ConfigureDepthDescriptor(ref depthDescriptor, depthStencilFormat, Screen.width, Screen.height);
TextureHandle depthBuffer = UniversalRenderer.CreateRenderGraphTexture(renderGraph, depthDescriptor, "_OverlayUITexture_Depth", false);
// Render uGUI and UIToolkit overlays
using (var builder = renderGraph.AddRasterRenderPass<PassData>("Draw Screen Space UIToolkit/uGUI - Offscreen", out var passData, profilingSampler))
{
// UIToolkit/uGUI pass accept custom shaders, we need to make sure we use all global textures
builder.UseAllGlobalTextures(true);
builder.SetRenderAttachment(overlayUITexture, 0);
passData.rendererList = renderGraph.CreateUIOverlayRendererList(cameraData.camera, UISubset.UIToolkit_UGUI);
builder.UseRendererList(passData.rendererList);
builder.SetRenderAttachmentDepth(depthBuffer, AccessFlags.ReadWrite);
if (overlayUITexture.IsValid())
builder.SetGlobalTextureAfterPass(overlayUITexture, ShaderPropertyId.overlayUITexture);
builder.SetRenderFunc(static (PassData data, RasterGraphContext context) =>
{
context.cmd.ClearRenderTarget(true, true, Color.clear);
ExecutePass(context.cmd, data, data.rendererList);
});
}
// Render IMGUI overlay and software cursor in a UnsafePass
// Doing so allow us to safely cover cases when graphics commands called through onGUI() in user scripts are not supported by RenderPass API
// Besides, Vulkan backend doesn't support SetSRGWrite() in RenderPass API and we have some of them at IMGUI levels
// Note, these specific UI calls doesn't need depth buffer unlike UIToolkit/uGUI
using (var builder = renderGraph.AddUnsafePass<UnsafePassData>("Draw Screen Space IMGUI/SoftwareCursor - Offscreen", out var passData, profilingSampler))
{
passData.colorTarget = overlayUITexture;
builder.UseTexture(overlayUITexture, AccessFlags.Write);
passData.rendererList = renderGraph.CreateUIOverlayRendererList(cameraData.camera, UISubset.LowLevel);
builder.UseRendererList(passData.rendererList);
builder.SetRenderFunc(static (UnsafePassData data, UnsafeGraphContext context) =>
{
context.cmd.SetRenderTarget(data.colorTarget);
ExecutePass(context.cmd, data, data.rendererList);
});
}
}
internal void RenderOverlay(RenderGraph renderGraph, ContextContainer frameData, in TextureHandle colorBuffer, in TextureHandle depthBuffer)
{
RenderOverlayUIToolkitAndUGUI(renderGraph, frameData, in colorBuffer, in depthBuffer);
RenderOverlayIMGUI(renderGraph, frameData, in colorBuffer, in depthBuffer);
}
// Default raster overlay path: used when no panel needs backdrop-filter (or the composite path can't
// run). A stray backdrop-filter element here silently no-ops, which is intended.
internal void RenderOverlayUIToolkitAndUGUI(RenderGraph renderGraph, ContextContainer frameData, in TextureHandle colorBuffer, in TextureHandle depthBuffer)
{
var cameraData = frameData.Get<UniversalCameraData>();
using (var builder = renderGraph.AddRasterRenderPass<PassData>("Draw UIToolkit/uGUI Overlay", out var passData, profilingSampler))
{
// UIToolkit/uGUI pass accept custom shaders, we need to make sure we use all global textures
builder.UseAllGlobalTextures(true);
builder.SetRenderAttachment(colorBuffer, 0);
if (depthBuffer.IsValid())
builder.SetRenderAttachmentDepth(depthBuffer, AccessFlags.ReadWrite);
passData.rendererList = renderGraph.CreateUIOverlayRendererList(cameraData.camera, UISubset.UIToolkit_UGUI);
builder.UseRendererList(passData.rendererList);
builder.SetRenderFunc(static (PassData data, RasterGraphContext context) =>
{
ExecutePass(context.cmd, data, data.rendererList);
});
}
}
// Composite variant: used when an overlay panel has backdrop-filter. UIToolkit renders into
// m_OverlayCompositeBuffer (pre-filled with the scene); a final diff-composite writes it to the backbuffer.
internal void RenderOverlayUIToolkitAndUGUIComposite(RenderGraph renderGraph, ContextContainer frameData, in TextureHandle destinationColor)
{
var cameraData = frameData.Get<UniversalCameraData>();
// Pass 1 — render UIToolkit/uGUI into the composite buffer (backdrop-filter samples it as it fills).
using (var builder = renderGraph.AddUnsafePass<UnsafePassData>("Draw UIToolkit/uGUI Overlay (Backdrop Composite)", out var passData, profilingSampler))
{
// UIToolkit/uGUI pass accepts custom shaders; we need to make sure we use all global textures
builder.UseAllGlobalTextures(true);
passData.colorTarget = m_OverlayCompositeBufferHandle;
passData.depthTarget = m_OverlayDepthStencilBufferHandle;
passData.bufferBefore = m_OverlayCompositeBufferBeforeHandle;
builder.UseTexture(m_OverlayCompositeBufferHandle, AccessFlags.ReadWrite);
builder.UseTexture(m_OverlayDepthStencilBufferHandle, AccessFlags.ReadWrite);
if (m_OverlayCompositeBufferBeforeHandle.IsValid())
builder.UseTexture(m_OverlayCompositeBufferBeforeHandle, AccessFlags.Write);
passData.rendererList = renderGraph.CreateUIOverlayRendererList(cameraData.camera, UISubset.UIToolkit_UGUI);
builder.UseRendererList(passData.rendererList);
builder.SetRenderFunc(static (UnsafePassData data, UnsafeGraphContext context) =>
{
// Copy the pre-UI buffer with CopyTexture (an exact copy; a shader blit would shift
// values slightly and break the diff).
if (data.bufferBefore.IsValid())
{
RTHandle src = data.colorTarget;
RTHandle dst = data.bufferBefore;
if (src != null && dst != null)
context.cmd.CopyTexture(src.nameID, dst.nameID);
}
// Bind the composite buffer as active RT: UIToolkit's BackdropFilterHelper samples
// RenderTexture.active at submission, so it must be set before DrawRendererList.
context.cmd.SetRenderTarget(data.colorTarget, data.depthTarget);
// Clear depth/stencil only (keep the seeded color): a stale stencil would drop the UI.
context.cmd.ClearRenderTarget(RTClearFlags.DepthStencil, Color.clear, 1.0f, 0);
ExecutePass(context.cmd, data, data.rendererList);
});
}
// Pass 2 — diff-composite the buffer over the backbuffer (non-UI pixels keep their FinalPost output).
AddUIBackdropFilterCompositePass(renderGraph, m_OverlayCompositeBufferHandle, m_OverlayCompositeBufferBeforeHandle, destinationColor, profilingSampler, "Overlay Composite To Backbuffer");
}
internal void RenderOverlayIMGUI(RenderGraph renderGraph, ContextContainer frameData, in TextureHandle colorBuffer, in TextureHandle depthBuffer)
{
var cameraData = frameData.Get<UniversalCameraData>();
// Render IMGUI overlay and software cursor in a UnsafePass
// Doing so allow us to safely cover cases when graphics commands called through onGUI() in user scripts are not supported by RenderPass API
// Besides, Vulkan backend doesn't support SetSRGWrite() in RenderPass API and we have some of them at IMGUI levels
// Note, these specific UI calls doesn't need depth buffer unlike UIToolkit/uGUI
using (var builder = renderGraph.AddUnsafePass<UnsafePassData>("Draw IMGUI/SoftwareCursor Overlay", out var passData, profilingSampler))
{
passData.colorTarget = colorBuffer;
builder.UseTexture(colorBuffer, AccessFlags.Write);
passData.rendererList = renderGraph.CreateUIOverlayRendererList(cameraData.camera, UISubset.LowLevel);
builder.UseRendererList(passData.rendererList);
builder.SetRenderFunc(static (UnsafePassData data, UnsafeGraphContext context) =>
{
context.cmd.SetRenderTarget(data.colorTarget);
ExecutePass(context.cmd, data, data.rendererList);
});
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a834bac5135fab746bc234ec665972de
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,125 @@
using System;
using UnityEngine.Rendering.RenderGraphModule;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.Universal.Internal;
namespace UnityEngine.Rendering.Universal
{
/// <summary>
/// Draw the skybox into the given color buffer using the given depth buffer for depth testing.
///
/// This pass renders the standard Unity skybox.
/// </summary>
public partial class DrawSkyboxPass : ScriptableRenderPass
{
/// <summary>
/// Creates a new <c>DrawSkyboxPass</c> instance.
/// </summary>
/// <param name="evt">The <c>RenderPassEvent</c> to use.</param>
/// <seealso cref="RenderPassEvent"/>
public DrawSkyboxPass(RenderPassEvent evt)
{
profilingSampler = URPProfilingSamplers.DrawSkybox;
renderPassEvent = evt;
}
private RendererListHandle CreateSkyBoxRendererList(RenderGraph renderGraph, UniversalCameraData cameraData)
{
var skyRendererListHandle = new RendererListHandle();
#if ENABLE_VR && ENABLE_XR_MODULE
if (cameraData.xr.enabled)
{
// Setup Legacy XR buffer states
if (cameraData.xr.singlePassEnabled)
{
skyRendererListHandle = renderGraph.CreateSkyboxRendererList(cameraData.camera,
cameraData.GetProjectionMatrix(0), cameraData.GetViewMatrix(0),
cameraData.GetProjectionMatrix(1), cameraData.GetViewMatrix(1));
}
else
{
skyRendererListHandle = renderGraph.CreateSkyboxRendererList(cameraData.camera, cameraData.GetProjectionMatrix(0), cameraData.GetViewMatrix(0));
}
}
else
#endif
{
skyRendererListHandle = renderGraph.CreateSkyboxRendererList(cameraData.camera);
}
return skyRendererListHandle;
}
private static void ExecutePass(RasterCommandBuffer cmd, XRPass xr, RendererList rendererList)
{
#if ENABLE_VR && ENABLE_XR_MODULE
if (xr.enabled && xr.singlePassEnabled)
cmd.SetSinglePassStereo(SystemInfo.supportsMultiview ? SinglePassStereoMode.Multiview : SinglePassStereoMode.Instancing);
#endif
cmd.DrawRendererList(rendererList);
#if ENABLE_VR && ENABLE_XR_MODULE
if (xr.enabled && xr.singlePassEnabled)
cmd.SetSinglePassStereo(SinglePassStereoMode.None);
#endif
}
private class PassData
{
internal XRPass xr;
internal RendererListHandle skyRendererListHandle;
internal Material material;
}
private void InitPassData(ref PassData passData, in XRPass xr, in RendererListHandle handle)
{
passData.xr = xr;
passData.skyRendererListHandle = handle;
}
internal void Render(RenderGraph renderGraph, ContextContainer frameData, ScriptableRenderContext context, in TextureHandle colorTarget, in TextureHandle depthTarget, Material skyboxMaterial)
{
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
var activeDebugHandler = GetActiveDebugHandler(cameraData);
if (activeDebugHandler != null)
{
// TODO: The skybox needs to work the same as the other shaders, but until it does we'll not render it
// when certain debug modes are active (e.g. wireframe/overdraw modes)
if (activeDebugHandler.IsScreenClearNeeded)
{
return;
}
}
using (var builder = renderGraph.AddRasterRenderPass<PassData>(passName, out var passData, profilingSampler))
{
var skyRendererListHandle = CreateSkyBoxRendererList(renderGraph, cameraData);
InitPassData(ref passData, cameraData.xr, skyRendererListHandle);
passData.material = skyboxMaterial;
builder.UseRendererList(skyRendererListHandle);
builder.SetRenderAttachment(colorTarget, 0, AccessFlags.Write);
builder.SetRenderAttachmentDepth(depthTarget, AccessFlags.ReadWrite);
builder.AllowPassCulling(false);
if (cameraData.xr.enabled)
{
bool passSupportsFoveation = cameraData.xrUniversal.canFoveateIntermediatePasses || resourceData.isActiveTargetBackBuffer;
builder.EnableFoveatedRasterization(cameraData.xr.supportsFoveatedRendering && passSupportsFoveation);
// Multiview render regions are incompatible with the inner (foveal) pass in Quad View
if (!cameraData.xr.isQuadViewInnerPass)
{
builder.SetExtendedFeatureFlags(ExtendedFeatureFlags.MultiviewRenderRegionsCompatible);
}
}
builder.SetRenderFunc(static (PassData data, RasterGraphContext context) =>
{
ExecutePass(context.cmd, data.xr, data.skyRendererListHandle);
});
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ae3e4e9915f7b6347b65203987c4f8b0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,248 @@
using System;
using UnityEngine.Rendering.RenderGraphModule;
using UnityEngine.Experimental.Rendering;
namespace UnityEngine.Rendering.Universal.Internal
{
/// <summary>
/// Copy the given color target to the current camera target
///
/// You can use this pass to copy the result of rendering to
/// the camera target. The pass takes the screen viewport into
/// consideration.
/// </summary>
public partial class FinalBlitPass : ScriptableRenderPass
{
// Use specialed URP fragment shader pass for debug draw support and color space conversion/encoding support.
// See CoreBlit.shader and BlitHDROverlay.shader
static class BlitPassNames
{
public const string NearestSampler = "NearestDebugDraw";
public const string BilinearSampler = "BilinearDebugDraw";
}
enum BlitType
{
Core = 0, // Core blit
HDR = 1, // Blit with HDR encoding and overlay UI compositing
Count = 2
}
struct BlitMaterialData
{
public Material material;
public int nearestSamplerPass;
public int bilinearSamplerPass;
}
BlitMaterialData[] m_BlitMaterialData;
/// <summary>
/// Creates a new <c>FinalBlitPass</c> instance.
/// </summary>
/// <param name="evt">The <c>RenderPassEvent</c> to use.</param>
/// <param name="blitMaterial">The <c>Material</c> to use for copying the executing the final blit.</param>
/// <param name="blitHDRMaterial">The <c>Material</c> to use for copying the executing the final blit when HDR output is active.</param>
/// <seealso cref="RenderPassEvent"/>
public FinalBlitPass(RenderPassEvent evt, Material blitMaterial, Material blitHDRMaterial)
{
profilingSampler = URPProfilingSamplers.BlitFinalToBackBuffer;
renderPassEvent = evt;
// Find sampler passes by name
const int blitTypeCount = (int)BlitType.Count;
m_BlitMaterialData = new BlitMaterialData[blitTypeCount];
for (int i = 0; i < blitTypeCount; ++i)
{
m_BlitMaterialData[i].material = i == (int)BlitType.Core ? blitMaterial : blitHDRMaterial;
m_BlitMaterialData[i].nearestSamplerPass = m_BlitMaterialData[i].material?.FindPass(BlitPassNames.NearestSampler) ?? -1;
m_BlitMaterialData[i].bilinearSamplerPass = m_BlitMaterialData[i].material?.FindPass(BlitPassNames.BilinearSampler) ?? -1;
}
}
/// <summary>
/// Cleans up resources used by the pass.
/// </summary>
public void Dispose()
{
}
/// <summary>
/// Configure the pass
/// </summary>
/// <param name="baseDescriptor"></param>
/// <param name="colorHandle"></param>
[Obsolete("Use RTHandles for colorHandle. #from(2022.1) #breakingFrom(2023.1)", true)]
public void Setup(RenderTextureDescriptor baseDescriptor, RenderTargetHandle colorHandle)
{
throw new NotSupportedException("Setup with RenderTargetHandle has been deprecated. Use it with RTHandles instead.");
}
/// <summary>
/// Configure the pass
/// </summary>
/// <param name="baseDescriptor"></param>
/// <param name="colorHandle"></param>
public void Setup(RenderTextureDescriptor baseDescriptor, RTHandle colorHandle)
{
}
static void SetupHDROutput(ColorGamut hdrDisplayColorGamut, Material material, HDROutputUtils.Operation hdrOperation, Vector4 hdrOutputParameters, bool rendersOverlayUI)
{
material.SetVector(ShaderPropertyId.hdrOutputLuminanceParams, hdrOutputParameters);
HDROutputUtils.ConfigureHDROutput(material, hdrDisplayColorGamut, hdrOperation);
CoreUtils.SetKeyword(material, ShaderKeywordStrings.HDROverlay, rendersOverlayUI);
}
private static void ExecutePass(RasterCommandBuffer cmd, PassData data, RTHandle source, RTHandle destination, UniversalCameraData cameraData, Vector4 scaleBias)
{
bool isRenderToBackBufferTarget = !cameraData.isSceneViewCamera;
#if ENABLE_VR && ENABLE_XR_MODULE
if (cameraData.xr.enabled)
isRenderToBackBufferTarget = new RenderTargetIdentifier(destination.nameID, 0, CubemapFace.Unknown, -1) == new RenderTargetIdentifier(cameraData.xr.renderTarget, 0, CubemapFace.Unknown, -1);
#endif
var pixelRect = data.useFullScreenViewport ? new Rect(0f, 0f, Screen.width, Screen.height) : cameraData.pixelRect;
RenderingUtils.SetupOffscreenUIViewportParams(data.blitMaterialData.material, ref pixelRect, isRenderToBackBufferTarget);
if (isRenderToBackBufferTarget)
cmd.SetViewport(pixelRect);
// turn off any global wireframe & "scene view wireframe shader hijack" settings for doing blits:
// we never want them to show up as wireframe
cmd.SetWireframe(false);
CoreUtils.SetKeyword(data.blitMaterialData.material, ShaderKeywordStrings._ENABLE_ALPHA_OUTPUT, data.enableAlphaOutput);
int shaderPassIndex = source.rt?.filterMode == FilterMode.Bilinear ? data.blitMaterialData.bilinearSamplerPass : data.blitMaterialData.nearestSamplerPass;
Blitter.BlitTexture(cmd, source, scaleBias, data.blitMaterialData.material, shaderPassIndex);
}
private class PassData
{
internal TextureHandle source;
internal TextureHandle destination;
internal int sourceID;
internal Vector4 hdrOutputLuminanceParams;
internal bool requireSrgbConversion;
internal bool enableAlphaOutput;
internal BlitMaterialData blitMaterialData;
internal UniversalCameraData cameraData;
internal bool useFullScreenViewport;
}
/// <summary>
/// Initialize the shared pass data.
/// </summary>
/// <param name="passData"></param>
private void InitPassData(UniversalCameraData cameraData, ref PassData passData, BlitType blitType, bool enableAlphaOutput, bool useFullScreenViewport)
{
passData.cameraData = cameraData;
passData.requireSrgbConversion = cameraData.requireSrgbConversion;
passData.enableAlphaOutput = enableAlphaOutput;
passData.useFullScreenViewport = useFullScreenViewport;
passData.blitMaterialData = m_BlitMaterialData[(int)blitType];
}
// Shared by both FinalBlitPass and OffscreenUICoverPrepass.
internal void Render(RenderGraph renderGraph, UniversalCameraData cameraData, UniversalResourceData resourceData, TextureHandle sourceTexture, bool useFullScreenViewport = false)
{
var destinationTexture = resourceData.backBufferColor; //By definition this pass blits to the backbuffer
var overlayUITexture = resourceData.overlayUITexture;
using (var builder = renderGraph.AddRasterRenderPass<PassData>(passName, out var passData, profilingSampler))
{
bool outputsToHDR = cameraData.isHDROutputActive;
bool outputsAlpha = cameraData.isAlphaOutputEnabled;
InitPassData(cameraData, ref passData, outputsToHDR ? BlitType.HDR : BlitType.Core, outputsAlpha, useFullScreenViewport);
passData.sourceID = ShaderPropertyId.sourceTex;
passData.source = sourceTexture;
builder.UseTexture(sourceTexture, AccessFlags.Read);
passData.destination = destinationTexture;
// The access flag can be set to WriteAll if there is a full screen blit and no alpha blending,
// so engine will set loadOperation to DontCare down to the pipe.
AccessFlags targetAccessFlag = cameraData.isDefaultViewport && !outputsAlpha ? AccessFlags.WriteAll : AccessFlags.Write;
#if ENABLE_VR && ENABLE_XR_MODULE
// 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);
// Multiview render regions are incompatible with the inner (foveal) pass in Quad View
if (!cameraData.xr.isQuadViewInnerPass)
{
builder.SetExtendedFeatureFlags(ExtendedFeatureFlags.MultiviewRenderRegionsCompatible);
}
#endif
builder.SetRenderAttachment(passData.destination, 0, targetAccessFlag);
if (outputsToHDR && overlayUITexture.IsValid())
{
VolumeStack stack = VolumeManager.instance.stack;
Tonemapping tonemapping = stack.GetComponent<Tonemapping>();
UniversalRenderPipeline.GetHDROutputLuminanceParameters(passData.cameraData.hdrDisplayInformation, passData.cameraData.hdrDisplayColorGamut, tonemapping, out passData.hdrOutputLuminanceParams);
builder.UseTexture(overlayUITexture, AccessFlags.Read);
}
else
{
passData.hdrOutputLuminanceParams = new Vector4(-1.0f, -1.0f, -1.0f, -1.0f);
}
builder.AllowGlobalStateModification(true);
builder.SetRenderFunc(static (PassData data, RasterGraphContext context) =>
{
data.blitMaterialData.material.enabledKeywords = null;
context.cmd.SetKeyword(ShaderGlobalKeywords.LinearToSRGBConversion, data.requireSrgbConversion);
data.blitMaterialData.material.SetTexture(data.sourceID, data.source);
DebugHandler debugHandler = GetActiveDebugHandler(data.cameraData);
bool resolveToDebugScreen = debugHandler != null && debugHandler.WriteToDebugScreenTexture(data.cameraData.resolveFinalTarget);
if (data.hdrOutputLuminanceParams.w >= 0)
{
HDROutputUtils.Operation hdrOperation = HDROutputUtils.Operation.None;
// If the HDRDebugView is on, we don't want the encoding
if (debugHandler == null || !debugHandler.HDRDebugViewIsActive(data.cameraData.resolveFinalTarget))
hdrOperation |= HDROutputUtils.Operation.ColorEncoding;
// Color conversion may have happened in the Uber post process through color grading, so we don't want to reapply it
if (!data.cameraData.postProcessEnabled)
hdrOperation |= HDROutputUtils.Operation.ColorConversion;
SetupHDROutput(data.cameraData.hdrDisplayColorGamut, data.blitMaterialData.material, hdrOperation, data.hdrOutputLuminanceParams, data.cameraData.rendersOverlayUI);
}
if (resolveToDebugScreen)
{
RTHandle sourceTex = data.source;
Vector2 viewportScale = sourceTex.useScaling ? new Vector2(sourceTex.rtHandleProperties.rtHandleScale.x, sourceTex.rtHandleProperties.rtHandleScale.y) : Vector2.one;
int shaderPassIndex = sourceTex.rt?.filterMode == FilterMode.Bilinear ? data.blitMaterialData.bilinearSamplerPass : data.blitMaterialData.nearestSamplerPass;
Blitter.BlitTexture(context.cmd, sourceTex, viewportScale, data.blitMaterialData.material, shaderPassIndex);
}
else
{
Vector4 scaleBias = RenderingUtils.GetFinalBlitScaleBias(context, in data.source, in data.destination);
ExecutePass(context.cmd, data, data.source, data.destination, data.cameraData, scaleBias);
}
});
}
}
/// <inheritdoc cref="IRenderGraphRecorder.RecordRenderGraph"/>
override public void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
var cameraData = frameData.Get<UniversalCameraData>();
var resourceData = frameData.Get<UniversalResourceData>();
Render(renderGraph, cameraData, resourceData, resourceData.cameraColor);
resourceData.SwitchActiveTexturesToBackbuffer();
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b66efce03c1804a4fbef78cccf176e4d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,204 @@
using System;
using Unity.Collections;
using Unity.Scripting.LifecycleManagement;
using UnityEngine.Rendering.RenderGraphModule;
namespace UnityEngine.Rendering.Universal.Internal
{
// Render all tiled-based deferred lights.
internal class GBufferPass : ScriptableRenderPass
{
// Statics
private static readonly int s_CameraNormalsTextureID = Shader.PropertyToID("_CameraNormalsTexture");
private static readonly int s_CameraRenderingLayersTextureID = Shader.PropertyToID("_CameraRenderingLayersTexture");
private static readonly ShaderTagId s_ShaderTagLit = new ShaderTagId("Lit");
private static readonly ShaderTagId s_ShaderTagSimpleLit = new ShaderTagId("SimpleLit");
private static readonly ShaderTagId s_ShaderTagUnlit = new ShaderTagId("Unlit");
private static readonly ShaderTagId s_ShaderTagComplexLit = new ShaderTagId("ComplexLit");
private static readonly ShaderTagId s_ShaderTagBakedLit = new ShaderTagId("BakedLit");
private static readonly ShaderTagId s_ShaderTagUniversalGBuffer = new ShaderTagId("UniversalGBuffer");
private static readonly ShaderTagId s_ShaderTagUniversalMaterialType = new ShaderTagId("UniversalMaterialType");
DeferredLights m_DeferredLights;
static readonly ShaderTagId[] s_ShaderTagValues = {
s_ShaderTagLit,
s_ShaderTagSimpleLit,
s_ShaderTagUnlit,
s_ShaderTagComplexLit,
s_ShaderTagBakedLit,
new ShaderTagId() // Special catch all case for materials where UniversalMaterialType is not defined or the tag value doesn't match anything we know.
};
RenderStateBlock[] m_RenderStateBlocks;
FilteringSettings m_FilteringSettings;
RenderStateBlock m_RenderStateBlock;
public GBufferPass(RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask, StencilState stencilState, int stencilReference, DeferredLights deferredLights)
{
base.profilingSampler = new ProfilingSampler("Draw GBuffer");
base.renderPassEvent = evt;
m_DeferredLights = deferredLights;
m_FilteringSettings = new FilteringSettings(renderQueueRange, layerMask);
m_RenderStateBlock = new RenderStateBlock(RenderStateMask.Nothing);
m_RenderStateBlock.stencilState = stencilState;
m_RenderStateBlock.stencilReference = stencilReference;
m_RenderStateBlock.mask = RenderStateMask.Stencil;
m_RenderStateBlocks = new RenderStateBlock[]
{
DeferredLights.OverwriteStencil(m_RenderStateBlock, (int)StencilUsage.MaterialMask, (int)StencilUsage.MaterialLit),
DeferredLights.OverwriteStencil(m_RenderStateBlock, (int)StencilUsage.MaterialMask, (int)StencilUsage.MaterialSimpleLit),
DeferredLights.OverwriteStencil(m_RenderStateBlock, (int)StencilUsage.MaterialMask, (int)StencilUsage.MaterialUnlit),
DeferredLights.OverwriteStencil(m_RenderStateBlock, (int)StencilUsage.MaterialMask, (int)StencilUsage.MaterialUnlit), // Fill GBuffer, but skip lighting pass for ComplexLit
DeferredLights.OverwriteStencil(m_RenderStateBlock, (int)StencilUsage.MaterialMask, (int)StencilUsage.MaterialUnlit), // Fill GBuffer, but skip lighting pass for BakedLit
DeferredLights.OverwriteStencil(m_RenderStateBlock, (int)StencilUsage.MaterialMask, (int)StencilUsage.MaterialLit)
};
}
static void ExecutePass(RasterCommandBuffer cmd, PassData data, RendererList rendererList, RendererList errorRendererList)
{
bool usesRenderingLayers = data.deferredLights.UseRenderingLayers && !data.deferredLights.HasRenderingLayerPrepass;
if (usesRenderingLayers)
cmd.SetKeyword(ShaderGlobalKeywords.WriteRenderingLayers, true);
bool useScreenSpaceIrradiance = data.screenSpaceIrradianceHdl.IsValid();
cmd.SetKeyword(ShaderGlobalKeywords.ScreenSpaceIrradiance, useScreenSpaceIrradiance);
if (useScreenSpaceIrradiance)
{
cmd.SetGlobalTexture(ShaderPropertyId.screenSpaceIrradiance, data.screenSpaceIrradianceHdl);
}
#if URP_SCREEN_SPACE_REFLECTION
bool useSSR = data.screenSpaceReflectionHdl.IsValid();
cmd.SetKeyword(ShaderGlobalKeywords.ScreenSpaceReflection, useSSR);
if (useSSR)
{
cmd.SetGlobalTexture(ShaderPropertyId.screenSpaceReflection, data.screenSpaceReflectionHdl);
}
#endif
cmd.DrawRendererList(rendererList);
// Render objects that did not match any shader pass with error shader
RenderingUtils.DrawRendererListObjectsWithError(cmd, ref errorRendererList);
// Clean up
if (usesRenderingLayers)
cmd.SetKeyword(ShaderGlobalKeywords.WriteRenderingLayers, false);
}
/// <summary>
/// Shared pass data
/// </summary>
private class PassData
{
internal DeferredLights deferredLights;
internal RendererListHandle rendererListHdl;
internal RendererListHandle objectsWithErrorRendererListHdl;
internal TextureHandle screenSpaceIrradianceHdl;
#if URP_SCREEN_SPACE_REFLECTION
internal TextureHandle screenSpaceReflectionHdl;
#endif
}
private void InitRendererLists( ref PassData passData, ScriptableRenderContext context, RenderGraph renderGraph, UniversalRenderingData renderingData, UniversalCameraData cameraData, UniversalLightData lightData, uint batchLayerMask = uint.MaxValue)
{
// User can stack several scriptable renderers during rendering but deferred renderer should only lit pixels added by this gbuffer pass.
// If we detect we are in such case (camera is in overlay mode), we clear the highest bits of stencil we have control of and use them to
// mark what pixel to shade during deferred pass. Gbuffer will always mark pixels using their material types.
ShaderTagId lightModeTag = s_ShaderTagUniversalGBuffer;
var drawingSettings = CreateDrawingSettings(lightModeTag, renderingData, cameraData, lightData, cameraData.defaultOpaqueSortFlags);
var filterSettings = m_FilteringSettings;
filterSettings.batchLayerMask = batchLayerMask;
#if UNITY_EDITOR
// When rendering the preview camera, we want the layer mask to be forced to Everything
if (cameraData.isPreviewCamera)
filterSettings.layerMask = -1;
#endif
NativeArray<ShaderTagId> tagValues = new NativeArray<ShaderTagId>(s_ShaderTagValues, Allocator.Temp);
NativeArray<RenderStateBlock> stateBlocks = new NativeArray<RenderStateBlock>(m_RenderStateBlocks, Allocator.Temp);
var param = new RendererListParams(renderingData.cullResults, drawingSettings, filterSettings)
{
tagValues = tagValues,
stateBlocks = stateBlocks,
tagName = s_ShaderTagUniversalMaterialType,
isPassTagName = false
};
passData.rendererListHdl = renderGraph.CreateRendererList(param);
RenderingUtils.CreateRendererListObjectsWithError(renderGraph, ref renderingData.cullResults, cameraData.camera, filterSettings, SortingCriteria.None, ref passData.objectsWithErrorRendererListHdl);
tagValues.Dispose();
stateBlocks.Dispose();
}
internal void Render(RenderGraph renderGraph, ContextContainer frameData, bool setGlobalTextures, uint batchLayerMask = uint.MaxValue)
{
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
UniversalRenderingData renderingData = frameData.Get<UniversalRenderingData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
UniversalLightData lightData = frameData.Get<UniversalLightData>();
using var builder = renderGraph.AddRasterRenderPass<PassData>(passName, out var passData, profilingSampler);
bool useCameraRenderingLayersTexture = m_DeferredLights.UseRenderingLayers && !m_DeferredLights.UseLightLayers;
var cameraColor = resourceData.activeColorTexture;
var cameraDepth = resourceData.activeDepthTexture;
var gbuffer = resourceData.gBuffer;
for (int i = 0; i < gbuffer.Length; i++)
{
Debug.Assert(gbuffer[i].IsValid());
builder.SetRenderAttachment(gbuffer[i], i, AccessFlags.Write);
}
TextureHandle irradianceTexture = resourceData.irradianceTexture;
if (irradianceTexture.IsValid())
{
passData.screenSpaceIrradianceHdl = irradianceTexture;
builder.UseTexture(irradianceTexture, AccessFlags.Read);
}
#if URP_SCREEN_SPACE_REFLECTION
TextureHandle ssrTexture = resourceData.ssrTexture;
if (ssrTexture.IsValid())
{
passData.screenSpaceReflectionHdl = ssrTexture;
builder.UseTexture(ssrTexture, AccessFlags.Read);
}
else
{
passData.screenSpaceReflectionHdl = TextureHandle.nullHandle;
}
#endif
RenderGraphUtils.UseDBufferIfValid(builder, resourceData);
builder.SetRenderAttachmentDepth(cameraDepth, AccessFlags.ReadWrite);
passData.deferredLights = m_DeferredLights;
InitRendererLists(ref passData, default, renderGraph, renderingData, cameraData, lightData);
builder.UseRendererList(passData.rendererListHdl);
builder.UseRendererList(passData.objectsWithErrorRendererListHdl);
if (setGlobalTextures)
{
builder.SetGlobalTextureAfterPass(resourceData.cameraNormalsTexture, s_CameraNormalsTextureID);
if (useCameraRenderingLayersTexture)
builder.SetGlobalTextureAfterPass(resourceData.renderingLayersTexture, s_CameraRenderingLayersTextureID);
}
builder.AllowGlobalStateModification(true);
builder.SetRenderFunc(static (PassData data, RasterGraphContext context) =>
{
ExecutePass(context.cmd, data, data.rendererListHdl, data.objectsWithErrorRendererListHdl);
});
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 10e31bb2e2b26314bb6132009378847c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,222 @@
using System;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
namespace UnityEngine.Rendering.Universal
{
/// <summary>
/// Generate HDR debug data into the given color target
/// </summary>
internal class HDRDebugViewPass : ScriptableRenderPass
{
private enum HDRDebugPassId
{
CIExyPrepass = 0,
DebugViewPass = 1
}
RTHandle m_PassthroughRT;
Material m_material;
/// <summary>
/// Creates a new <c>HDRDebugViewPass</c> instance.
/// </summary>
/// <param name="mat">The <c>Material</c> to use.</param>
/// <seealso cref="RenderPassEvent"/>
public HDRDebugViewPass(Material mat)
{
profilingSampler = new ProfilingSampler("Blit HDR Debug Data");
renderPassEvent = RenderPassEvent.AfterRendering + 3;
m_material = mat;
}
// Common to RenderGraph and non-RenderGraph paths
private class PassDataCIExy
{
internal Material material;
internal Vector4 luminanceParameters;
internal TextureHandle srcColor;
internal TextureHandle xyBuffer;
internal TextureHandle passThrough;
}
private class PassDataDebugView
{
internal Material material;
internal HDRDebugMode hdrDebugMode;
internal UniversalCameraData cameraData;
internal Vector4 luminanceParameters;
internal TextureHandle xyBuffer;
internal TextureHandle srcColor;
internal TextureHandle dstColor;
}
public static void ConfigureDescriptorForCIEPrepass(ref RenderTextureDescriptor descriptor)
{
descriptor.graphicsFormat = GraphicsFormat.R32_SFloat;
descriptor.width = descriptor.height = ShaderConstants._SizeOfHDRXYMapping;
descriptor.useMipMap = false;
descriptor.autoGenerateMips = false;
descriptor.useDynamicScale = true;
descriptor.depthStencilFormat = GraphicsFormat.None;
descriptor.enableRandomWrite = true;
descriptor.msaaSamples = 1;
descriptor.dimension = TextureDimension.Tex2D;
descriptor.vrUsage = VRTextureUsage.None; // We only need one for both eyes in VR
}
internal static Vector4 GetLuminanceParameters(UniversalCameraData cameraData)
{
var luminanceParams = Vector4.zero;
if (cameraData.isHDROutputActive)
{
Tonemapping tonemapping = VolumeManager.instance.stack.GetComponent<Tonemapping>();
UniversalRenderPipeline.GetHDROutputLuminanceParameters(cameraData.hdrDisplayInformation, cameraData.hdrDisplayColorGamut, tonemapping, out luminanceParams);
}
else
{
luminanceParams.z = 1.0f;
}
return luminanceParams;
}
private static void ExecuteCIExyPrepass(CommandBuffer cmd, PassDataCIExy data, RTHandle sourceTexture, RTHandle xyTarget, RTHandle destTexture)
{
CoreUtils.SetRenderTarget(cmd, destTexture, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.DontCare, ClearFlag.None, Color.clear);
Vector4 debugParameters = new Vector4(ShaderConstants._SizeOfHDRXYMapping, ShaderConstants._SizeOfHDRXYMapping, 0, 0);
cmd.SetRandomWriteTarget(ShaderConstants._CIExyUAVIndex, xyTarget);
data.material.SetVector(ShaderConstants._HDRDebugParamsId, debugParameters);
data.material.SetVector(ShaderPropertyId.hdrOutputLuminanceParams, data.luminanceParameters);
Vector2 viewportScale = sourceTexture.useScaling ? new Vector2(sourceTexture.rtHandleProperties.rtHandleScale.x, sourceTexture.rtHandleProperties.rtHandleScale.y) : Vector2.one;
Blitter.BlitTexture(cmd, sourceTexture, viewportScale, data.material, 0);
cmd.ClearRandomWriteTargets();
}
private static void ExecuteHDRDebugViewFinalPass(RasterCommandBuffer cmd, in PassDataDebugView data, RTHandle source, Vector4 scaleBias, RTHandle destination, RTHandle xyTarget)
{
if (data.cameraData.isHDROutputActive)
{
HDROutputUtils.ConfigureHDROutput(data.material, data.cameraData.hdrDisplayColorGamut, HDROutputUtils.Operation.ColorEncoding);
CoreUtils.SetKeyword(data.material, ShaderKeywordStrings.HDROverlay, data.cameraData.rendersOverlayUI);
}
data.material.SetTexture(ShaderConstants._xyTextureId, xyTarget);
Vector4 debugParameters = new Vector4(ShaderConstants._SizeOfHDRXYMapping, ShaderConstants._SizeOfHDRXYMapping, 0, 0);
data.material.SetVector(ShaderConstants._HDRDebugParamsId, debugParameters);
data.material.SetVector(ShaderPropertyId.hdrOutputLuminanceParams, data.luminanceParameters);
data.material.SetInteger(ShaderConstants._DebugHDRModeId, (int)data.hdrDebugMode);
RenderTargetIdentifier cameraTarget = BuiltinRenderTextureType.CameraTarget;
#if ENABLE_VR && ENABLE_XR_MODULE
if (data.cameraData.xr.enabled)
cameraTarget = data.cameraData.xr.renderTarget;
#endif
if (destination.nameID == cameraTarget || data.cameraData.targetTexture != null)
cmd.SetViewport(data.cameraData.pixelRect);
Blitter.BlitTexture(cmd, source, scaleBias, data.material, 1);
}
// Non-RenderGraph path
public void Dispose()
{
m_PassthroughRT?.Release();
}
/// <summary>
/// Configure the pass
/// </summary>
/// <param name="cameraData">Descriptor for the color buffer.</param>
/// <param name="hdrdebugMode">Active DebugMode for HDR.</param>
public void Setup(UniversalCameraData cameraData, HDRDebugMode hdrdebugMode)
{
RenderTextureDescriptor descriptor = cameraData.cameraTargetDescriptor;
DebugHandler.ConfigureColorDescriptorForDebugScreen(ref descriptor, cameraData.pixelWidth, cameraData.pixelHeight);
RenderingUtils.ReAllocateHandleIfNeeded(ref m_PassthroughRT, descriptor, name: "_HDRDebugDummyRT");
}
internal void RenderHDRDebug(RenderGraph renderGraph, UniversalCameraData cameraData, in TextureHandle srcColor, in TextureHandle overlayUITexture, in TextureHandle dstColor, HDRDebugMode hdrDebugMode)
{
bool requiresCIExyData = hdrDebugMode != HDRDebugMode.ValuesAbovePaperWhite;
Vector4 luminanceParameters = GetLuminanceParameters(cameraData);
TextureHandle intermediateRT = srcColor;
TextureHandle xyBuffer = TextureHandle.nullHandle;
if (requiresCIExyData)
{
RenderTextureDescriptor descriptor = cameraData.cameraTargetDescriptor;
DebugHandler.ConfigureColorDescriptorForDebugScreen(ref descriptor, cameraData.pixelWidth, cameraData.pixelHeight);
intermediateRT = UniversalRenderer.CreateRenderGraphTexture(renderGraph, descriptor, "_HDRDebugDummyRT", false);
ConfigureDescriptorForCIEPrepass(ref descriptor);
xyBuffer = UniversalRenderer.CreateRenderGraphTexture(renderGraph, descriptor, "_xyBuffer", true);
// Using low level pass because of random UAV support, and since this is a debug view, we don't care much about merging passes or optimizing for TBDR.
// This could be a compute pass (like in HDRP) but doing it in pixel is compatible with devices that might support HDR output but not compute shaders.
using (var builder = renderGraph.AddUnsafePass<PassDataCIExy>("Blit HDR DebugView CIExy", out var passData, profilingSampler))
{
passData.material = m_material;
passData.luminanceParameters = luminanceParameters;
passData.srcColor = srcColor;
builder.UseTexture(srcColor);
passData.xyBuffer = xyBuffer;
builder.UseTexture(xyBuffer, AccessFlags.Write);
passData.passThrough = intermediateRT;
builder.UseTexture(intermediateRT, AccessFlags.Write);
builder.SetRenderFunc(static (PassDataCIExy data, UnsafeGraphContext context) =>
{
ExecuteCIExyPrepass(CommandBufferHelpers.GetNativeCommandBuffer(context.cmd), data, data.srcColor, data.xyBuffer, data.passThrough);
});
}
}
using (var builder = renderGraph.AddRasterRenderPass<PassDataDebugView>("Blit HDR DebugView", out var passData, profilingSampler))
{
passData.material = m_material;
passData.hdrDebugMode = hdrDebugMode;
passData.luminanceParameters = luminanceParameters;
passData.cameraData = cameraData;
if (requiresCIExyData)
{
passData.xyBuffer = xyBuffer;
builder.UseTexture(xyBuffer);
}
passData.srcColor = srcColor;
builder.UseTexture(srcColor);
passData.dstColor = dstColor;
builder.SetRenderAttachment(dstColor, 0, AccessFlags.WriteAll);
if (overlayUITexture.IsValid())
{
builder.UseTexture(overlayUITexture);
}
builder.SetRenderFunc(static (PassDataDebugView data, RasterGraphContext context) =>
{
data.material.enabledKeywords = null;
Vector4 scaleBias = RenderingUtils.GetFinalBlitScaleBias(in context, in data.srcColor, in data.dstColor);
ExecuteHDRDebugViewFinalPass(context.cmd, in data, data.srcColor, scaleBias, data.dstColor, data.xyBuffer);
});
}
}
internal class ShaderConstants
{
public static readonly int _DebugHDRModeId = Shader.PropertyToID("_DebugHDRMode");
public static readonly int _HDRDebugParamsId = Shader.PropertyToID("_HDRDebugParams");
public static readonly int _xyTextureId = Shader.PropertyToID("_xyBuffer");
public static readonly int _SizeOfHDRXYMapping = 512;
public static readonly int _CIExyUAVIndex = 1;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4bcbfb254d9395f439b5d8a711e29acd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,41 @@
using System;
using UnityEngine.Rendering.RenderGraphModule;
namespace UnityEngine.Rendering.Universal
{
/// <summary>
/// Invokes OnRenderObject callback
/// </summary>
internal class InvokeOnRenderObjectCallbackPass : ScriptableRenderPass
{
public InvokeOnRenderObjectCallbackPass(RenderPassEvent evt)
{
profilingSampler = new ProfilingSampler("Invoke OnRenderObject Callback");
renderPassEvent = evt;
}
private class PassData
{
internal TextureHandle colorTarget;
internal TextureHandle depthTarget;
}
internal void Render(RenderGraph renderGraph, in TextureHandle colorTarget, in TextureHandle depthTarget)
{
using (var builder = renderGraph.AddUnsafePass<PassData>(passName, out var passData, profilingSampler))
{
passData.colorTarget = colorTarget;
builder.UseTexture(colorTarget, AccessFlags.Write);
passData.depthTarget = depthTarget;
builder.UseTexture(depthTarget, AccessFlags.Write);
builder.AllowPassCulling(false);
builder.SetRenderFunc(static (PassData data, UnsafeGraphContext context) =>
{
context.cmd.SetRenderTarget(data.colorTarget, data.depthTarget);
context.cmd.InvokeOnRenderObjectCallbacks();
});
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 65317dda628fae54d953accd02570d92
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,527 @@
using System;
using UnityEngine.Rendering.RenderGraphModule;
namespace UnityEngine.Rendering.Universal.Internal
{
/// <summary>
/// Renders a shadow map for the main Light.
/// </summary>
public partial class MainLightShadowCasterPass : ScriptableRenderPass
{
// Internal
internal RTHandle m_MainLightShadowmapTexture;
// Private
private int m_RenderTargetWidth;
private int m_RenderTargetHeight;
private int m_ShadowCasterCascadesCount;
private bool m_CreateEmptyShadowmap;
private bool m_SetKeywordForEmptyShadowmap;
private float m_CascadeBorder;
private float m_MaxShadowDistanceSq;
private RenderTextureDescriptor m_MainLightShadowDescriptor;
private readonly Vector4[] m_CascadeSplitDistances;
private readonly Matrix4x4[] m_MainLightShadowMatrices;
private readonly ProfilingSampler m_ProfilingSetupSampler = new("Setup Main Shadowmap");
private readonly ShadowSliceData[] m_CascadeSlices;
// Constants and Statics
private const int k_EmptyShadowMapDimensions = 1;
private const int k_MaxCascades = 4;
private const int k_ShadowmapBufferBits = 16;
private const string k_MainLightShadowMapTextureName = "_MainLightShadowmapTexture";
private static readonly Vector4 k_DefaultEmptyShadowParams = new Vector4(0f, 0f, 1f, 0f);
private static Vector4 s_EmptyShadowParams = k_DefaultEmptyShadowParams;
#if UNITY_EDITOR
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
static void ResetStaticsOnLoad()
{
s_EmptyShadowParams = k_DefaultEmptyShadowParams;
}
#endif
private static readonly Vector4 s_EmptyShadowmapSize = new(k_EmptyShadowMapDimensions, 1f / k_EmptyShadowMapDimensions, k_EmptyShadowMapDimensions, k_EmptyShadowMapDimensions);
// Classes
private static class MainLightShadowConstantBuffer
{
public static readonly int _WorldToShadow = Shader.PropertyToID("_MainLightWorldToShadow");
public static readonly int _ShadowParams = Shader.PropertyToID("_MainLightShadowParams");
public static readonly int _CascadeShadowSplitSpheres0 = Shader.PropertyToID("_CascadeShadowSplitSpheres0");
public static readonly int _CascadeShadowSplitSpheres1 = Shader.PropertyToID("_CascadeShadowSplitSpheres1");
public static readonly int _CascadeShadowSplitSpheres2 = Shader.PropertyToID("_CascadeShadowSplitSpheres2");
public static readonly int _CascadeShadowSplitSpheres3 = Shader.PropertyToID("_CascadeShadowSplitSpheres3");
public static readonly int _CascadeShadowSplitSphereRadii = Shader.PropertyToID("_CascadeShadowSplitSphereRadii");
public static readonly int _ShadowOffset0 = Shader.PropertyToID("_MainLightShadowOffset0");
public static readonly int _ShadowOffset1 = Shader.PropertyToID("_MainLightShadowOffset1");
public static readonly int _ShadowmapSize = Shader.PropertyToID("_MainLightShadowmapSize");
public static readonly int _MainLightShadowmapID = Shader.PropertyToID(k_MainLightShadowMapTextureName);
}
private class PassData
{
internal bool emptyShadowmap;
internal bool setKeywordForEmptyShadowmap;
internal UniversalRenderingData renderingData;
internal UniversalCameraData cameraData;
internal UniversalLightData lightData;
internal UniversalShadowData shadowData;
internal MainLightShadowCasterPass pass;
internal TextureHandle shadowmapTexture;
internal readonly RendererListHandle[] shadowRendererListsHandle = new RendererListHandle[k_MaxCascades];
}
/// <summary>
/// Creates a new <c>MainLightShadowCasterPass</c> instance.
/// </summary>
/// <param name="evt">The <c>RenderPassEvent</c> to use.</param>
/// <seealso cref="RenderPassEvent"/>
public MainLightShadowCasterPass(RenderPassEvent evt)
{
profilingSampler = new ProfilingSampler("Draw Main Light Shadowmap");
renderPassEvent = evt;
m_MainLightShadowMatrices = new Matrix4x4[k_MaxCascades + 1];
m_CascadeSlices = new ShadowSliceData[k_MaxCascades];
m_CascadeSplitDistances = new Vector4[k_MaxCascades];
}
/// <summary>
/// Cleans up resources used by the pass.
/// </summary>
public void Dispose()
{
m_MainLightShadowmapTexture?.Release();
}
internal TextureHandle GetOrCreateShadowMapTexture(RenderGraph renderGraph, UniversalShadowData shadowData)
{
// When shadow caching is supported (e.g. 2-pass QuadViews XR), use a persistent RTHandle
// imported into the render graph so the texture survives between XR passes.
// When caching is not supported, create a transient render graph texture.
#if ENABLE_VR && ENABLE_XR_MODULE
if (shadowData.supportShadowMapCaching)
{
RenderingUtils.ReAllocateHandleIfNeeded(ref m_MainLightShadowmapTexture,
m_MainLightShadowDescriptor,
ShadowUtils.m_ForceShadowPointSampling ? FilterMode.Point : FilterMode.Bilinear,
TextureWrapMode.Repeat, 1, 0, k_MainLightShadowMapTextureName);
ImportResourceParams importParams = new ImportResourceParams();
importParams.clearOnFirstUse = !shadowData.useCachedShadowMap;
importParams.clearColor = Color.black;
importParams.discardOnLastUse = shadowData.useCachedShadowMap;
return renderGraph.ImportTexture(m_MainLightShadowmapTexture, importParams);
}
#endif
return UniversalRenderer.CreateRenderGraphTexture(renderGraph, m_MainLightShadowDescriptor, k_MainLightShadowMapTextureName, true, ShadowUtils.m_ForceShadowPointSampling ? FilterMode.Point : FilterMode.Bilinear);
}
/// <summary>
/// Sets up the pass.
/// </summary>
/// <param name="renderingData"></param>
/// <returns>True if the pass should be enqueued, otherwise false.</returns>
/// <seealso cref="RenderingData"/>
public bool Setup(ref RenderingData renderingData)
{
ContextContainer frameData = renderingData.frameData;
UniversalRenderingData universalRenderingData = frameData.Get<UniversalRenderingData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
UniversalLightData lightData = frameData.Get<UniversalLightData>();
UniversalShadowData shadowData = frameData.Get<UniversalShadowData>();
return Setup(universalRenderingData, cameraData, lightData, shadowData);
}
/// <summary>
/// Sets up the pass.
/// </summary>
/// <param name="renderingData">Data containing rendering settings.</param>
/// <param name="cameraData">Data containing camera settings.</param>
/// <param name="lightData">Data containing light settings.</param>
/// <param name="shadowData">Data containing shadow settings.</param>
/// <returns>True if the pass should be enqueued, otherwise false.</returns>
/// <seealso cref="RenderingData"/>
public bool Setup(UniversalRenderingData renderingData, UniversalCameraData cameraData, UniversalLightData lightData, UniversalShadowData shadowData)
{
bool shadowsEnabled = shadowData.mainLightShadowsEnabled;
bool shadowsSupported = shadowData.supportsMainLightShadows;
#if UNITY_EDITOR
if (CoreUtils.IsSceneLightingDisabled(cameraData.camera))
return false;
#endif
using var profScope = new ProfilingScope(m_ProfilingSetupSampler);
bool stripShadowsOffVariants = cameraData.renderer.stripShadowsOffVariants;
// For cached shadow maps (e.g. 2-pass QuadViews XR inner pass), skip Clear() and
// setup — the outer pass has already populated all member state (matrices, cascades,
// descriptors). Reading and re-broadcasting that state happens in FetchShadowMapAndSetParameters.
#if ENABLE_VR && ENABLE_XR_MODULE
if (shadowData.useCachedShadowMap)
return true;
#endif
Clear();
int shadowLightIndex = lightData.mainLightIndex;
if (shadowLightIndex == -1 || (cameraData.camera.targetTexture != null && cameraData.camera.targetTexture.format == RenderTextureFormat.Depth))
{
if (shadowsEnabled)
return SetupForEmptyRendering(stripShadowsOffVariants, shadowsEnabled, null, cameraData, shadowData);
else
return false;
}
VisibleLight shadowLight = lightData.visibleLights[shadowLightIndex];
Light light = shadowLight.light;
if (shadowsSupported && light.shadows == LightShadows.None)
return SetupForEmptyRendering(stripShadowsOffVariants, shadowsEnabled, light, cameraData, shadowData);
if (!shadowsEnabled)
{
// If (realtime) shadows are disabled, but the light casts baked shadows, we need to do empty rendering to setup the _MainLightShadowParams uniform,
// which is also used when sampling baked shadows. This allows for using baked shadows even when realtime shadows are completely disabled.
if (light.shadows != LightShadows.None &&
light.bakingOutput.isBaked &&
light.bakingOutput.mixedLightingMode != MixedLightingMode.IndirectOnly &&
light.bakingOutput.lightmapBakeType == LightmapBakeType.Mixed)
{
return SetupForEmptyRendering(stripShadowsOffVariants, shadowsEnabled, light, cameraData, shadowData);
}
return false;
}
if (!shadowsSupported)
return SetupForEmptyRendering(stripShadowsOffVariants, shadowsEnabled, null, cameraData, shadowData);
if (shadowLight.lightType != LightType.Directional)
{
Debug.LogWarning("Only directional lights are supported as main light.");
}
if (!renderingData.cullResults.GetShadowCasterBounds(shadowLightIndex, out Bounds _))
return SetupForEmptyRendering(stripShadowsOffVariants, shadowsEnabled, light, cameraData, shadowData);
m_ShadowCasterCascadesCount = shadowData.mainLightShadowCascadesCount;
m_RenderTargetWidth = shadowData.mainLightRenderTargetWidth;
m_RenderTargetHeight = shadowData.mainLightRenderTargetHeight;
ref readonly URPLightShadowCullingInfos shadowCullingInfos = ref shadowData.visibleLightsShadowCullingInfos.UnsafeElementAt(shadowLightIndex);
for (int cascadeIndex = 0; cascadeIndex < m_ShadowCasterCascadesCount; ++cascadeIndex)
{
ref readonly ShadowSliceData sliceData = ref shadowCullingInfos.slices.UnsafeElementAt(cascadeIndex);
m_CascadeSplitDistances[cascadeIndex] = sliceData.splitData.cullingSphere;
m_CascadeSlices[cascadeIndex] = sliceData;
if (!shadowCullingInfos.IsSliceValid(cascadeIndex))
return SetupForEmptyRendering(stripShadowsOffVariants, shadowsEnabled, light, cameraData, shadowData);
}
UpdateTextureDescriptorIfNeeded();
m_MaxShadowDistanceSq = cameraData.maxShadowDistance * cameraData.maxShadowDistance;
m_CascadeBorder = shadowData.mainLightShadowCascadeBorder;
m_CreateEmptyShadowmap = false;
return true;
}
private void UpdateTextureDescriptorIfNeeded()
{
if ( m_MainLightShadowDescriptor.width != m_RenderTargetWidth
|| m_MainLightShadowDescriptor.height != m_RenderTargetHeight
|| m_MainLightShadowDescriptor.depthBufferBits != k_ShadowmapBufferBits
|| m_MainLightShadowDescriptor.colorFormat != RenderTextureFormat.Shadowmap)
{
m_MainLightShadowDescriptor = new RenderTextureDescriptor(m_RenderTargetWidth, m_RenderTargetHeight, RenderTextureFormat.Shadowmap, k_ShadowmapBufferBits);
}
}
bool SetupForEmptyRendering(bool stripShadowsOffVariants, bool shadowsEnabled, Light light, UniversalCameraData cameraData, UniversalShadowData shadowData)
{
if (!stripShadowsOffVariants)
return false;
m_CreateEmptyShadowmap = true;
m_SetKeywordForEmptyShadowmap = shadowsEnabled;
// Even though there are not real-time shadows, the light might be using shadowmasks,
// which is why we need to update the shadow parameters, for example so shadow strength can be used.
if (light == null)
{
s_EmptyShadowParams = new Vector4(0, 0, 1, 0);
}
else
{
bool supportsSoftShadows = shadowData.supportsSoftShadows;
float maxShadowDistanceSq = cameraData.maxShadowDistance;
float mainLightShadowCascadeBorder = shadowData.mainLightShadowCascadeBorder;
bool softShadows = light.shadows == LightShadows.Soft && supportsSoftShadows;
float softShadowsProp = ShadowUtils.SoftShadowQualityToShaderProperty(light, softShadows);
ShadowUtils.GetScaleAndBiasForLinearDistanceFade(maxShadowDistanceSq, mainLightShadowCascadeBorder, out float shadowFadeScale, out float shadowFadeBias);
s_EmptyShadowParams = new Vector4(light.shadowStrength, softShadowsProp, shadowFadeScale, shadowFadeBias);
}
return true;
}
void Clear()
{
for (int i = 0; i < m_MainLightShadowMatrices.Length; ++i)
m_MainLightShadowMatrices[i] = Matrix4x4.identity;
for (int i = 0; i < m_CascadeSplitDistances.Length; ++i)
m_CascadeSplitDistances[i] = new Vector4(0.0f, 0.0f, 0.0f, 0.0f);
for (int i = 0; i < m_CascadeSlices.Length; ++i)
m_CascadeSlices[i].Clear();
}
internal static void SetShadowParamsForEmptyShadowmap(RasterCommandBuffer rasterCommandBuffer)
{
rasterCommandBuffer.SetGlobalVector(MainLightShadowConstantBuffer._ShadowmapSize, s_EmptyShadowmapSize);
rasterCommandBuffer.SetGlobalVector(MainLightShadowConstantBuffer._ShadowParams, s_EmptyShadowParams);
}
void RenderMainLightCascadeShadowmap(RasterCommandBuffer cmd, ref PassData data)
{
var lightData = data.lightData;
int shadowLightIndex = lightData.mainLightIndex;
if (shadowLightIndex == -1)
return;
VisibleLight shadowLight = lightData.visibleLights[shadowLightIndex];
using (new ProfilingScope(cmd, URPProfilingSamplers.MainLightShadow, shadowLight.light))
{
// Need to start by setting the Camera position and worldToCamera Matrix as that is not set for passes executed before normal rendering
ShadowUtils.SetCameraPosition(cmd, data.cameraData.worldSpaceCameraPos);
for (int cascadeIndex = 0; cascadeIndex < m_ShadowCasterCascadesCount; ++cascadeIndex)
{
Vector4 shadowBias = ShadowUtils.GetShadowBias(ref shadowLight, shadowLightIndex, data.shadowData, m_CascadeSlices[cascadeIndex].projectionMatrix, m_CascadeSlices[cascadeIndex].resolution);
ShadowUtils.SetupShadowCasterConstantBuffer(cmd, ref shadowLight, shadowBias);
cmd.SetKeyword(ShaderGlobalKeywords.CastingPunctualLightShadow, false);
RendererList shadowRendererList = data.shadowRendererListsHandle[cascadeIndex];
ShadowUtils.RenderShadowSlice(cmd, ref m_CascadeSlices[cascadeIndex], ref shadowRendererList, m_CascadeSlices[cascadeIndex].projectionMatrix, m_CascadeSlices[cascadeIndex].viewMatrix);
}
SetShadowGlobalKeywordsAndConstants(cmd, ref shadowLight, data.shadowData);
}
}
internal void SetShadowGlobalKeywordsAndConstants(RasterCommandBuffer cmd, ref VisibleLight shadowLight, UniversalShadowData shadowData)
{
shadowData.isKeywordSoftShadowsEnabled = shadowLight.light.shadows == LightShadows.Soft && shadowData.supportsSoftShadows;
cmd.SetKeyword(ShaderGlobalKeywords.MainLightShadows, shadowData.mainLightShadowCascadesCount == 1);
cmd.SetKeyword(ShaderGlobalKeywords.MainLightShadowCascades, shadowData.mainLightShadowCascadesCount > 1);
ShadowUtils.SetSoftShadowQualityShaderKeywords(cmd, shadowData);
SetupMainLightShadowReceiverConstants(cmd, ref shadowLight, shadowData);
}
void SetupMainLightShadowReceiverConstants(RasterCommandBuffer cmd, ref VisibleLight shadowLight, UniversalShadowData shadowData)
{
Light light = shadowLight.light;
bool softShadows = shadowLight.light.shadows == LightShadows.Soft && shadowData.supportsSoftShadows;
int cascadeCount = m_ShadowCasterCascadesCount;
for (int i = 0; i < cascadeCount; ++i)
m_MainLightShadowMatrices[i] = m_CascadeSlices[i].shadowTransform;
// We setup and additional a no-op WorldToShadow matrix in the last index
// because the ComputeCascadeIndex function in Shadows.hlsl can return an index
// out of bounds. (position not inside any cascade) and we want to avoid branching
Matrix4x4 noOpShadowMatrix = Matrix4x4.zero;
noOpShadowMatrix.m22 = (SystemInfo.usesReversedZBuffer) ? 1.0f : 0.0f;
for (int i = cascadeCount; i <= k_MaxCascades; ++i)
m_MainLightShadowMatrices[i] = noOpShadowMatrix;
float invShadowAtlasWidth = 1.0f / m_RenderTargetWidth;
float invShadowAtlasHeight = 1.0f / m_RenderTargetHeight;
float invHalfShadowAtlasWidth = 0.5f * invShadowAtlasWidth;
float invHalfShadowAtlasHeight = 0.5f * invShadowAtlasHeight;
float softShadowsProp = ShadowUtils.SoftShadowQualityToShaderProperty(light, softShadows);
ShadowUtils.GetScaleAndBiasForLinearDistanceFade(m_MaxShadowDistanceSq, m_CascadeBorder, out float shadowFadeScale, out float shadowFadeBias);
cmd.SetGlobalMatrixArray(MainLightShadowConstantBuffer._WorldToShadow, m_MainLightShadowMatrices);
cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowParams,
new Vector4(light.shadowStrength, softShadowsProp, shadowFadeScale, shadowFadeBias));
if (m_ShadowCasterCascadesCount > 1)
{
cmd.SetGlobalVector(MainLightShadowConstantBuffer._CascadeShadowSplitSpheres0,
m_CascadeSplitDistances[0]);
cmd.SetGlobalVector(MainLightShadowConstantBuffer._CascadeShadowSplitSpheres1,
m_CascadeSplitDistances[1]);
cmd.SetGlobalVector(MainLightShadowConstantBuffer._CascadeShadowSplitSpheres2,
m_CascadeSplitDistances[2]);
cmd.SetGlobalVector(MainLightShadowConstantBuffer._CascadeShadowSplitSpheres3,
m_CascadeSplitDistances[3]);
cmd.SetGlobalVector(MainLightShadowConstantBuffer._CascadeShadowSplitSphereRadii, new Vector4(
m_CascadeSplitDistances[0].w * m_CascadeSplitDistances[0].w,
m_CascadeSplitDistances[1].w * m_CascadeSplitDistances[1].w,
m_CascadeSplitDistances[2].w * m_CascadeSplitDistances[2].w,
m_CascadeSplitDistances[3].w * m_CascadeSplitDistances[3].w));
}
// Inside shader soft shadows are controlled through global keyword.
// If any additional light has soft shadows it will force soft shadows on main light too.
// As it is not trivial finding out which additional light has soft shadows, we will pass main light properties if soft shadows are supported.
// This workaround will be removed once we will support soft shadows per light.
if (shadowData.supportsSoftShadows)
{
cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowOffset0,
new Vector4(-invHalfShadowAtlasWidth, -invHalfShadowAtlasHeight,
invHalfShadowAtlasWidth, -invHalfShadowAtlasHeight));
cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowOffset1,
new Vector4(-invHalfShadowAtlasWidth, invHalfShadowAtlasHeight,
invHalfShadowAtlasWidth, invHalfShadowAtlasHeight));
cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowmapSize, new Vector4(invShadowAtlasWidth,
invShadowAtlasHeight,
m_RenderTargetWidth, m_RenderTargetHeight));
}
}
private void InitPassData(
ref PassData passData,
UniversalRenderingData renderingData,
UniversalCameraData cameraData,
UniversalLightData lightData,
UniversalShadowData shadowData)
{
passData.pass = this;
passData.emptyShadowmap = m_CreateEmptyShadowmap;
passData.setKeywordForEmptyShadowmap = m_SetKeywordForEmptyShadowmap;
passData.renderingData = renderingData;
passData.cameraData = cameraData;
passData.lightData = lightData;
passData.shadowData = shadowData;
}
private void InitRendererLists(ref PassData passData, RenderGraph renderGraph)
{
int shadowLightIndex = passData.lightData.mainLightIndex;
if (!m_CreateEmptyShadowmap && shadowLightIndex != -1)
{
ShadowDrawingSettings settings = new (passData.renderingData.cullResults, shadowLightIndex) {
useRenderingLayerMaskTest = UniversalRenderPipeline.asset.useRenderingLayers
};
for (int cascadeIndex = 0; cascadeIndex < m_ShadowCasterCascadesCount; ++cascadeIndex)
{
passData.shadowRendererListsHandle[cascadeIndex] = renderGraph.CreateShadowRendererList(ref settings);
}
}
}
#if ENABLE_VR && ENABLE_XR_MODULE
internal TextureHandle FetchShadowMapAndSetParameters(RenderGraph graph, ContextContainer frameData)
{
UniversalRenderingData renderingData = frameData.Get<UniversalRenderingData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
UniversalLightData lightData = frameData.Get<UniversalLightData>();
UniversalShadowData shadowData = frameData.Get<UniversalShadowData>();
TextureHandle shadowTexture;
using (var builder = graph.AddRasterRenderPass<PassData>(passName, out var passData, profilingSampler))
{
InitPassData(ref passData, renderingData, cameraData, lightData, shadowData);
if (!m_CreateEmptyShadowmap)
{
shadowTexture = GetOrCreateShadowMapTexture(graph, shadowData);
}
else
{
shadowTexture = graph.defaultResources.defaultShadowTexture;
}
builder.AllowGlobalStateModification(true);
if (shadowTexture.IsValid())
builder.SetGlobalTextureAfterPass(shadowTexture, MainLightShadowConstantBuffer._MainLightShadowmapID);
builder.SetRenderFunc(static (PassData data, RasterGraphContext context) =>
{
var lightData = data.lightData;
int shadowLightIndex = lightData.mainLightIndex;
if (shadowLightIndex == -1)
return;
VisibleLight shadowLight = lightData.visibleLights[shadowLightIndex];
data.pass.SetShadowGlobalKeywordsAndConstants(context.cmd, ref shadowLight, data.shadowData);
if (data.emptyShadowmap)
{
if (data.setKeywordForEmptyShadowmap)
context.cmd.EnableKeyword(ShaderGlobalKeywords.MainLightShadows);
SetShadowParamsForEmptyShadowmap(context.cmd);
}
});
}
return shadowTexture;
}
#endif
internal TextureHandle Render(RenderGraph graph, ContextContainer frameData)
{
UniversalRenderingData renderingData = frameData.Get<UniversalRenderingData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
UniversalLightData lightData = frameData.Get<UniversalLightData>();
UniversalShadowData shadowData = frameData.Get<UniversalShadowData>();
TextureHandle shadowTexture;
using (var builder = graph.AddRasterRenderPass<PassData>(passName, out var passData, profilingSampler))
{
InitPassData(ref passData, renderingData, cameraData, lightData, shadowData);
InitRendererLists(ref passData, graph);
if (!m_CreateEmptyShadowmap)
{
for (int cascadeIndex = 0; cascadeIndex < m_ShadowCasterCascadesCount; ++cascadeIndex)
{
builder.UseRendererList(passData.shadowRendererListsHandle[cascadeIndex]);
}
shadowTexture = GetOrCreateShadowMapTexture(graph, shadowData);
builder.SetRenderAttachmentDepth(shadowTexture, AccessFlags.ReadWrite);
}
else
{
shadowTexture = graph.defaultResources.defaultShadowTexture;
}
builder.AllowGlobalStateModification(true);
if (shadowTexture.IsValid())
builder.SetGlobalTextureAfterPass(shadowTexture, MainLightShadowConstantBuffer._MainLightShadowmapID);
builder.SetRenderFunc(static (PassData data, RasterGraphContext context) =>
{
RasterCommandBuffer rasterCommandBuffer = context.cmd;
if (!data.emptyShadowmap)
{
data.pass.RenderMainLightCascadeShadowmap(rasterCommandBuffer, ref data);
}
else
{
if (data.setKeywordForEmptyShadowmap)
rasterCommandBuffer.EnableKeyword(ShaderGlobalKeywords.MainLightShadows);
SetShadowParamsForEmptyShadowmap(rasterCommandBuffer);
}
});
}
return shadowTexture;
}
};
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 51015cd4dddd59d4b95f01cf645067bd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,220 @@
using System;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
namespace UnityEngine.Rendering.Universal
{
sealed class MotionVectorRenderPass : ScriptableRenderPass
{
internal const string k_MotionVectorTextureName = "_MotionVectorTexture";
internal const string k_MotionVectorDepthTextureName = "_MotionVectorDepthTexture";
internal const GraphicsFormat k_TargetFormat = GraphicsFormat.R16G16_SFloat;
public const string k_MotionVectorsLightModeTag = "MotionVectors";
static readonly string[] s_ShaderTags = new string[] { k_MotionVectorsLightModeTag };
static readonly int s_CameraDepthTextureID = Shader.PropertyToID("_CameraDepthTexture");
static readonly ProfilingSampler s_SetMotionMatrixProfilingSampler = new ProfilingSampler("Set Motion Vector Global Matrices");
readonly Material m_CameraMaterial;
readonly FilteringSettings m_FilteringSettings;
internal MotionVectorRenderPass(RenderPassEvent evt, Material cameraMaterial, LayerMask opaqueLayerMask)
{
profilingSampler = URPProfilingSamplers.DrawMotionVectors;
renderPassEvent = evt;
m_CameraMaterial = cameraMaterial;
m_FilteringSettings = new FilteringSettings(RenderQueueRange.opaque,opaqueLayerMask);
ConfigureInput(ScriptableRenderPassInput.Depth);
}
private static void ExecutePass(RasterCommandBuffer cmd, PassData passData, RendererList rendererList)
{
var cameraMaterial = passData.cameraMaterial;
if (cameraMaterial == null)
return;
// Get data
Camera camera = passData.camera;
// Never draw in Preview
if (camera.cameraType == CameraType.Preview)
return;
// These flags are still required in SRP or the engine won't compute previous model matrices...
// If the flag hasn't been set yet on this camera, motion vectors will skip a frame.
camera.depthTextureMode |= DepthTextureMode.MotionVectors | DepthTextureMode.Depth;
// TODO: add option to only draw either one?
DrawCameraMotionVectors(cmd, passData.xr, cameraMaterial);
DrawObjectMotionVectors(cmd, passData.xr, ref rendererList);
}
private static DrawingSettings GetDrawingSettings(Camera camera)
{
var sortingSettings = new SortingSettings(camera) { criteria = SortingCriteria.CommonOpaque };
var drawingSettings = new DrawingSettings(ShaderTagId.none, sortingSettings)
{
perObjectData = PerObjectData.MotionVectors,
enableInstancing = true,
lodCrossFadeStencilMask = 0, // Disable stencil-based lod because depth copy before motion vector pass doesn't copy stencils.
};
for (int i = 0; i < s_ShaderTags.Length; ++i)
{
drawingSettings.SetShaderPassName(i, new ShaderTagId(s_ShaderTags[i]));
}
return drawingSettings;
}
// NOTE: depends on camera depth to reconstruct static geometry positions
private static void DrawCameraMotionVectors(RasterCommandBuffer cmd, XRPass xr, Material cameraMaterial)
{
#if ENABLE_VR && ENABLE_XR_MODULE
bool foveatedRendering = xr.supportsFoveatedRendering;
bool nonUniformFoveatedRendering = foveatedRendering && XRSystem.foveatedRenderingCaps.HasFlag(FoveatedRenderingCaps.NonUniformRaster);
if (foveatedRendering)
{
if (nonUniformFoveatedRendering)
// This is a screen-space pass, make sure foveated rendering is disabled for non-uniform renders
cmd.SetFoveatedRenderingMode(FoveatedRenderingMode.Disabled);
else
cmd.SetFoveatedRenderingMode(FoveatedRenderingMode.Enabled);
}
#endif
// Draw fullscreen quad
cmd.DrawProcedural(Matrix4x4.identity, cameraMaterial, 0, MeshTopology.Triangles, 3, 1);
#if ENABLE_VR && ENABLE_XR_MODULE
if (foveatedRendering && !nonUniformFoveatedRendering)
cmd.SetFoveatedRenderingMode(FoveatedRenderingMode.Disabled);
#endif
}
private static void DrawObjectMotionVectors(RasterCommandBuffer cmd, XRPass xr, ref RendererList rendererList)
{
#if ENABLE_VR && ENABLE_XR_MODULE
bool foveatedRendering = xr.supportsFoveatedRendering;
if (foveatedRendering)
// This is a geometry pass, enable foveated rendering (we need to disable it after)
cmd.SetFoveatedRenderingMode(FoveatedRenderingMode.Enabled);
#endif
cmd.DrawRendererList(rendererList);
#if ENABLE_VR && ENABLE_XR_MODULE
if (foveatedRendering)
cmd.SetFoveatedRenderingMode(FoveatedRenderingMode.Disabled);
#endif
}
/// <summary>
/// Shared pass data
/// </summary>
private class PassData
{
internal Camera camera;
internal XRPass xr;
internal TextureHandle cameraDepth;
internal Material cameraMaterial;
internal RendererListHandle rendererListHdl;
// Required for code sharing purpose between RG and non-RG.
internal RendererList rendererList;
}
/// <summary>
/// Initialize the shared pass data.
/// </summary>
/// <param name="passData"></param>
private void InitPassData(ref PassData passData, UniversalCameraData cameraData)
{
passData.camera = cameraData.camera;
passData.xr = cameraData.xr;
passData.cameraMaterial = m_CameraMaterial;
}
private void InitRendererLists(ref PassData passData, ref CullingResults cullResults, RenderGraph renderGraph)
{
var drawingSettings = GetDrawingSettings(passData.camera);
var renderStateBlock = new RenderStateBlock(RenderStateMask.Nothing);
RenderingUtils.CreateRendererListWithRenderStateBlock(renderGraph, ref cullResults, drawingSettings, m_FilteringSettings, renderStateBlock, ref passData.rendererListHdl);
}
internal void Render(RenderGraph renderGraph, ContextContainer frameData, in TextureHandle cameraDepthTexture, in TextureHandle motionVectorColor, in TextureHandle motionVectorDepth)
{
UniversalRenderingData renderingData = frameData.Get<UniversalRenderingData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
using (var builder = renderGraph.AddRasterRenderPass<PassData>(passName, out var passData, profilingSampler))
{
builder.UseAllGlobalTextures(true);
builder.AllowGlobalStateModification(true);
if (cameraData.xr.enabled)
{
builder.EnableFoveatedRasterization(cameraData.xr.supportsFoveatedRendering && cameraData.xrUniversal.canFoveateIntermediatePasses);
// Multiview render regions are incompatible with the inner (foveal) pass in Quad View
if (!cameraData.xr.isQuadViewInnerPass)
{
builder.SetExtendedFeatureFlags(ExtendedFeatureFlags.MultiviewRenderRegionsCompatible);
}
}
builder.SetRenderAttachment(motionVectorColor, 0, AccessFlags.Write);
builder.SetRenderAttachmentDepth(motionVectorDepth, AccessFlags.ReadWrite);
InitPassData(ref passData, cameraData);
passData.cameraDepth = cameraDepthTexture;
builder.UseTexture(cameraDepthTexture, AccessFlags.Read);
InitRendererLists(ref passData, ref renderingData.cullResults, renderGraph);
builder.UseRendererList(passData.rendererListHdl);
if (motionVectorColor.IsValid())
builder.SetGlobalTextureAfterPass(motionVectorColor, Shader.PropertyToID(k_MotionVectorTextureName));
if (motionVectorDepth.IsValid())
builder.SetGlobalTextureAfterPass(motionVectorDepth, Shader.PropertyToID(k_MotionVectorDepthTextureName));
builder.SetRenderFunc(static (PassData data, RasterGraphContext context) =>
{
if (data.cameraMaterial != null)
data.cameraMaterial.SetTexture(s_CameraDepthTextureID, data.cameraDepth);
ExecutePass(context.cmd, data, data.rendererListHdl);
});
}
}
// Global motion vector matrix setup pass.
// Used for MotionVector passes and also read in VFX early compute shader
public class MotionMatrixPassData
{
public MotionVectorsPersistentData motionData;
public XRPass xr;
}
internal static void SetRenderGraphMotionVectorGlobalMatrices(RenderGraph renderGraph, UniversalCameraData cameraData)
{
if (cameraData.camera.TryGetComponent<UniversalAdditionalCameraData>(out var additionalCameraData))
{
using (var builder = renderGraph.AddRasterRenderPass<MotionMatrixPassData>(s_SetMotionMatrixProfilingSampler.name, out var passData, s_SetMotionMatrixProfilingSampler))
{
passData.motionData = additionalCameraData.motionVectorsPersistentData;
passData.xr = cameraData.xr;
builder.AllowGlobalStateModification(true);
builder.SetRenderFunc(static (MotionMatrixPassData data, RasterGraphContext context) =>
{
data.motionData.SetGlobalMotionMatrices(context.cmd, data.xr);
});
}
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 096294dfb309e47929b561541e4b087e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 296ca1f7792f46beb0b79e0209c7c02b
timeCreated: 1748346433
@@ -0,0 +1,521 @@
using System;
using Unity.Profiling.LowLevel;
using UnityEngine.Rendering.RenderGraphModule;
using System.Runtime.CompilerServices; // AggressiveInlining
namespace UnityEngine.Rendering.Universal
{
internal sealed class BloomPostProcessPass : PostProcessPass
{
public const int k_MaxPyramidSize = 16;
/// <summary>Generates bloom mipmap chain using iterative Kawase blur kernels for fast, high-quality bloom with fewer texture samples per pass.</summary>
static readonly ProfilingSampler k_ProfilingSamplerKawase = ProfilingSampler.Create("Blit Bloom Mipmaps (Kawase)", MarkerFlags.Default);
/// <summary>Generates bloom mipmap chain using dual filtering (downsample then upsample with tent filter) for efficient wide-radius bloom.</summary>
static readonly ProfilingSampler k_ProfilingSamplerDual = ProfilingSampler.Create("Blit Bloom Mipmaps (Dual)", MarkerFlags.Default);
Material m_Material;
Material[] m_MaterialPyramid;
// Cached bloom params from previous frame to avoid unnecessary material updates
MaterialParams m_PrevParams;
BloomMipPyramid m_MipPyramid;
bool m_IsValid;
public BloomMipPyramid mipPyramid => m_MipPyramid;
const string k_PassNameKawase = "Blit Bloom Mipmaps (Kawase)";
const string k_PassNameDual = "Blit Bloom Mipmaps (Dual)";
public BloomPostProcessPass(Shader shader)
{
this.renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing - 1;
this.profilingSampler = URPProfilingSamplers.Bloom;
m_Material = PostProcessUtils.LoadShader(shader, passName, logLevel: LogType.Log);
// Array for bloom pyramid materials.
// Materials are references in the command buffer, so we need a separate material for each mip level.
m_MaterialPyramid = new Material[k_MaxPyramidSize];
for (uint i = 0; i < k_MaxPyramidSize; ++i)
m_MaterialPyramid[i] = PostProcessUtils.LoadShader(shader, passName, logLevel: LogType.Log);
// Check if the pass init was successful.
m_IsValid = m_Material != null;
for(int i = 0; i < k_MaxPyramidSize; i++)
m_IsValid &= m_MaterialPyramid[i] != null;
m_MipPyramid = new BloomMipPyramid(k_MaxPyramidSize);
}
public override void Dispose()
{
CoreUtils.Destroy(m_Material);
for(int i = 0; i < k_MaxPyramidSize; i++)
CoreUtils.Destroy(m_MaterialPyramid[i]);
}
private class BloomPassData
{
internal Material material;
internal Material[] mipMaterials;
internal TextureHandle sourceTexture;
internal BloomMipPyramid mipPyramid;
}
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
if (!m_IsValid)
return;
var bloom = volumeStack.GetComponent<Bloom>();
if (!bloom.IsActive())
return;
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
var sourceTexture = resourceData.cameraColor;
var sourceDesc = sourceTexture.GetDescriptor(renderGraph);
// Setup
// Materials are set up beforehand.
// We rely on the fact that they're private and separate for each blit.
// They should remain unchanged between graph build and execution.
using(new ProfilingScope(URPProfilingSamplers.BloomSetup))
{
m_MipPyramid.Update(renderGraph, bloom, in sourceDesc);
int mipCount = m_MipPyramid.mipCount;
// Pre-filtering parameters
float clamp = bloom.clamp.value;
float threshold = Mathf.GammaToLinearSpace(bloom.threshold.value);
float thresholdKnee = threshold * 0.5f; // Hardcoded soft knee
// Material setup
float scatter = Mathf.Lerp(0.05f, 0.95f, bloom.scatter.value); // Blend factor between low/hi mip on upsample.
float kawaseScatter = Mathf.Clamp01(bloom.scatter.value); // Blend factor between linear and blurred sample. 1.0 for strict Kawase blur.
float dualScatter = Mathf.Lerp(0.3f, 1.3f, bloom.scatter.value); // Dual upsample filter scale. Scatter default == 0.7 --> 1.0 filter scale.
MaterialParams newParams = new MaterialParams();
newParams.parameters = new Vector4(scatter, clamp, threshold, thresholdKnee);
newParams.parameters2 = new Vector4(0.5f, kawaseScatter, dualScatter, 0.5f * dualScatter);
newParams.bloomFilter = bloom.filter.value;
newParams.highQualityFiltering = bloom.highQualityFiltering.value;
newParams.enableAlphaOutput = cameraData.isAlphaOutputEnabled;
// Setting keywords can be somewhat expensive on low-end platforms.
// Previous params are cached to avoid setting the same keywords every frame.
bool paramsDirty = !m_PrevParams.Equals(ref newParams);
bool isParamsPropertySet = m_Material.HasProperty(ShaderConstants._Params);
if (paramsDirty || !isParamsPropertySet)
{
m_Material.SetVector(ShaderConstants._Params, newParams.parameters);
m_Material.SetVector(ShaderConstants._Params2, newParams.parameters2);
CoreUtils.SetKeyword(m_Material, ShaderKeywordStrings.BloomHQ, newParams.highQualityFiltering);
CoreUtils.SetKeyword(m_Material, ShaderKeywordStrings._ENABLE_ALPHA_OUTPUT, newParams.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_MaterialPyramid[i];
materialPyramid.SetVector(ShaderConstants._Params, newParams.parameters);
CoreUtils.SetKeyword(materialPyramid, ShaderKeywordStrings.BloomHQ, newParams.highQualityFiltering);
CoreUtils.SetKeyword(materialPyramid, ShaderKeywordStrings._ENABLE_ALPHA_OUTPUT, newParams.enableAlphaOutput);
// TODO: investigate suggested quality improvement trick in more detail:
// Kawase5: 0, 1, 2, 2, 3
// Kawase9: 0, 1, 2, 3, 4, 4, 5, 6, 7
// ? -> KawaseN: duplicate pass at N/2 (See, Bandwidth-Efficient Rendering, siggraph2015)
float kawaseDist = 0.5f + ((i > mipCount / 2) ? (i - 1) : i);
Vector4 params2 = newParams.parameters2;
params2.x = kawaseDist;
materialPyramid.SetVector(ShaderConstants._Params2, params2);
}
m_PrevParams = newParams;
}
}
switch (bloom.filter.value)
{
case BloomFilterMode.Dual:
resourceData.bloom = BloomDual(renderGraph, sourceTexture);
break;
case BloomFilterMode.Kawase:
resourceData.bloom = BloomKawase(renderGraph, sourceTexture);
break;
case BloomFilterMode.Gaussian: goto default;
default:
resourceData.bloom = BloomGaussian(renderGraph, sourceTexture);
break;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static public Vector2Int CalcBloomResolution(Bloom bloom, in TextureDesc bloomSourceDesc)
{
// Start at half-res
int downres = 1;
switch (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, bloomSourceDesc.width >> downres);
int th = Mathf.Max(1, bloomSourceDesc.height >> downres);
return new Vector2Int(tw, th);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static public int CalcBloomMipCount(Bloom bloom, in Vector2Int bloomResolution)
{
// Determine the iteration count
int maxSize = Mathf.Max(bloomResolution.x, bloomResolution.y);
int iterations = Mathf.FloorToInt(Mathf.Log(maxSize, 2f) - 1);
int mipCount = Mathf.Clamp(iterations, 1, bloom.maxIterations.value);
return mipCount;
}
TextureHandle BloomGaussian(RenderGraph renderGraph, in TextureHandle source)
{
using (var builder = renderGraph.AddUnsafePass<BloomPassData>(passName, out var passData, profilingSampler))
{
passData.sourceTexture = source;
passData.material = m_Material;
passData.mipMaterials = m_MaterialPyramid;
passData.mipPyramid = m_MipPyramid;
int mipCount = m_MipPyramid.mipCount;
builder.UseTexture(source, AccessFlags.Read);
for (int i = 0; i < mipCount; i++)
{
builder.UseTexture(m_MipPyramid.mipDownTextures[i], AccessFlags.ReadWrite);
builder.UseTexture(m_MipPyramid.mipUpTextures[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.mipPyramid.mipCount;
TextureHandle[] mipDownTextures = data.mipPyramid.mipDownTextures;
TextureHandle[] mipUpTextures = data.mipPyramid.mipUpTextures;
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, URPProfilingSamplers.BloomPrefilter))
{
Blitter.BlitCameraTexture(cmd, data.sourceTexture, mipDownTextures[0], loadAction,
storeAction, material, ShaderPass.k_Prefilter);
}
// 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, URPProfilingSamplers.BloomDownsample))
{
TextureHandle lastDown = mipDownTextures[0];
for (int i = 1; i < mipCount; i++)
{
TextureHandle mipDown = mipDownTextures[i];
TextureHandle mipUp = mipUpTextures[i];
Blitter.BlitCameraTexture(cmd, lastDown, mipUp, loadAction, storeAction, material, ShaderPass.k_BlurHorizontal);
Blitter.BlitCameraTexture(cmd, mipUp, mipDown, loadAction, storeAction, material, ShaderPass.k_BlurVertical);
lastDown = mipDown;
}
}
using (new ProfilingScope(cmd, URPProfilingSamplers.BloomUpsample))
{
// Upsample (bilinear by default, HQ filtering does bicubic instead
for (int i = mipCount - 2; i >= 0; i--)
{
TextureHandle lowMip =
(i == mipCount - 2) ? mipDownTextures[i + 1] : mipUpTextures[i + 1];
TextureHandle highMip = mipDownTextures[i];
TextureHandle dst = mipUpTextures[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.mipMaterials[i];
upMaterial.SetTexture(ShaderConstants._SourceTexLowMip, lowMip);
Blitter.BlitCameraTexture(cmd, highMip, dst, loadAction, storeAction, upMaterial, ShaderPass.k_Upsample);
}
}
});
// 1st mip is the prefilter.
m_MipPyramid.resultTexture = mipCount == 1 ? m_MipPyramid.mipDownTextures[0] : m_MipPyramid.mipUpTextures[0];
return m_MipPyramid.resultTexture;
}
}
TextureHandle BloomKawase(RenderGraph renderGraph, in TextureHandle source)
{
using (var builder = renderGraph.AddUnsafePass<BloomPassData>(k_PassNameKawase, out var passData, k_ProfilingSamplerKawase))
{
passData.sourceTexture = source;
passData.material = m_Material;
passData.mipMaterials = m_MaterialPyramid;
passData.mipPyramid = m_MipPyramid;
int mipCount = m_MipPyramid.mipCount;
builder.UseTexture(source, AccessFlags.Read);
builder.UseTexture(m_MipPyramid.mipDownTextures[0], AccessFlags.ReadWrite);
builder.UseTexture(m_MipPyramid.mipUpTextures[0], 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.mipPyramid.mipCount;
TextureHandle[] mipDownTextures = data.mipPyramid.mipDownTextures;
TextureHandle[] mipUpTextures = data.mipPyramid.mipUpTextures;
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, URPProfilingSamplers.BloomPrefilter))
{
Blitter.BlitCameraTexture(cmd, data.sourceTexture, mipDownTextures[0], loadAction, storeAction, material, ShaderPass.k_Prefilter);
}
// Kawase blur passes
using(new ProfilingScope(cmd, URPProfilingSamplers.BloomDownsample))
{
for (int i = 0; i < mipCount; i++)
{
TextureHandle src = ((i & 1) == 0) ? mipDownTextures[0] : mipUpTextures[0];
TextureHandle dst = ((i & 1) == 0) ? mipUpTextures[0] : mipDownTextures[0];
Material mat = data.mipMaterials[i];
Blitter.BlitCameraTexture(cmd, src, dst, loadAction, storeAction, mat, ShaderPass.k_Kawase);
}
}
});
m_MipPyramid.resultTexture = (((mipCount - 1) & 1) == 0) ? m_MipPyramid.mipUpTextures[0] : m_MipPyramid.mipDownTextures[0];
return m_MipPyramid.resultTexture;
}
}
// Dual Filter, Bandwidth-Efficient Rendering, siggraph2015
TextureHandle BloomDual(RenderGraph renderGraph, in TextureHandle source)
{
using (var builder = renderGraph.AddUnsafePass<BloomPassData>(k_PassNameDual, out var passData, k_ProfilingSamplerDual))
{
passData.sourceTexture = source;
passData.material = m_Material;
passData.mipMaterials = m_MaterialPyramid;
passData.mipPyramid = m_MipPyramid;
int mipCount = m_MipPyramid.mipCount;
builder.UseTexture(source, AccessFlags.Read);
for (int i = 0; i < mipCount; i++)
{
builder.UseTexture(m_MipPyramid.mipDownTextures[i], AccessFlags.ReadWrite);
builder.UseTexture(m_MipPyramid.mipUpTextures[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.mipPyramid.mipCount;
TextureHandle[] mipDownTextures = data.mipPyramid.mipDownTextures;
TextureHandle[] mipUpTextures = data.mipPyramid.mipUpTextures;
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, URPProfilingSamplers.BloomPrefilter))
{
Blitter.BlitCameraTexture(cmd, data.sourceTexture, mipDownTextures[0], loadAction, storeAction, material, ShaderPass.k_Prefilter);
}
// ARM: Bandwidth-Efficient Rendering, siggraph2015
// Downsample - dual pyramid, fixed Kawase0 blur on shrinking targets.
using(new ProfilingScope(cmd, URPProfilingSamplers.BloomDownsample))
{
TextureHandle lastDown = mipDownTextures[0];
for (int i = 1; i < mipCount; i++)
{
TextureHandle src = mipDownTextures[i - 1];
TextureHandle dst = mipDownTextures[i];
Blitter.BlitCameraTexture(cmd, src, dst, loadAction, storeAction, material, ShaderPass.k_DualDownsample);
}
}
using (new ProfilingScope(cmd, URPProfilingSamplers.BloomUpsample))
{
for (int i = mipCount - 2; i >= 0; i--)
{
TextureHandle src = (i == mipCount - 2) ? mipDownTextures[i + 1] : mipUpTextures[i + 1];
TextureHandle dst = mipUpTextures[i];
Blitter.BlitCameraTexture(cmd, src, dst, loadAction, storeAction, material, ShaderPass.k_DualUpsample);
}
}
});
// 1st mip is the prefilter.
m_MipPyramid.resultTexture = mipCount == 1 ? m_MipPyramid.mipDownTextures[0] : m_MipPyramid.mipUpTextures[0];
return m_MipPyramid.resultTexture;
}
}
// Helper class to communicate bloom mip results with external passes in a consistent way.
public class BloomMipPyramid
{
int m_MipCount;
TextureHandle[] m_MipDownPyramidTextures;
TextureHandle[] m_MipUpPyramidTextures;
string[] m_MipDownPyramidNames;
string[] m_MipUpPyramidNames;
BloomFilterMode m_BloomFilterMode;
public int mipCapacity => m_MipDownPyramidTextures.Length;
public int mipCount => m_MipCount;
public TextureHandle resultTexture { get; internal set; }
public TextureHandle GetResultMip(int index)
{
if (m_BloomFilterMode == BloomFilterMode.Kawase)
return resultTexture; // No mips.
if (mipCount == 1)
return mipDownTextures[0]; // Prefilter only.
int i = Mathf.Max(Mathf.Min(index, mipCount - 1), 0);
return mipUpTextures[i]; // Upsampled results.
}
internal TextureHandle[] mipDownTextures => m_MipDownPyramidTextures;
internal TextureHandle[] mipUpTextures => m_MipUpPyramidTextures;
internal BloomMipPyramid(int size = k_MaxPyramidSize)
{
// Arrays for Bloom pyramid TextureHandle names.
m_MipDownPyramidNames = new string[size];
m_MipUpPyramidNames = new string[size];
for (int i = 0; i < size; i++)
{
m_MipDownPyramidNames[i] = "_BloomMipDown" + i;
m_MipUpPyramidNames[i] = "_BloomMipUp" + i;
}
// Arrays for Bloom pyramid TextureHandles.
m_MipDownPyramidTextures = new TextureHandle[size];
m_MipUpPyramidTextures = new TextureHandle[size];
}
// Create bloom mip pyramid textures
internal void Update(RenderGraph renderGraph, Bloom bloom, in TextureDesc bloomSourceDesc)
{
Vector2Int bloomResolution = CalcBloomResolution(bloom, in bloomSourceDesc);
m_MipCount = CalcBloomMipCount(bloom, in bloomResolution);
int tw = bloomResolution.x;
int th = bloomResolution.y;
var desc = PostProcessUtils.GetCompatibleDescriptor(bloomSourceDesc, tw, th, bloomSourceDesc.colorFormat);
m_MipDownPyramidTextures[0] = PostProcessUtils.CreateCompatibleTexture(renderGraph, desc, m_MipDownPyramidNames[0], false, FilterMode.Bilinear);
m_MipUpPyramidTextures[0] = PostProcessUtils.CreateCompatibleTexture(renderGraph, desc, m_MipUpPyramidNames[0], false, FilterMode.Bilinear);
m_BloomFilterMode = bloom.filter.value;
if (m_BloomFilterMode != BloomFilterMode.Kawase)
{
for (int i = 1; i < mipCount; i++)
{
tw = Mathf.Max(1, tw >> 1);
th = Mathf.Max(1, th >> 1);
ref TextureHandle mipDown = ref m_MipDownPyramidTextures[i];
ref TextureHandle mipUp = ref m_MipUpPyramidTextures[i];
desc.width = tw;
desc.height = th;
mipDown = PostProcessUtils.CreateCompatibleTexture(renderGraph, desc, m_MipDownPyramidNames[i], false, FilterMode.Bilinear);
mipUp = PostProcessUtils.CreateCompatibleTexture(renderGraph, desc, m_MipUpPyramidNames[i], false, FilterMode.Bilinear);
}
}
}
}
// Cache params to avoid setting each property in each material every frame.
internal struct MaterialParams
{
internal Vector4 parameters;
internal Vector4 parameters2;
internal BloomFilterMode bloomFilter;
internal bool highQualityFiltering;
internal bool enableAlphaOutput;
internal bool Equals(ref MaterialParams other)
{
return parameters == other.parameters &&
parameters2 == other.parameters2 &&
highQualityFiltering == other.highQualityFiltering &&
enableAlphaOutput == other.enableAlphaOutput &&
bloomFilter == other.bloomFilter;
}
}
// Precomputed shader ids to same some CPU cycles (mostly affects mobile)
public static class ShaderConstants
{
public static readonly int _Params = Shader.PropertyToID("_Params");
public static readonly int _Params2 = Shader.PropertyToID("_Params2");
public static readonly int _SourceTexLowMip = Shader.PropertyToID("_SourceTexLowMip");
}
public static class ShaderPass
{
public const int k_Prefilter = 0;
public const int k_BlurHorizontal = 1;
public const int k_BlurVertical = 2;
public const int k_Upsample = 3;
public const int k_Kawase = 4;
public const int k_DualDownsample = 5;
public const int k_DualUpsample = 6;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4d4f645458af4b849672d0ccaae85173
timeCreated: 1749306894
@@ -0,0 +1,285 @@
using System;
using UnityEngine.Rendering.RenderGraphModule;
using System.Runtime.CompilerServices; // AggressiveInlining
namespace UnityEngine.Rendering.Universal
{
internal sealed class DepthOfFieldBokehPostProcessPass : PostProcessPass
{
public const string k_TargetName = "CameraColorDepthOfFieldBokeh";
const int k_DownSample = 2;
Material m_Material;
bool m_IsValid;
Vector4[] m_BokehKernel;
int m_BokehHash;
// Needed if the device changes its render target width/height (ex, Mobile platform allows change of orientation)
float m_BokehMaxRadius;
float m_BokehRcpAspect;
public DepthOfFieldBokehPostProcessPass(Shader shader)
{
this.renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing - 1;
this.profilingSampler = URPProfilingSamplers.BokehDepthOfField;
m_Material = PostProcessUtils.LoadShader(shader, passName, logLevel: LogType.Log);
m_IsValid = m_Material != null;
}
public override void Dispose()
{
CoreUtils.Destroy(m_Material);
m_IsValid = false;
}
private class DoFBokehPassData
{
internal Material material;
// Inputs
internal TextureHandle sourceTexture;
internal TextureHandle depthTexture;
// Pass textures
internal TextureHandle halfCoCTexture;
internal TextureHandle fullCoCTexture;
internal TextureHandle pingTexture;
internal TextureHandle pongTexture;
// Output texture
internal TextureHandle destinationTexture;
// Setup
internal Vector4[] bokehKernel;
internal Vector4 cocParams;
internal int downSample;
internal float uvMargin;
internal bool useFastSRGBLinearConversion;
internal bool enableAlphaOutput;
};
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
if (!m_IsValid)
return;
var depthOfField = volumeStack.GetComponent<DepthOfField>();
if (!depthOfField.IsActive() || depthOfField.mode.value != DepthOfFieldMode.Bokeh)
return;
var cameraData = frameData.Get<UniversalCameraData>();
if (cameraData.isSceneViewCamera)
return;
var postProcessingData = frameData.Get<UniversalPostProcessingData>();
var resourceData = frameData.Get<UniversalResourceData>();
var sourceTexture = resourceData.cameraColor;
var destinationTexture = PostProcessUtils.CreateCompatibleTexture(renderGraph, sourceTexture, k_TargetName, true, FilterMode.Bilinear);
var srcDesc = sourceTexture.GetDescriptor(renderGraph);
int wh = srcDesc.width / k_DownSample;
int hh = srcDesc.height / k_DownSample;
// Pass Textures
var fullCoCTextureDesc = PostProcessUtils.GetCompatibleDescriptor(srcDesc, srcDesc.width, srcDesc.height, Experimental.Rendering.GraphicsFormat.R8_UNorm);
var fullCoCTexture = PostProcessUtils.CreateCompatibleTexture(renderGraph, fullCoCTextureDesc, "_FullCoCTexture", true, FilterMode.Bilinear);
var pingTextureDesc = PostProcessUtils.GetCompatibleDescriptor(srcDesc, wh, hh, Experimental.Rendering.GraphicsFormat.R16G16B16A16_SFloat);
var pingTexture = PostProcessUtils.CreateCompatibleTexture(renderGraph, pingTextureDesc, "_PingTexture", true, FilterMode.Bilinear);
var pongTextureDesc = PostProcessUtils.GetCompatibleDescriptor(srcDesc, wh, hh, Experimental.Rendering.GraphicsFormat.R16G16B16A16_SFloat);
var pongTexture = PostProcessUtils.CreateCompatibleTexture(renderGraph, pongTextureDesc, "_PongTexture", true, FilterMode.Bilinear);
using (var builder = renderGraph.AddUnsafePass<DoFBokehPassData>(passName, out var passData, profilingSampler))
{
// Setup
// "A Lens and Aperture Camera Model for Synthetic Image Generation" [Potmesil81]
float F = depthOfField.focalLength.value / 1000f;
float A = depthOfField.focalLength.value / depthOfField.aperture.value;
float P = 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 = depthOfField.GetHashCode();
if (hash != m_BokehHash || maxRadius != m_BokehMaxRadius || rcpAspect != m_BokehRcpAspect)
{
m_BokehHash = hash;
m_BokehMaxRadius = maxRadius;
m_BokehRcpAspect = rcpAspect;
PrepareBokehKernel( ref m_BokehKernel,
depthOfField.bladeCount.value,
depthOfField.bladeCurvature.value,
depthOfField.bladeRotation.value,
maxRadius, rcpAspect);
}
float uvMargin = (1.0f / srcDesc.height) * k_DownSample;
passData.bokehKernel = m_BokehKernel;
passData.downSample = k_DownSample;
passData.uvMargin = uvMargin;
passData.cocParams = new Vector4(P, maxCoC, maxRadius, rcpAspect);
passData.useFastSRGBLinearConversion = postProcessingData.useFastSRGBLinearConversion;
passData.enableAlphaOutput = cameraData.isAlphaOutputEnabled;
// Inputs
passData.sourceTexture = sourceTexture;
builder.UseTexture(sourceTexture, AccessFlags.Read);
passData.depthTexture = resourceData.cameraDepthTexture;
builder.UseTexture(resourceData.cameraDepthTexture, AccessFlags.Read);
passData.material = m_Material;
// 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.destinationTexture = destinationTexture;
builder.UseTexture(destinationTexture, AccessFlags.Write);
// TODO RENDERGRAPH: properly setup dependencies between passes
builder.SetRenderFunc(static (DoFBokehPassData data, UnsafeGraphContext context) =>
{
var dofMat = data.material;
var cmd = CommandBufferHelpers.GetNativeCommandBuffer(context.cmd);
RTHandle sourceTextureHdl = data.sourceTexture;
RTHandle dst = data.destinationTexture;
// Setup
using (new ProfilingScope(URPProfilingSamplers.SetupDoF))
{
Vector4 sourceSize = PostProcessUtils.CalcShaderSourceSize(data.sourceTexture);
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));
dofMat.SetVector(ShaderConstants._SourceSize, sourceSize);
CoreUtils.SetKeyword(dofMat, ShaderKeywordStrings.UseFastSRGBLinearConversion, data.useFastSRGBLinearConversion);
CoreUtils.SetKeyword(dofMat, ShaderKeywordStrings._ENABLE_ALPHA_OUTPUT, data.enableAlphaOutput);
}
// Compute CoC
using (new ProfilingScope(URPProfilingSamplers.DOFComputeCOC))
{
dofMat.SetTexture(ShaderConstants._CameraDepthTextureID, data.depthTexture);
Blitter.BlitCameraTexture(cmd, sourceTextureHdl, data.fullCoCTexture, dofMat, ShaderPass.k_ComputeCoc);
}
// Downscale and Prefilter Color + CoC
using (new ProfilingScope(URPProfilingSamplers.DOFDownscalePrefilter))
{
dofMat.SetTexture(ShaderConstants._FullCoCTexture, data.fullCoCTexture);
Blitter.BlitCameraTexture(cmd, sourceTextureHdl, data.pingTexture, dofMat, ShaderPass.k_DownscalePrefilter);
}
// Blur
using (new ProfilingScope(URPProfilingSamplers.DOFBlurBokeh))
{
Blitter.BlitCameraTexture(cmd, data.pingTexture, data.pongTexture, dofMat, ShaderPass.k_Blur);
}
// Post Filtering
using (new ProfilingScope(URPProfilingSamplers.DOFPostFilter))
{
Blitter.BlitCameraTexture(cmd, data.pongTexture, data.pingTexture, dofMat, ShaderPass.k_PostFilter);
}
// Composite
using (new ProfilingScope(URPProfilingSamplers.DOFComposite))
{
dofMat.SetTexture(ShaderConstants._DofTexture, data.pingTexture);
Blitter.BlitCameraTexture(cmd, sourceTextureHdl, dst, dofMat, ShaderPass.k_Composite);
}
});
}
resourceData.cameraColor = destinationTexture;
}
static void PrepareBokehKernel(ref Vector4[] bokehKernel, int bladeCount, float bladeCurvature, float bladeRotation, float maxRadius, float rcpAspect)
{
const int kRings = 4;
const int kPointsPerRing = 7;
// Check the existing array
if (bokehKernel == null)
bokehKernel = new Vector4[42];
// Fill in sample points (concentric circles transformed to rotated N-Gon)
int idx = 0;
float bladeCountf = bladeCount;
float curvature = 1f - bladeCurvature;
float rotation = bladeRotation * Mathf.Deg2Rad;
const float PI = Mathf.PI;
const float TWO_PI = Mathf.PI * 2f;
for (int ring = 1; ring < kRings; ring++)
{
float bias = 1f / kPointsPerRing;
float radius = (ring + bias) / (kRings - 1f + bias);
int points = ring * kPointsPerRing;
for (int point = 0; point < points; point++)
{
// Angle on ring
float phi = 2f * PI * point / points;
// Transform to rotated N-Gon
// Adapted from "CryEngine 3 Graphics Gems" [Sousa13]
float nt = Mathf.Cos(PI / bladeCountf);
float dt = Mathf.Cos(phi - (TWO_PI / bladeCountf) * Mathf.Floor((bladeCountf * phi + Mathf.PI) / TWO_PI));
float r = radius * Mathf.Pow(nt / dt, curvature);
float u = r * Mathf.Cos(phi - rotation);
float v = r * Mathf.Sin(phi - rotation);
float uRadius = u * maxRadius;
float vRadius = v * maxRadius;
float uRadiusPowTwo = uRadius * uRadius;
float vRadiusPowTwo = vRadius * vRadius;
float kernelLength = Mathf.Sqrt((uRadiusPowTwo + vRadiusPowTwo));
float uRCP = uRadius * rcpAspect;
bokehKernel[idx] = new Vector4(uRadius, vRadius, kernelLength, uRCP);
idx++;
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float GetMaxBokehRadiusInPixels(float viewportHeight)
{
// Estimate the maximum radius of bokeh (empirically derived from the ring count)
const float kRadiusInPixels = 14f;
return Mathf.Min(0.05f, kRadiusInPixels / viewportHeight);
}
// Precomputed shader ids to same some CPU cycles (mostly affects mobile)
public static class ShaderConstants
{
public static readonly int _CameraDepthTextureID = Shader.PropertyToID("_CameraDepthTexture");
public static readonly int _SourceSize = Shader.PropertyToID("_SourceSize");
public static readonly int _FullCoCTexture = Shader.PropertyToID("_FullCoCTexture");
public static readonly int _DofTexture = Shader.PropertyToID("_DofTexture");
public static readonly int _CoCParams = Shader.PropertyToID("_CoCParams");
public static readonly int _BokehKernel = Shader.PropertyToID("_BokehKernel");
public static readonly int _BokehConstants = Shader.PropertyToID("_BokehConstants");
public static readonly int _DownSampleScaleFactor = Shader.PropertyToID("_DownSampleScaleFactor");
}
public static class ShaderPass
{
public const int k_ComputeCoc = 0;
public const int k_DownscalePrefilter = 1;
public const int k_Blur = 2;
public const int k_PostFilter = 3;
public const int k_Composite = 4;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 5e0f824bccf304ad2ad63266bf1a602e
@@ -0,0 +1,247 @@
using System;
using System.Collections.Generic;
using UnityEngine.Rendering.RenderGraphModule;
using System.Runtime.CompilerServices; // AggressiveInlining
namespace UnityEngine.Rendering.Universal
{
internal sealed class DepthOfFieldGaussianPostProcessPass : PostProcessPass
{
public const string k_TargetName = "CameraColorDepthOfFieldGaussian";
const int k_DownSample = 2;
Material m_Material;
Material m_MaterialCoc;
bool m_IsValid;
Experimental.Rendering.GraphicsFormat m_CoCFormat;
public DepthOfFieldGaussianPostProcessPass(Shader shader)
{
this.renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing - 1;
this.profilingSampler = URPProfilingSamplers.GaussianDepthOfField;
m_Material = PostProcessUtils.LoadShader(shader, passName, logLevel: LogType.Log);
m_MaterialCoc = PostProcessUtils.LoadShader(shader, passName, logLevel: LogType.Log);
m_IsValid = m_Material != null && m_MaterialCoc != null;
// Depth of Field
//
// CoC
// UUM-41070: We require `Linear | Render` but with the deprecated FormatUsage this was checking `Blend`
// For now, we keep checking for `Blend` until the performance hit of doing the correct checks is evaluated
if (SystemInfo.IsFormatSupported(Experimental.Rendering.GraphicsFormat.R16_UNorm, Experimental.Rendering.GraphicsFormatUsage.Blend))
m_CoCFormat = Experimental.Rendering.GraphicsFormat.R16_UNorm;
else if (SystemInfo.IsFormatSupported(Experimental.Rendering.GraphicsFormat.R16_SFloat, Experimental.Rendering.GraphicsFormatUsage.Blend))
m_CoCFormat = Experimental.Rendering.GraphicsFormat.R16_SFloat;
else // Expect CoC banding
m_CoCFormat = Experimental.Rendering.GraphicsFormat.R8_UNorm;
}
public override void Dispose()
{
CoreUtils.Destroy(m_Material);
CoreUtils.Destroy(m_MaterialCoc);
m_IsValid = false;
}
private class DoFGaussianPassData
{
// Inputs
internal Material material;
internal Material materialCoC;
internal TextureHandle sourceTexture;
internal TextureHandle depthTexture;
// 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;
// Setup
internal Vector3 cocParams;
internal int downsample;
internal bool highQualitySamplingValue;
internal bool enableAlphaOutput;
};
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
if (!m_IsValid)
return;
var depthOfField = volumeStack.GetComponent<DepthOfField>();
if (!depthOfField.IsActive() || depthOfField.mode.value != DepthOfFieldMode.Gaussian)
return;
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
if (cameraData.isSceneViewCamera)
return;
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
var sourceTexture = resourceData.cameraColor;
var destinationTexture = PostProcessUtils.CreateCompatibleTexture(renderGraph, sourceTexture, k_TargetName, true, FilterMode.Bilinear);
var srcDesc = sourceTexture.GetDescriptor(renderGraph);
var colorFormat = srcDesc.colorFormat;
int wh = srcDesc.width / k_DownSample;
int hh = srcDesc.height / k_DownSample;
// Pass Textures
var fullCoCTextureDesc = PostProcessUtils.GetCompatibleDescriptor(srcDesc, srcDesc.width, srcDesc.height, m_CoCFormat);
var fullCoCTexture = PostProcessUtils.CreateCompatibleTexture(renderGraph, fullCoCTextureDesc, "_FullCoCTexture", true, FilterMode.Bilinear);
var halfCoCTextureDesc = PostProcessUtils.GetCompatibleDescriptor(srcDesc, wh, hh, m_CoCFormat);
var halfCoCTexture = PostProcessUtils.CreateCompatibleTexture(renderGraph, halfCoCTextureDesc, "_HalfCoCTexture", true, FilterMode.Bilinear);
var pingTextureDesc = PostProcessUtils.GetCompatibleDescriptor(srcDesc, wh, hh, colorFormat);
var pingTexture = PostProcessUtils.CreateCompatibleTexture(renderGraph, pingTextureDesc, "_PingTexture", true, FilterMode.Bilinear);
var pongTextureDesc = PostProcessUtils.GetCompatibleDescriptor(srcDesc, wh, hh, colorFormat);
var pongTexture = PostProcessUtils.CreateCompatibleTexture(renderGraph, pongTextureDesc, "_PongTexture", true, FilterMode.Bilinear);
using (var builder = renderGraph.AddUnsafePass<DoFGaussianPassData>(passName, out var passData, profilingSampler))
{
// Setup
float farStart = depthOfField.gaussianStart.value;
float farEnd = Mathf.Max(farStart, 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 = depthOfField.gaussianMaxRadius.value * (wh / 1080f);
maxRadius = Mathf.Min(maxRadius, 2f);
passData.downsample = k_DownSample;
passData.cocParams = new Vector3(farStart, farEnd, maxRadius);
passData.highQualitySamplingValue = depthOfField.highQualitySampling.value;
passData.enableAlphaOutput = cameraData.isAlphaOutputEnabled;
passData.material = m_Material;
passData.materialCoC = m_MaterialCoc;
// Inputs
passData.sourceTexture = sourceTexture;
builder.UseTexture(sourceTexture, 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 = destinationTexture;
builder.UseTexture(destinationTexture, AccessFlags.Write);
builder.SetRenderFunc(static (DoFGaussianPassData data, UnsafeGraphContext context) =>
{
var dofMat = data.material;
var dofMatCoC = data.materialCoC; // TODO: is materialCoC needed here? It's setup the same way. Can we use the same material?
var cmd = CommandBufferHelpers.GetNativeCommandBuffer(context.cmd);
RTHandle sourceTextureHdl = data.sourceTexture;
RTHandle dstHdl = data.destination;
// Setup Materials
using (new ProfilingScope(URPProfilingSamplers.SetupDoF))
{
// Dof material
Vector4 sourceSize = PostProcessUtils.CalcShaderSourceSize(data.sourceTexture);
dofMat.SetVector(ShaderConstants._CoCParams, data.cocParams);
dofMat.SetVector(ShaderConstants._DownSampleScaleFactor, new Vector4(1.0f / data.downsample, 1.0f / data.downsample, data.downsample, data.downsample));
dofMat.SetVector(ShaderConstants._SourceSize, sourceSize);
// PostProcessUtils.SetGlobalShaderSourceSize(cmd, data.sourceTexture);
CoreUtils.SetKeyword(dofMat, ShaderKeywordStrings.HighQualitySampling, data.highQualitySamplingValue);
CoreUtils.SetKeyword(dofMat, ShaderKeywordStrings._ENABLE_ALPHA_OUTPUT, data.enableAlphaOutput);
// Dof CoC material
dofMatCoC.SetVector(ShaderConstants._CoCParams, data.cocParams);
dofMatCoC.SetVector(ShaderConstants._SourceSize, sourceSize);
CoreUtils.SetKeyword(dofMatCoC, ShaderKeywordStrings.HighQualitySampling, data.highQualitySamplingValue);
}
// Compute CoC
using (new ProfilingScope(URPProfilingSamplers.DOFComputeCOC))
{
dofMatCoC.SetTexture(ShaderConstants._CameraDepthTextureID, data.depthTexture);
Blitter.BlitCameraTexture(cmd, data.sourceTexture, data.fullCoCTexture, dofMatCoC, ShaderPass.k_ComputeCoc);
}
// Downscale & prefilter color + CoC
using (new ProfilingScope(URPProfilingSamplers.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, ShaderPass.k_DownscalePrefilter);
}
// Blur H
using (new ProfilingScope(URPProfilingSamplers.DOFBlurH))
{
dofMat.SetTexture(ShaderConstants._HalfCoCTexture, data.halfCoCTexture);
Blitter.BlitCameraTexture(cmd, data.pingTexture, data.pongTexture, dofMat, ShaderPass.k_BlurH);
}
// Blur V
using (new ProfilingScope(URPProfilingSamplers.DOFBlurV))
{
Blitter.BlitCameraTexture(cmd, data.pongTexture, data.pingTexture, dofMat, ShaderPass.k_BlurV);
}
// Composite
using (new ProfilingScope(URPProfilingSamplers.DOFComposite))
{
dofMat.SetTexture(ShaderConstants._ColorTexture, data.pingTexture);
dofMat.SetTexture(ShaderConstants._FullCoCTexture, data.fullCoCTexture);
Blitter.BlitCameraTexture(cmd, sourceTextureHdl, dstHdl, dofMat, ShaderPass.k_Composite);
}
});
}
resourceData.cameraColor = destinationTexture;
}
// Precomputed shader ids to same some CPU cycles (mostly affects mobile)
public static class ShaderConstants
{
public static readonly int _ColorTexture = Shader.PropertyToID("_ColorTexture");
public static readonly int _CameraDepthTextureID = Shader.PropertyToID("_CameraDepthTexture");
public static readonly int _SourceSize = Shader.PropertyToID("_SourceSize");
public static readonly int _DownSampleScaleFactor = Shader.PropertyToID("_DownSampleScaleFactor");
public static readonly int _CoCParams = Shader.PropertyToID("_CoCParams");
public static readonly int _FullCoCTexture = Shader.PropertyToID("_FullCoCTexture");
public static readonly int _HalfCoCTexture = Shader.PropertyToID("_HalfCoCTexture");
}
public static class ShaderPass
{
public const int k_ComputeCoc = 0;
public const int k_DownscalePrefilter = 1;
public const int k_BlurH = 2;
public const int k_BlurV = 3;
public const int k_Composite = 4;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 14a1338d3233f42aca614647651477ee
@@ -0,0 +1,240 @@
using System;
using Unity.Profiling.LowLevel;
using UnityEngine.Rendering.RenderGraphModule;
using System.Runtime.CompilerServices; // AggressiveInlining
namespace UnityEngine.Rendering.Universal
{
internal sealed class FinalPostProcessPass : PostProcessPass
{
/// <summary>Blits the final post-processed image to the camera target, applying FXAA, upscaling, or format conversion as the last rendering step.</summary>
static readonly ProfilingSampler k_ProfilingSampler = ProfilingSampler.Create("Blit Final Post Processing", MarkerFlags.Default);
Material m_Material;
Texture2D[] m_FilmGrainTextures;
bool m_IsValid;
public enum FilteringOperation
{
Linear,
Point,
TaaSharpening,
FsrSharpening
}
Texture m_DitherTexture;
FilteringOperation m_FilteringOperation;
HDROutputUtils.Operation m_HdrOperations;
bool m_ApplySrgbEncoding;
bool m_ApplyFxaa; // NOTE: This is used to communicate if FXAA is already done in the previous pass.
bool m_RenderOverlayUI;
public FinalPostProcessPass(Shader shader, Texture2D[] filmGrainTextures)
{
this.renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing - 1;
this.profilingSampler = k_ProfilingSampler;
m_Material = PostProcessUtils.LoadShader(shader, passName);
m_IsValid = m_Material != null;
m_FilmGrainTextures = filmGrainTextures;
// Defaults
m_FilteringOperation = FilteringOperation.Linear; // Common case.
m_HdrOperations = HDROutputUtils.Operation.None; // HDR disabled by default.
m_ApplySrgbEncoding = false; // sRGB conversion is typically automatic based on format.
m_ApplyFxaa = true; // Upscale disabled by default. Apply FXAA in final pass.
m_RenderOverlayUI = false; // HDR disabled by default. HDR only.
}
public override void Dispose()
{
CoreUtils.Destroy(m_Material);
m_IsValid = false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Setup(Texture ditherTexture, FilteringOperation filteringOperation, HDROutputUtils.Operation hdrOperations, bool applySrgbEncoding, bool applyFxaa, bool renderOverlayUI)
{
m_DitherTexture = ditherTexture;
m_FilteringOperation = filteringOperation;
m_HdrOperations = hdrOperations;
m_ApplySrgbEncoding = applySrgbEncoding;
m_ApplyFxaa = applyFxaa;
m_RenderOverlayUI = renderOverlayUI;
}
private class PostProcessingFinalBlitPassData
{
internal TextureHandle destinationTexture;
internal TextureHandle sourceTexture;
internal Material material;
internal UniversalCameraData cameraData;
internal Tonemapping tonemapping;
internal FilteringOperation filteringOperation;
internal HDROutputUtils.Operation hdrOperations;
internal UberPostProcessPass.FilmGrainParams filmGrain;
internal UberPostProcessPass.DitheringParams dithering;
internal bool applySrgbEncoding;
internal bool applyFxaa;
}
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
if (!m_IsValid)
return;
var tonemapping = volumeStack.GetComponent<Tonemapping>();
var filmGrain = volumeStack.GetComponent<FilmGrain>();
var cameraData = frameData.Get<UniversalCameraData>();
var resourceData = frameData.Get<UniversalResourceData>();
var sourceTexture = resourceData.cameraColor;
var destinationTexture = resourceData.backBufferColor; //By definition this pass blits to the backbuffer
// Final blit pass
using (var builder = renderGraph.AddRasterRenderPass<PostProcessingFinalBlitPassData>(passName, out var passData, profilingSampler))
{
// FSR and RCAS use global state constants.
builder.AllowGlobalStateModification(true);
passData.destinationTexture = destinationTexture;
builder.SetRenderAttachment(destinationTexture, 0, AccessFlags.Write);
passData.sourceTexture = sourceTexture;
builder.UseTexture(sourceTexture, AccessFlags.Read);
// NOTE: Global texture. 'overlayUITexture' is bound by the DrawOffscreenUIPass pass if needed. Not in FinalPostProcessPass.
if (m_RenderOverlayUI)
builder.UseTexture(resourceData.overlayUITexture, AccessFlags.Read);
passData.material = m_Material;
passData.cameraData = cameraData;
passData.tonemapping = tonemapping;
passData.filteringOperation = m_FilteringOperation;
passData.hdrOperations = m_HdrOperations;
passData.filmGrain.Setup(filmGrain, m_FilmGrainTextures, cameraData.pixelWidth, cameraData.pixelHeight);
passData.dithering.Setup(m_DitherTexture, cameraData.pixelWidth, cameraData.pixelHeight);
passData.applySrgbEncoding = m_ApplySrgbEncoding;
passData.applyFxaa = m_ApplyFxaa;
#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 = !Experimental.Rendering.XRSystem.foveatedRenderingCaps.HasFlag(FoveatedRenderingCaps.NonUniformRaster);
builder.EnableFoveatedRasterization(cameraData.xr.supportsFoveatedRendering && passSupportsFoveation);
// Multiview render regions are incompatible with the inner (foveal) pass in Quad View
if (!cameraData.xr.isQuadViewInnerPass)
{
builder.SetExtendedFeatureFlags(ExtendedFeatureFlags.MultiviewRenderRegionsCompatible);
}
}
#endif
builder.SetRenderFunc(static (PostProcessingFinalBlitPassData data, RasterGraphContext context) =>
{
var cmd = context.cmd;
var material = data.material;
var cameraData = data.cameraData;
var filteringOperation = data.filteringOperation;
var requireHDROutput = PostProcessUtils.RequireHDROutput(cameraData);
var isAlphaOutputEnabled = cameraData.isAlphaOutputEnabled;
var applyFxaa = data.applyFxaa;
var hdrColorEncoding = data.hdrOperations.HasFlag(HDROutputUtils.Operation.ColorEncoding);
var applySrgbEncoding = data.applySrgbEncoding;
RTHandle sourceTextureHdl = data.sourceTexture;
// Clear shader keywords state
material.shaderKeywords = null;
switch (filteringOperation)
{
case FilteringOperation.Point:
material.EnableKeyword(ShaderKeywordStrings.PointSampling);
break;
case FilteringOperation.TaaSharpening:
// 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); // TODO: Global state constants.
break;
case FilteringOperation.FsrSharpening:
// 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); // TODO: Global state constants.
}
break;
case FilteringOperation.Linear: goto default;
default:
break;
}
if (isAlphaOutputEnabled)
CoreUtils.SetKeyword(material, ShaderKeywordStrings._ENABLE_ALPHA_OUTPUT, isAlphaOutputEnabled);
if(data.filmGrain.IsActive())
data.filmGrain.Apply(material);
if(data.dithering.IsActive())
data.dithering.Apply(material);
// FXAA could have been already applied by a previous pass.
if (applyFxaa)
material.EnableKeyword(ShaderKeywordStrings.Fxaa);
if (applySrgbEncoding)
material.EnableKeyword(ShaderKeywordStrings.LinearToSRGBConversion);
if (hdrColorEncoding)
{
PostProcessUtils.SetupHDROutput(material, cameraData.hdrDisplayInformation, cameraData.hdrDisplayColorGamut, data.tonemapping, data.hdrOperations, cameraData.rendersOverlayUI);
RenderingUtils.SetupOffscreenUIViewportParams(material, ref cameraData.pixelRect, cameraData.resolveFinalTarget);
}
material.SetVector(ShaderConstants._SourceSize, PostProcessUtils.CalcShaderSourceSize(sourceTextureHdl));
Vector4 scaleBias = RenderingUtils.GetFinalBlitScaleBias(context, data.sourceTexture, data.destinationTexture);
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(ShaderConstants._BlitScaleBias, scaleBias);
xrPropertyBlock.SetTexture(ShaderConstants._BlitTexture, sourceTextureHdl);
data.cameraData.xr.RenderVisibleMeshCustomMaterial(cmd, data.cameraData.xr.occlusionMeshScale, material, xrPropertyBlock, 1, context.GetTextureUVOrigin(in data.sourceTexture) == context.GetTextureUVOrigin(in data.destinationTexture));
}
else
#endif
Blitter.BlitTexture(cmd, sourceTextureHdl, scaleBias, material, 0);
});
}
resourceData.SwitchActiveTexturesToBackbuffer();
}
// Precomputed shader ids to same some CPU cycles (mostly affects mobile)
public static class ShaderConstants
{
public static readonly int _SourceSize = Shader.PropertyToID("_SourceSize");
#if ENABLE_VR && ENABLE_XR_MODULE
public static readonly int _BlitScaleBias = Shader.PropertyToID("_BlitScaleBias");
public static readonly int _BlitTexture = Shader.PropertyToID("_BlitTexture");
#endif
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: aef66ab76b8cc4546855b92dfdddeb23
@@ -0,0 +1,103 @@
using System;
using UnityEngine.Rendering.RenderGraphModule;
using System.Runtime.CompilerServices; // AggressiveInlining
namespace UnityEngine.Rendering.Universal
{
internal sealed class Fsr1UpscalePostProcessPass : PostProcessPass
{
public const string k_TargetName = "CameraColorUpscaled";
Material m_Material;
bool m_IsValid;
TextureDesc m_UpscaledDesc;
public Fsr1UpscalePostProcessPass(Shader shader)
{
this.renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing - 1;
this.profilingSampler = new ProfilingSampler("Blit FSR Upscaling");
m_Material = PostProcessUtils.LoadShader(shader, passName);
m_IsValid = m_Material != null;
// Default
PostProcessUtils.MakeCompatible(ref m_UpscaledDesc);
m_UpscaledDesc.width = 1; // Unknown at pipe/pass construction time. Safe default. Avoid division by zero.
m_UpscaledDesc.height = 1;
m_UpscaledDesc.format = Experimental.Rendering.GraphicsFormat.B10G11R11_UFloatPack32; // URP default.
}
public override void Dispose()
{
CoreUtils.Destroy(m_Material);
m_IsValid = false;
}
private class PostProcessingFinalFSRScalePassData
{
internal Material material;
internal TextureHandle sourceTexture;
internal Vector2 fsrInputSize;
internal Vector2 fsrOutputSize;
internal bool enableAlphaOutput;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Setup(TextureDesc upscaledDesc)
{
m_UpscaledDesc = upscaledDesc;
}
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
if (!m_IsValid)
return;
var cameraData = frameData.Get<UniversalCameraData>();
var resourceData = frameData.Get<UniversalResourceData>();
var sourceTexture = resourceData.cameraColor;
var srcDesc = renderGraph.GetTextureDesc(sourceTexture);
var destinationTexture = PostProcessUtils.CreateCompatibleTexture(renderGraph, m_UpscaledDesc, k_TargetName, true, FilterMode.Point);
var dstDesc = renderGraph.GetTextureDesc(destinationTexture);
// FSR upscale
using (var builder = renderGraph.AddRasterRenderPass<PostProcessingFinalFSRScalePassData>(passName, out var passData, profilingSampler))
{
builder.AllowGlobalStateModification(true);
builder.SetRenderAttachment(destinationTexture, 0, AccessFlags.Write);
passData.sourceTexture = sourceTexture;
builder.UseTexture(sourceTexture, AccessFlags.Read);
passData.material = m_Material;
passData.fsrInputSize = new Vector2(srcDesc.width, srcDesc.height);
passData.fsrOutputSize = new Vector2(dstDesc.width, dstDesc.height);
passData.enableAlphaOutput = cameraData.isAlphaOutputEnabled;
builder.SetRenderFunc(static (PostProcessingFinalFSRScalePassData data, RasterGraphContext context) =>
{
var material = data.material;
RTHandle sourceHdl = (RTHandle)data.sourceTexture;
// TODO: Fsr uses global state constants.
FSRUtils.SetEasuConstants(context.cmd, data.fsrInputSize, data.fsrInputSize, data.fsrOutputSize);
material.shaderKeywords = null;
CoreUtils.SetKeyword(material, ShaderKeywordStrings._ENABLE_ALPHA_OUTPUT, data.enableAlphaOutput);
Vector2 viewportScale = sourceHdl.useScaling ? new Vector2(sourceHdl.rtHandleProperties.rtHandleScale.x, sourceHdl.rtHandleProperties.rtHandleScale.y) : Vector2.one;
Blitter.BlitTexture(context.cmd, data.sourceTexture, viewportScale, material, 0);
});
}
resourceData.cameraColor = destinationTexture;
}
// Precomputed shader ids to same some CPU cycles (mostly affects mobile)
public static class ShaderConstants
{
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: f6f4af85aaf424142972405bdfd5954e
@@ -0,0 +1,279 @@
using System;
using UnityEngine.Rendering.RenderGraphModule;
using System.Runtime.CompilerServices; // AggressiveInlining
namespace UnityEngine.Rendering.Universal
{
internal sealed class LensFlareDataDrivenPostProcessPass : PostProcessPass
{
Material m_Material;
bool m_IsValid;
const string k_passNameOcclusion = "Blit Lens Flare Occlusion";
public LensFlareDataDrivenPostProcessPass(Shader shader)
{
this.renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing - 1;
this.profilingSampler = URPProfilingSamplers.LensFlareDataDriven;
m_Material = PostProcessUtils.LoadShader(shader, passName);
m_IsValid = m_Material != null;
}
public override void Dispose()
{
CoreUtils.Destroy(m_Material);
m_IsValid = false;
}
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;
}
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
if (!m_IsValid)
return;
var postProcessingData = frameData.Get<UniversalPostProcessingData>();
if (LensFlareCommonSRP.Instance.IsEmpty() || !postProcessingData.supportDataDrivenLensFlare)
return;
var cameraData = frameData.Get<UniversalCameraData>();
var resourceData = frameData.Get<UniversalResourceData>();
// No "sourceTexture". Source is procedurally generated.
var destinationTexture = resourceData.cameraColor;
// Reset keywords
m_Material.shaderKeywords = null;
var desc = destinationTexture.GetDescriptor(renderGraph);
var paniniProjection = volumeStack.GetComponent<PaniniProjection>();
if (LensFlareCommonSRP.IsOcclusionRTCompatible())
LensFlareDataDrivenComputeOcclusion(renderGraph, resourceData, cameraData, in desc, paniniProjection);
RenderLensFlareDataDriven(renderGraph, resourceData, cameraData, destinationTexture, in desc, paniniProjection);
}
void LensFlareDataDrivenComputeOcclusion(RenderGraph renderGraph, UniversalResourceData resourceData, UniversalCameraData cameraData, in TextureDesc dstDesc, PaniniProjection paniniProjection)
{
using (var builder = renderGraph.AddUnsafePass<LensFlarePassData>(k_passNameOcclusion, out var passData, URPProfilingSamplers.LensFlareDataDrivenComputeOcclusion))
{
TextureHandle occlusionHandle = renderGraph.ImportTexture(LensFlareCommonSRP.occlusionRT);
passData.destinationTexture = occlusionHandle;
builder.UseTexture(occlusionHandle, AccessFlags.Write);
passData.cameraData = cameraData;
passData.viewport = cameraData.pixelRect;
passData.material = m_Material;
passData.width = (float)dstDesc.width;
passData.height = (float)dstDesc.height;
if (paniniProjection.IsActive())
{
passData.usePanini = true;
passData.paniniDistance = paniniProjection.distance.value;
passData.paniniCropToFit = 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;
Experimental.Rendering.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)
{
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
});
}
}
void RenderLensFlareDataDriven(RenderGraph renderGraph, UniversalResourceData resourceData, UniversalCameraData cameraData, in TextureHandle destination, in TextureDesc srcDesc, PaniniProjection paniniProjection)
{
using (var builder = renderGraph.AddUnsafePass<LensFlarePassData>(passName, out var passData, profilingSampler))
{
// 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_Material;
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 (paniniProjection.IsActive())
{
passData.usePanini = true;
passData.paniniDistance = paniniProjection.distance.value;
passData.paniniCropToFit = 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;
Experimental.Rendering.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
});
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float GetLensFlareLightAttenuation(Light light, Camera cam, Vector3 wo)
{
// Must always be true
if (light != null)
{
switch (light.type)
{
case LightType.Directional:
return LensFlareCommonSRP.ShapeAttenuationDirLight(light.transform.forward, cam.transform.forward);
case LightType.Point:
return LensFlareCommonSRP.ShapeAttenuationPointLight();
case LightType.Spot:
return LensFlareCommonSRP.ShapeAttenuationSpotConeLight(light.transform.forward, wo, light.spotAngle, light.innerSpotAngle / 180.0f);
default:
return 1.0f;
}
}
return 1.0f;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8820d60aea9a4c6fa6d11ddb83d1db90
timeCreated: 1749306989
@@ -0,0 +1,180 @@
using System;
using UnityEngine.Rendering.RenderGraphModule;
using System.Runtime.CompilerServices; // AggressiveInlining
namespace UnityEngine.Rendering.Universal
{
internal sealed class LensFlareScreenSpacePostProcessPass : PostProcessPass
{
Material m_Material;
bool m_IsValid;
// Post-processing main color-buffer texture desc. Can be different from downsampled bloom/flare textures.
int m_ColorBufferWidth;
int m_ColorBufferHeight;
int m_FlareSourceBloomMipIndex;
public LensFlareScreenSpacePostProcessPass(Shader shader)
{
this.renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing - 1;
this.profilingSampler = URPProfilingSamplers.LensFlareScreenSpace;
m_Material = PostProcessUtils.LoadShader(shader, passName);
m_IsValid = m_Material != null;
// Default values
m_ColorBufferWidth = 1; // Unknown at pipe/pass construction time. Safe default. Avoid division by zero.
m_ColorBufferHeight = 1;
m_FlareSourceBloomMipIndex = 0; // Safe. Mip pyramid might not exist.
}
public override void Dispose()
{
CoreUtils.Destroy(m_Material);
m_IsValid = false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Setup(int colorBufferWidth, int colorBufferHeight, int flareSourceBloomMipIndex)
{
m_ColorBufferWidth = colorBufferWidth;
m_ColorBufferHeight = colorBufferHeight;
m_FlareSourceBloomMipIndex = flareSourceBloomMipIndex;
}
private class LensFlareScreenSpacePassData
{
internal Material material;
internal ScreenSpaceLensFlare lensFlareScreenSpace;
internal Camera camera;
internal TextureHandle streakTmpTexture;
internal TextureHandle streakTmpTexture2;
internal TextureHandle flareResultTmp;
internal TextureHandle flareDestinationBloomTexture;
internal TextureHandle flareSourceBloomMipTexture;
internal int actualColorWidth;
internal int actualColorHeight;
internal int downsample;
}
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
if (!m_IsValid)
return;
if (!IsActive(volumeStack, frameData))
return;
var lensFlareScreenSpace = volumeStack.GetComponent<ScreenSpaceLensFlare>();
var bloom = volumeStack.GetComponent<Bloom>();
// Check if Flare source and Flare target is the same texture. In practice BloomMip[0]
// TODO: Check src/dst handle equality in the future.
// Kawase blur does not use the mip pyramid.
// It is safe to pass the same texture to both input/output.
bool useDestinationAsSource = m_FlareSourceBloomMipIndex == 0 || bloom.filter.value == BloomFilterMode.Kawase;
var resourceData = frameData.Get<UniversalResourceData>();
var cameraData = frameData.Get<UniversalCameraData>();
// Flare streaks are generated from this texture. Typically, a lower resolution downsampled bloom (mipN) texture.
var sourceTexture = resourceData.cameraColor;
// Flare is blended to the destination. Typically, the bloom (mip0) texture. Bloom can be at different resolution compared to the main color source.
var destinationTexture = resourceData.bloom;
var downsample = (int) lensFlareScreenSpace.resolution.value;
int flareRenderWidth = Math.Max( m_ColorBufferWidth / downsample, 1);
int flareRenderHeight = Math.Max( m_ColorBufferHeight / downsample, 1);
var streakDesc = renderGraph.GetTextureDesc(resourceData.cameraColor);
var streakTextureDesc = PostProcessUtils.GetCompatibleDescriptor(streakDesc, flareRenderWidth, flareRenderHeight, streakDesc.colorFormat);
var streakTmpTexture = PostProcessUtils.CreateCompatibleTexture(renderGraph, streakTextureDesc, "_StreakTmpTexture", true, FilterMode.Bilinear);
var streakTmpTexture2 = PostProcessUtils.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 resultTmpTexture = PostProcessUtils.CreateCompatibleTexture(renderGraph, streakTextureDesc, "_LensFlareScreenSpace", true, FilterMode.Bilinear);
using (var builder = renderGraph.AddUnsafePass<LensFlareScreenSpacePassData>(passName, out var passData, profilingSampler))
{
// 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.flareSourceBloomMipTexture = sourceTexture;
builder.UseTexture(sourceTexture, AccessFlags.ReadWrite);
passData.flareDestinationBloomTexture = destinationTexture;
// Input/Output can be the same texture. There's a temp texture in between. Avoid RG double write error.
if(!useDestinationAsSource)
builder.UseTexture(destinationTexture, AccessFlags.ReadWrite);
passData.actualColorWidth = m_ColorBufferWidth;
passData.actualColorHeight = m_ColorBufferHeight;
passData.camera = cameraData.camera;
passData.material = m_Material;
passData.lensFlareScreenSpace = lensFlareScreenSpace; // NOTE: reference, assumed constant until executed.
passData.downsample = downsample;
passData.flareResultTmp = resultTmpTexture;
builder.UseTexture(resultTmpTexture, 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.actualColorWidth,
(float)data.actualColorHeight,
data.lensFlareScreenSpace.tintColor.value,
data.flareDestinationBloomTexture,
data.flareSourceBloomMipTexture,
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.flareResultTmp,
false);
});
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static internal bool IsActive(VolumeStack volumeStack, ContextContainer frameData)
{
var lensFlareScreenSpace = volumeStack.GetComponent<ScreenSpaceLensFlare>();
if (!lensFlareScreenSpace.IsActive())
return false;
return frameData.Get<UniversalPostProcessingData>().supportScreenSpaceLensFlare;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7c68662492324997981f99fb0847793d
timeCreated: 1749306928
@@ -0,0 +1,189 @@
using System;
using UnityEngine.Rendering.RenderGraphModule;
using System.Runtime.CompilerServices; // AggressiveInlining
namespace UnityEngine.Rendering.Universal
{
internal sealed class MotionBlurPostProcessPass : PostProcessPass
{
public const string k_TargetName = "CameraColorMotionBlur";
Material m_Material;
bool m_IsValid;
public MotionBlurPostProcessPass(Shader shader)
{
this.renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing - 1;
this.profilingSampler = URPProfilingSamplers.MotionBlur;
m_Material = PostProcessUtils.LoadShader(shader, passName, logLevel: LogType.Log);
m_IsValid = m_Material != null;
}
public override void Dispose()
{
CoreUtils.Destroy(m_Material);
m_IsValid = false;
}
private class MotionBlurPassData
{
internal Material material;
internal Camera camera;
internal TextureHandle sourceTexture;
internal int passIndex;
internal float intensity;
internal float clamp;
internal bool enableAlphaOutput;
internal Experimental.Rendering.XRPass xr;
}
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
if(!m_IsValid)
return;
// 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
if(!Application.isPlaying)
return;
var motionBlur = volumeStack.GetComponent<MotionBlur>();
if (!motionBlur.IsActive())
return;
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
if (cameraData.isSceneViewCamera)
return;
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
TextureHandle motionVectorColor = resourceData.motionVectorColor;
if (motionBlur.mode.value == MotionBlurMode.CameraAndObjects)
{
if(!motionVectorColor.IsValid())
{
var warning = "Disabling Motion Blur for Camera And Objects because of missing motion vectors. The renderer might not support rendering motion vectors.";
const int warningThrottleFrames = 60 * 1; // 60 FPS * 1 sec
if (Time.frameCount % warningThrottleFrames == 0)
Debug.LogWarning(warning);
return;
}
}
var sourceTexture = resourceData.cameraColor;
var destinationTexture = PostProcessUtils.CreateCompatibleTexture(renderGraph, sourceTexture, k_TargetName, true, FilterMode.Bilinear);
TextureHandle cameraDepthTexture = resourceData.cameraDepthTexture;
var mode = motionBlur.mode.value;
int passIndex = (int)motionBlur.quality.value;
passIndex += (mode == MotionBlurMode.CameraAndObjects) ? ShaderPass.k_CameraAndObjectMotionBlurLow : ShaderPass.k_CameraMotionBlurLow;
using (var builder = renderGraph.AddRasterRenderPass<MotionBlurPassData>(passName, out var passData, profilingSampler))
{
builder.SetRenderAttachment(destinationTexture, 0, AccessFlags.Write);
passData.sourceTexture = sourceTexture;
builder.UseTexture(sourceTexture, 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.");
builder.UseTexture(motionVectorColor, AccessFlags.Read);
}
Debug.Assert(cameraDepthTexture.IsValid(), "Camera depth texture is invalid. Per-camera motion blur requires a depth texture.");
builder.UseTexture(cameraDepthTexture, AccessFlags.Read);
passData.material = m_Material;
passData.passIndex = passIndex;
passData.camera = cameraData.camera;
passData.enableAlphaOutput = cameraData.isAlphaOutputEnabled;
passData.intensity = motionBlur.intensity.value;
passData.clamp = motionBlur.clamp.value;
passData.xr = cameraData.xr;
builder.SetRenderFunc(static (MotionBlurPassData data, RasterGraphContext context) =>
{
var cmd = context.cmd;
RTHandle sourceTextureHdl = data.sourceTexture;
UpdateMotionBlurMatrices(data.material, data.camera, data.xr);
var sourceSize = PostProcessUtils.CalcShaderSourceSize(sourceTextureHdl);
data.material.SetVector(ShaderConstants._SourceSize, sourceSize);
data.material.SetFloat(ShaderConstants._Intensity, data.intensity);
data.material.SetFloat(ShaderConstants._Clamp, data.clamp);
CoreUtils.SetKeyword(data.material, ShaderKeywordStrings._ENABLE_ALPHA_OUTPUT, data.enableAlphaOutput);
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);
});
}
resourceData.cameraColor = destinationTexture;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void UpdateMotionBlurMatrices(Material material, Camera camera, Experimental.Rendering.XRPass xr)
{
MotionVectorsPersistentData motionData = null;
if(camera.TryGetComponent<UniversalAdditionalCameraData>(out var additionalCameraData))
motionData = additionalCameraData.motionVectorsPersistentData;
if (motionData == null)
return;
#if ENABLE_VR && ENABLE_XR_MODULE
if (xr.enabled && xr.singlePassEnabled)
{
// pass maximum of 2 matrices per pass. Need to access into the matrix array
var viewStartIndex = xr.viewCount * xr.multipassId;
// Using motionData.stagingMatrixStereo as staging buffer to avoid allocation
Array.Copy(motionData.previousViewProjectionStereo, viewStartIndex, motionData.stagingMatrixStereo, 0, xr.viewCount);
material.SetMatrixArray(ShaderConstants._PrevViewProjMStereo, motionData.stagingMatrixStereo);
Array.Copy(motionData.viewProjectionStereo, viewStartIndex, motionData.stagingMatrixStereo, 0, xr.viewCount);
material.SetMatrixArray(ShaderConstants._ViewProjMStereo, motionData.stagingMatrixStereo);
}
else
#endif
{
int viewProjMIdx = 0;
#if ENABLE_VR && ENABLE_XR_MODULE
if (xr.enabled)
viewProjMIdx = xr.multipassId * xr.viewCount;
#endif
// TODO: These should be part of URP main matrix set. For now, we set them here for motion vector rendering.
material.SetMatrix(ShaderConstants._PrevViewProjM, motionData.previousViewProjectionStereo[viewProjMIdx]);
material.SetMatrix(ShaderConstants._ViewProjM, motionData.viewProjectionStereo[viewProjMIdx]);
}
}
// Precomputed shader ids to same some CPU cycles (mostly affects mobile)
public static class ShaderConstants
{
public static readonly int _ViewProjM = Shader.PropertyToID("_ViewProjM");
public static readonly int _PrevViewProjM = Shader.PropertyToID("_PrevViewProjM");
public static readonly int _ViewProjMStereo = Shader.PropertyToID("_ViewProjMStereo");
public static readonly int _PrevViewProjMStereo = Shader.PropertyToID("_PrevViewProjMStereo");
public static readonly int _Intensity = Shader.PropertyToID("_Intensity");
public static readonly int _Clamp = Shader.PropertyToID("_Clamp");
public static readonly int _SourceSize = Shader.PropertyToID("_SourceSize");
}
public static class ShaderPass
{
public const int k_CameraMotionBlurLow = 0;
public const int k_CameraMotionBlurMedium = 1;
public const int k_CameraMotionBlurHigh = 2;
public const int k_CameraAndObjectMotionBlurLow = 3;
public const int k_CameraAndObjectMotionBlurMedium = 4;
public const int k_CameraAndObjectMotionBlurHigh = 5;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: fa054ad1c981d4fdab51c7cc151226c6
@@ -0,0 +1,150 @@
using System;
using UnityEngine.Rendering.RenderGraphModule;
using System.Runtime.CompilerServices; // AggressiveInlining
namespace UnityEngine.Rendering.Universal
{
internal sealed class PaniniProjectionPostProcessPass : PostProcessPass
{
public const string k_TargetName = "CameraColorPaniniProjection";
Material m_Material;
bool m_IsValid;
public PaniniProjectionPostProcessPass(Shader shader)
{
this.renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing - 1;
this.profilingSampler = URPProfilingSamplers.PaniniProjection;
m_Material = PostProcessUtils.LoadShader(shader, passName, logLevel: LogType.Log);
m_IsValid = m_Material != null;
}
public override void Dispose()
{
CoreUtils.Destroy(m_Material);
m_IsValid = false;
}
private class PaniniProjectionPassData
{
internal Material material;
internal TextureHandle sourceTexture;
internal Vector4 paniniParams;
internal bool isPaniniGeneric;
}
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
if(!m_IsValid)
return;
var paniniProjection = volumeStack.GetComponent<PaniniProjection>();
if(!paniniProjection.IsActive())
return;
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
if (cameraData.isSceneViewCamera)
return;
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
var sourceTexture = resourceData.cameraColor;
var destinationTexture = PostProcessUtils.CreateCompatibleTexture(renderGraph, sourceTexture, k_TargetName, true, FilterMode.Bilinear);
Camera camera = cameraData.camera;
// Use source width/height for aspect ratio which can be different from camera aspect. (e.g. viewport)
var desc = sourceTexture.GetDescriptor(renderGraph);
float distance = paniniProjection.distance.value;
var viewExtents = CalcViewExtents(camera.fieldOfView, desc.width, desc.height);
var cropExtents = CalcCropExtents(camera.fieldOfView, 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), paniniProjection.cropToFit.value);
using (var builder = renderGraph.AddRasterRenderPass<PaniniProjectionPassData>(passName, out var passData, profilingSampler))
{
builder.AllowGlobalStateModification(true);
builder.SetRenderAttachment(destinationTexture, 0, AccessFlags.Write);
passData.sourceTexture = sourceTexture;
builder.UseTexture(sourceTexture, AccessFlags.Read);
passData.material = m_Material;
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;
data.material.SetVector(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);
});
}
resourceData.cameraColor = destinationTexture;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2 CalcViewExtents(float fieldOfView, int width, int height)
{
float fovY = fieldOfView * Mathf.Deg2Rad;
float aspect = width / (float)height;
float viewExtY = Mathf.Tan(0.5f * fovY);
float viewExtX = aspect * viewExtY;
return new Vector2(viewExtX, viewExtY);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2 CalcCropExtents(float fieldOfView, float d, int width, int height)
{
// given
// S----------- E--X-------
// | ` ~. /,´
// |-- --- Q
// | ,/ `
// 1 | ,´/ `
// | ,´ / ´
// | ,´ / ´
// |,` / ,
// O /
// | / ,
// d | /
// | / ,
// |/ .
// P
// | ´
// | , ´
// +- ´
//
// have X
// want to find E
float viewDist = 1f + d;
var projPos = CalcViewExtents(fieldOfView, width, height);
var projHyp = Mathf.Sqrt(projPos.x * projPos.x + 1f);
float cylDistMinusD = 1f / projHyp;
float cylDist = cylDistMinusD + d;
var cylPos = projPos * cylDistMinusD;
return cylPos * (viewDist / cylDist);
}
// Precomputed shader ids to same some CPU cycles (mostly affects mobile)
public static class ShaderConstants
{
public static readonly int _Params = Shader.PropertyToID("_Params");
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d5c66722071146f69b099990ed8bcbda
timeCreated: 1749486046
@@ -0,0 +1,30 @@
using System;
namespace UnityEngine.Rendering.Universal
{
internal abstract class PostProcessPass : ScriptableRenderPass, IDisposable
{
VolumeStack m_VolumeStackOverride;
public VolumeStack volumeStack
{
get{
if (m_VolumeStackOverride == null)
{
return VolumeManager.instance.stack;
}
else
{
return m_VolumeStackOverride;
}
}
}
public VolumeStack volumeStackOverride
{
set { m_VolumeStackOverride = value; }
}
public abstract void Dispose();
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 389e75599c6f47399ec151a887fef961
timeCreated: 1761069683
@@ -0,0 +1,125 @@
using System;
using UnityEngine.Rendering.RenderGraphModule;
using System.Runtime.CompilerServices; // AggressiveInlining
namespace UnityEngine.Rendering.Universal
{
internal sealed class ScalingSetupPostProcessPass : PostProcessPass
{
public const string k_TargetName = "SetupUpscaling";
Material m_Material;
bool m_IsValid;
HDROutputUtils.Operation m_HdrOperations;
public ScalingSetupPostProcessPass(Shader shader)
{
this.renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing - 1;
this.profilingSampler = new ProfilingSampler("Setup Final Post Processing");
m_Material = PostProcessUtils.LoadShader(shader, passName);
m_IsValid = m_Material != null;
// Defaults
m_HdrOperations = HDROutputUtils.Operation.None; // HDR disabled.
}
public override void Dispose()
{
CoreUtils.Destroy(m_Material);
m_IsValid = false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Setup(HDROutputUtils.Operation hdrOperations)
{
m_HdrOperations = hdrOperations;
}
private class PostProcessingFinalSetupPassData
{
internal TextureHandle destinationTexture;
internal TextureHandle sourceTexture;
internal Material material;
internal UniversalCameraData cameraData;
internal Tonemapping tonemapping;
internal HDROutputUtils.Operation hdrOperations;
}
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
if (!m_IsValid)
return;
var tonemapping = volumeStack.GetComponent<Tonemapping>();
var cameraData = frameData.Get<UniversalCameraData>();
var resourceData = frameData.Get<UniversalResourceData>();
var sourceTexture = resourceData.cameraColor;
var scalingSetupDesc = renderGraph.GetTextureDesc(sourceTexture);
bool requireHDROutput = PostProcessUtils.RequireHDROutput(cameraData);
if (!requireHDROutput)
{
// 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.
scalingSetupDesc.format = UniversalRenderPipeline.MakeUnormRenderTextureGraphicsFormat();
}
var destinationTexture = PostProcessUtils.CreateCompatibleTexture(renderGraph, scalingSetupDesc, k_TargetName, true, FilterMode.Point);
// Scaled FXAA
using (var builder = renderGraph.AddRasterRenderPass<PostProcessingFinalSetupPassData>(passName, out var passData, profilingSampler))
{
passData.destinationTexture = destinationTexture;
builder.SetRenderAttachment(destinationTexture, 0, AccessFlags.Write);
passData.sourceTexture = sourceTexture;
builder.UseTexture(sourceTexture, AccessFlags.Read);
passData.material = m_Material;
passData.cameraData = cameraData;
passData.tonemapping = tonemapping;
passData.hdrOperations = m_HdrOperations;
builder.SetRenderFunc(static (PostProcessingFinalSetupPassData data, RasterGraphContext context) =>
{
UniversalCameraData cameraData = data.cameraData;
Material material = data.material;
material.shaderKeywords = null;
bool hdrColorEncoding = data.hdrOperations.HasFlag(HDROutputUtils.Operation.ColorEncoding);
bool isFxaaEnabled = PostProcessUtils.IsFxaaEnabled(cameraData);
bool isFsrEnabled = PostProcessUtils.IsFsrEnabled(cameraData);
if (isFxaaEnabled)
material.EnableKeyword(ShaderKeywordStrings.Fxaa);
if (isFsrEnabled)
material.EnableKeyword( hdrColorEncoding ? ShaderKeywordStrings.Gamma20AndHDRInput : ShaderKeywordStrings.Gamma20);
if (hdrColorEncoding)
PostProcessUtils.SetupHDROutput(material, cameraData.hdrDisplayInformation, cameraData.hdrDisplayColorGamut, data.tonemapping, data.hdrOperations, cameraData.rendersOverlayUI);
if (data.cameraData.isAlphaOutputEnabled)
CoreUtils.SetKeyword(material, ShaderKeywordStrings._ENABLE_ALPHA_OUTPUT, cameraData.isAlphaOutputEnabled);
material.SetVector(ShaderConstants._SourceSize, PostProcessUtils.CalcShaderSourceSize(data.sourceTexture));
const bool isFinalPass = false; // This is a pass just before final pass. Viewport must match intermediate target.
PostProcessUtils.ScaleViewportAndBlit(context, data.sourceTexture, data.destinationTexture, data.cameraData, data.material, isFinalPass);
});
}
resourceData.cameraColor = destinationTexture;
}
// Precomputed shader ids to same some CPU cycles (mostly affects mobile)
public static class ShaderConstants
{
public static readonly int _SourceSize = Shader.PropertyToID("_SourceSize");
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: e6ccd2bcab38044d49049ca64624997a
@@ -0,0 +1,229 @@
using System;
using Unity.Profiling.LowLevel;
using UnityEngine.Rendering.RenderGraphModule;
using System.Runtime.CompilerServices; // AggressiveInlining
namespace UnityEngine.Rendering.Universal
{
internal sealed class SmaaPostProcessPass : PostProcessPass
{
public const string k_TargetName = "CameraColorSMAA";
Material m_Material;
bool m_IsValid;
// Static resource textures for SMAA technique
Texture2D m_AreaTexture;
Texture2D m_SearchTexture;
Experimental.Rendering.GraphicsFormat m_SMAAEdgeFormat;
const string k_passNameEdgeDetection = "Blit SMAA Edge Detection";
const string k_passNameBlendWeights = "Blit SMAA Blend Weights";
public SmaaPostProcessPass(Shader shader, Texture2D smaaAreaTexture, Texture2D smaaSearchTexture)
{
this.renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing - 1;
this.profilingSampler = URPProfilingSamplers.SMAANeighborhoodBlend;
m_Material = PostProcessUtils.LoadShader(shader, passName);
m_IsValid = m_Material != null;
m_AreaTexture = smaaAreaTexture;
m_SearchTexture = smaaSearchTexture;
// SMAA
// Only two components are needed for edge render texture, but on some vendors four components may be faster.
if (SystemInfo.IsFormatSupported(Experimental.Rendering.GraphicsFormat.R8G8_UNorm, Experimental.Rendering.GraphicsFormatUsage.Render) && SystemInfo.graphicsDeviceVendor.ToLowerInvariant().Contains("arm"))
m_SMAAEdgeFormat = Experimental.Rendering.GraphicsFormat.R8G8_UNorm;
else
m_SMAAEdgeFormat = Experimental.Rendering.GraphicsFormat.R8G8B8A8_UNorm;
}
public override void Dispose()
{
CoreUtils.Destroy(m_Material);
m_IsValid = false;
}
private class SMAASetupPassData
{
internal Vector4 metrics;
internal Texture2D areaTexture;
internal Texture2D searchTexture;
internal float stencilRef;
internal float stencilMask;
internal AntialiasingQuality antialiasingQuality;
internal Material material;
internal TextureHandle sourceTexture;
}
private class SMAAPassData
{
internal TextureHandle sourceTexture;
internal TextureHandle blendTexture;
internal Material material;
}
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
if (!m_IsValid)
return;
var cameraData = frameData.Get<UniversalCameraData>();
if (cameraData.antialiasing != AntialiasingMode.SubpixelMorphologicalAntiAliasing)
return;
var resourceData = frameData.Get<UniversalResourceData>();
var sourceTexture = resourceData.cameraColor;
var destDesc = renderGraph.GetTextureDesc(sourceTexture);
var destinationTexture = PostProcessUtils.CreateCompatibleTexture(renderGraph, destDesc, k_TargetName, true, FilterMode.Bilinear);
destDesc.clearColor = Color.black;
destDesc.clearColor.a = 0.0f;
var edgeTextureDesc = destDesc;
edgeTextureDesc.format = m_SMAAEdgeFormat;
var edgeTexture = PostProcessUtils.CreateCompatibleTexture(renderGraph, edgeTextureDesc, "_EdgeStencilTexture", true, FilterMode.Bilinear);
var edgeTextureStencilDesc = destDesc;
edgeTextureStencilDesc.format = Experimental.Rendering.GraphicsFormatUtility.GetDepthStencilFormat(24);
var edgeTextureStencil = PostProcessUtils.CreateCompatibleTexture(renderGraph, edgeTextureStencilDesc, "_EdgeTexture", true, FilterMode.Bilinear);
var blendTextureDesc = destDesc;
blendTextureDesc.format = Experimental.Rendering.GraphicsFormat.R8G8B8A8_UNorm;
var blendTexture = PostProcessUtils.CreateCompatibleTexture(renderGraph, blendTextureDesc, "_BlendTexture", true, FilterMode.Point);
using (var builder = renderGraph.AddRasterRenderPass<SMAASetupPassData>(k_passNameEdgeDetection, out var passData, URPProfilingSamplers.SMAAEdgeDetection))
{
// Material setup
const int kStencilBit = 64;
// TODO RENDERGRAPH: handle dynamic scaling
passData.metrics = new Vector4(1f / destDesc.width, 1f / destDesc.height, destDesc.width, destDesc.height);
// TODO: These are constant for every frame
passData.areaTexture = m_AreaTexture;
passData.searchTexture = m_SearchTexture;
passData.stencilRef = (float)kStencilBit;
passData.stencilMask = (float)kStencilBit;
passData.antialiasingQuality = cameraData.antialiasingQuality;
passData.material = m_Material;
builder.SetRenderAttachment(edgeTexture, 0, AccessFlags.Write);
builder.SetRenderAttachmentDepth(edgeTextureStencil, AccessFlags.Write);
passData.sourceTexture = sourceTexture;
builder.UseTexture(sourceTexture, AccessFlags.Read);
builder.UseTexture(resourceData.cameraDepth ,AccessFlags.Read);
builder.SetRenderFunc(static (SMAASetupPassData data, RasterGraphContext context) =>
{
var SMAAMaterial = data.material;
var cmd = context.cmd;
RTHandle sourceTextureHdl = data.sourceTexture;
// Pass 0: Material Setup
SetupMaterial(data);
// 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, ShaderPass.k_EdgeDetection);
});
}
using (var builder = renderGraph.AddRasterRenderPass<SMAAPassData>(k_passNameBlendWeights, out var passData, URPProfilingSamplers.SMAABlendWeight))
{
builder.SetRenderAttachment(blendTexture, 0, AccessFlags.Write);
builder.SetRenderAttachmentDepth(edgeTextureStencil, AccessFlags.Read);
passData.sourceTexture = edgeTexture;
builder.UseTexture(edgeTexture, AccessFlags.Read);
passData.material = m_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, ShaderPass.k_BlendWeightsCalculation);
});
}
using (var builder = renderGraph.AddRasterRenderPass<SMAAPassData>(passName, out var passData, profilingSampler))
{
builder.SetRenderAttachment(destinationTexture, 0, AccessFlags.Write);
passData.sourceTexture = sourceTexture;
builder.UseTexture(sourceTexture, AccessFlags.Read);
passData.blendTexture = blendTexture;
builder.UseTexture(blendTexture, AccessFlags.Read);
passData.material = m_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, ShaderPass.k_NeighborhoodBlending);
});
}
resourceData.cameraColor = destinationTexture;
}
static void SetupMaterial(SMAASetupPassData data)
{
using (new ProfilingScope(URPProfilingSamplers.SMAAMaterialSetup))
{
// Common for all passes
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;
}
}
}
// Precomputed shader ids to same some CPU cycles (mostly affects mobile)
public static class ShaderConstants
{
public static readonly int _Metrics = Shader.PropertyToID("_Metrics");
public static readonly int _AreaTexture = Shader.PropertyToID("_AreaTexture");
public static readonly int _SearchTexture = Shader.PropertyToID("_SearchTexture");
public static readonly int _BlendTexture = Shader.PropertyToID("_BlendTexture");
//public static readonly int _EdgeTexture = Shader.PropertyToID("_EdgeTexture");
public static readonly int _StencilRef = Shader.PropertyToID("_StencilRef");
public static readonly int _StencilMask = Shader.PropertyToID("_StencilMask");
}
public static class ShaderPass
{
public const int k_EdgeDetection = 0;
public const int k_BlendWeightsCalculation = 1;
public const int k_NeighborhoodBlending = 2;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: a2a23360f57994b0d825d87e4ce62755
@@ -0,0 +1,72 @@
using System;
using UnityEngine.Rendering.RenderGraphModule;
using System.Runtime.CompilerServices; // AggressiveInlining
namespace UnityEngine.Rendering.Universal
{
internal sealed class StopNanPostProcessPass : PostProcessPass
{
public const string k_TargetName = "CameraColorStopNaNs";
Material m_Material;
bool m_IsValid;
public StopNanPostProcessPass(Shader shader)
{
this.renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing - 1;
this.profilingSampler = URPProfilingSamplers.StopNaNs;
m_Material = PostProcessUtils.LoadShader(shader, passName);
m_IsValid = m_Material != null;
}
public override void Dispose()
{
CoreUtils.Destroy(m_Material);
m_IsValid = false;
}
private class StopNaNsPassData
{
internal TextureHandle sourceTexture;
internal Material stopNaN;
}
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
if (!m_IsValid)
return;
var cameraData = frameData.Get<UniversalCameraData>();
if (!cameraData.isStopNaNEnabled)
return;
var resourceData = frameData.Get<UniversalResourceData>();
var sourceTexture = resourceData.cameraColor;
var destinationTexture = PostProcessUtils.CreateCompatibleTexture(renderGraph, sourceTexture, k_TargetName, true, FilterMode.Bilinear);
using (var builder = renderGraph.AddRasterRenderPass<StopNaNsPassData>(passName, out var passData,
profilingSampler))
{
builder.SetRenderAttachment(destinationTexture, 0, AccessFlags.ReadWrite);
passData.sourceTexture = sourceTexture;
builder.UseTexture(sourceTexture, AccessFlags.Read);
passData.stopNaN = m_Material;
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);
});
}
resourceData.cameraColor = destinationTexture;
}
// Precomputed shader ids to same some CPU cycles (mostly affects mobile)
public static class ShaderConstants
{
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 3ff5a8cb01afe45ceb78977408e21133
@@ -0,0 +1,107 @@
using System;
using UnityEngine.Rendering.RenderGraphModule;
using System.Runtime.CompilerServices; // AggressiveInlining
namespace UnityEngine.Rendering.Universal
{
#if ENABLE_UPSCALER_FRAMEWORK
[Obsolete("StpPostProcessPass is replaced by STPIUpscaler #from(6000.3)")]
#endif
internal sealed class StpPostProcessPass : PostProcessPass
{
public const string k_UpscaledColorTargetName = "CameraColorUpscaledSTP";
Texture2D[] m_BlueNoise16LTex;
bool m_IsValid;
uint m_WarnCounter;
public StpPostProcessPass(Texture2D[] blueNoise16LTex)
{
this.renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing - 1;
this.profilingSampler = null; // Use default name
m_BlueNoise16LTex = blueNoise16LTex;
m_IsValid = m_BlueNoise16LTex != null && m_BlueNoise16LTex.Length > 0;
m_WarnCounter = 0;
}
public override void Dispose()
{
m_IsValid = false;
}
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
if (!m_IsValid)
return;
#if ENABLE_UPSCALER_FRAMEWORK
var postProcessingData = frameData.Get<UniversalPostProcessingData>();
if (postProcessingData.activeUpscaler != null)
return;
#endif
// 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.
var cameraData = frameData.Get<UniversalCameraData>();
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;
if (!useSTP)
{
// Warn users if TAA and STP are disabled despite being requested
if (cameraData.IsTemporalAARequested())
TemporalAA.ValidateAndWarn(cameraData, ref m_WarnCounter, isSTPRequested);
return;
}
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
var sourceTexture = resourceData.cameraColor;
var srcDesc = renderGraph.GetTextureDesc(sourceTexture);
var dstDesc = StpPostProcessPass.GetStpTargetDesc(srcDesc, cameraData);
var destinationTexture = PostProcessUtils.CreateCompatibleTexture(renderGraph, dstDesc, k_UpscaledColorTargetName, false, FilterMode.Bilinear);
TextureHandle cameraDepth = resourceData.cameraDepthTexture;
TextureHandle motionVectors = resourceData.motionVectorColor;
Debug.Assert(motionVectors.IsValid(), "MotionVectors are invalid. STP requires a motion vector texture.");
int frameIndex = Time.frameCount;
var noiseTexture = m_BlueNoise16LTex[frameIndex & (m_BlueNoise16LTex.Length - 1)];
StpUtils.Execute(renderGraph, resourceData, cameraData, sourceTexture, cameraDepth, motionVectors, destinationTexture, noiseTexture);
// Update the camera resolution to reflect the upscaled size
var destDesc = destinationTexture.GetDescriptor(renderGraph);
UpscalerPostProcessPass.UpdateCameraResolution(renderGraph, cameraData, new Vector2Int(destDesc.width, destDesc.height));
resourceData.cameraColor = destinationTexture;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TextureDesc GetStpTargetDesc(in TextureDesc sourceDesc, UniversalCameraData cameraData)
{
var targetDesc = PostProcessUtils.GetCompatibleDescriptor(sourceDesc,
cameraData.pixelWidth,
cameraData.pixelHeight,
// Avoid enabling sRGB because STP works with compute shaders which can't output sRGB automatically.
Experimental.Rendering.GraphicsFormatUtility.GetLinearFormat(sourceDesc.format));
// STP uses compute shaders so all render textures must enable random writes
targetDesc.enableRandomWrite = true;
return targetDesc;
}
// Precomputed shader ids to same some CPU cycles (mostly affects mobile)
public static class ShaderConstants
{
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 015eb3e4cffa84bdfb3961bc135baa65
@@ -0,0 +1,80 @@
using System;
using UnityEngine.Rendering.RenderGraphModule;
using System.Runtime.CompilerServices; // AggressiveInlining
namespace UnityEngine.Rendering.Universal
{
internal sealed class TemporalAntiAliasingPostProcessPass : PostProcessPass
{
public const string k_TargetName = "CameraColorTemporalAA";
Material m_Material;
bool m_IsValid;
uint m_WarnCounter;
public TemporalAntiAliasingPostProcessPass(Shader shader)
{
this.renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing - 1;
this.profilingSampler = null;
m_Material = PostProcessUtils.LoadShader(shader, passName);
m_IsValid = m_Material != null;
m_WarnCounter = 0;
}
public override void Dispose()
{
CoreUtils.Destroy(m_Material);
m_IsValid = false;
}
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
if (!m_IsValid)
return;
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
#if ENABLE_UPSCALER_FRAMEWORK
var postProcessingData = frameData.Get<UniversalPostProcessingData>();
if (postProcessingData.activeUpscaler != null && postProcessingData.activeUpscaler.isTemporal)
return; // we are using a temporal upscaler that uses jitter, skip TAA pass
#else
// We are actually running STP which reuses TAA Jitter. Skip TAA pass.
if (cameraData.IsSTPRequested())
return;
#endif
// Note that enabling camera jitter uses the same CameraData::IsTemporalAAEnabled(). So if we add any other kind of overrides
// then we need to put it in CameraData::IsTemporalAAEnabled() as opposed
// to tweaking it locally here.
if (!cameraData.IsTemporalAAEnabled())
{
// Warn users if TAA is disabled despite being requested
if (cameraData.IsTemporalAARequested())
TemporalAA.ValidateAndWarn(cameraData, ref m_WarnCounter, false);
return;
}
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
var sourceTexture = resourceData.cameraColor;
var destinationTexture = PostProcessUtils.CreateCompatibleTexture(renderGraph, sourceTexture, k_TargetName, 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_Material, cameraData, sourceTexture, in cameraDepth, in motionVectors, destinationTexture);
resourceData.cameraColor = destinationTexture;
}
// Precomputed shader ids to same some CPU cycles (mostly affects mobile)
public static class ShaderConstants
{
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 26e1f8400167947f5a628494c94d0ff5
@@ -0,0 +1,720 @@
using System;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
using System.Runtime.CompilerServices; // AggressiveInlining
namespace UnityEngine.Rendering.Universal
{
internal sealed class UberPostProcessPass : PostProcessPass
{
Material m_Material;
Texture2D[] m_FilmGrainTextures;
public enum FilteringOperation
{
Linear,
Point
}
Texture m_DitherTexture;
RTHandle m_UserLut;
FilteringOperation m_FilteringOperation;
HDROutputUtils.Operation m_HdrOperations;
bool m_IsValid;
bool m_IsFinalPass;
bool m_RequireSRGBConversionBlit;
bool m_RenderOverlayUI;
public UberPostProcessPass(Shader shader, Texture2D[] filmGrainTextures)
{
this.renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing - 1;
this.profilingSampler = URPProfilingSamplers.UberPostProcess;
m_Material = PostProcessUtils.LoadShader(shader, passName);
m_IsValid = m_Material != null;
m_FilmGrainTextures = filmGrainTextures;
// Defaults
m_DitherTexture = null; // Dither disabled.
m_FilteringOperation = FilteringOperation.Linear; // Common case.
m_HdrOperations = HDROutputUtils.Operation.None; // HDR disabled.
m_RequireSRGBConversionBlit = false; // sRGB conversion is typically automatic based on format.
m_IsFinalPass = false; // Assume other passes.
m_RenderOverlayUI = false; // HDR disabled. HDR only.
}
public override void Dispose()
{
m_UserLut?.Release();
CoreUtils.Destroy(m_Material);
m_IsValid = false;
}
public void Setup(Texture ditherTexture,
FilteringOperation filteringOperation,
HDROutputUtils.Operation hdrOperations,
bool requireSRGBConversionBlit,
bool isFinalPass,
bool renderOverlayUI)
{
m_DitherTexture = ditherTexture;
m_FilteringOperation = filteringOperation;
m_HdrOperations = hdrOperations;
m_RequireSRGBConversionBlit = requireSRGBConversionBlit;
m_IsFinalPass = isFinalPass;
m_RenderOverlayUI = renderOverlayUI;
}
private class UberPostPassData
{
internal TextureHandle destinationTexture;
internal TextureHandle sourceTexture;
internal TextureHandle internalLutTexture;
internal Material material; // NOTE: material is a ref, pass instance is not re-entrant within a frame!
internal UniversalCameraData cameraData;
internal Tonemapping tonemapping;
internal FilteringOperation filteringOperation;
internal HDROutputUtils.Operation hdrOperations;
internal bool isHdrGrading;
internal LutParams lut;
internal BloomParams bloom;
internal LensDistortionParams lensDistortion;
internal ChromaticAberrationParams chromaticAberration;
internal VignetteParams vignette;
internal FilmGrainParams filmGrain;
internal DitheringParams dither;
internal bool isActiveTargetBackBuffer;
internal bool useFastSRGBLinearConversion;
internal bool requireSRGBConversionBlit;
}
const string _CameraColorAfterPostProcessingName = "_CameraColorAfterPostProcessing";
/// <inheritdoc />
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
if(!m_IsValid)
return;
var colorLookup = volumeStack.GetComponent<ColorLookup>();
var colorAdjustments = volumeStack.GetComponent<ColorAdjustments>();
var tonemapping = volumeStack.GetComponent<Tonemapping>();
var bloom = volumeStack.GetComponent<Bloom>();
var lensDistortion = volumeStack.GetComponent<LensDistortion>();
var chromaticAberration = volumeStack.GetComponent<ChromaticAberration>();
var vignette = volumeStack.GetComponent<Vignette>();
var filmGrain = volumeStack.GetComponent<FilmGrain>();
var cameraData = frameData.Get<UniversalCameraData>();
var postProcessingData = frameData.Get<UniversalPostProcessingData>();
var resourceData = frameData.Get<UniversalResourceData>();
var sourceTexture = resourceData.cameraColor;
var overlayUITexture = resourceData.overlayUITexture;
var internalColorLut = resourceData.internalColorLut;
var userColorLut = TryGetCachedUserLutTextureHandle(renderGraph, colorLookup);
var bloomTexture = resourceData.bloom;
TextureHandle destinationTexture;
AccessFlags destinationAccessFlag = AccessFlags.Write;
if (resourceData.isActiveTargetBackBuffer)
{
destinationTexture = resourceData.backBufferColor;
// Full-screen blit to backbuffer with no alpha will overwrite every pixel, so use flag
// WriteAll for the compiler to set load op DontCare later and skip the color load
if (cameraData.isDefaultViewport && !cameraData.isAlphaOutputEnabled)
destinationAccessFlag = AccessFlags.WriteAll;
#if ENABLE_VR && ENABLE_XR_MODULE
if (cameraData.xr.enabled && cameraData.xr.hasValidVisibleMesh) // Not a full-screen blit
destinationAccessFlag = AccessFlags.Write;
#endif
}
else
{
//Due to camera stacking we could be rendering to a persistent texture that is not the backbuffer
destinationTexture = resourceData.destinationCameraColor.IsValid() ?
resourceData.destinationCameraColor
//TODO here we don't seem to apply PostProcessUtils.CreateCompatibleTexture. This seems completely out of sync with the other post process passes.
//However, changing it, breaks some tests because rendering with the color and depth after this pass when MSAA is on leads to mismatch.
//It seems completely arbitrary that we continue to write out color with MSAA if no other pp pass has been applied earlier.
: renderGraph.CreateTexture(sourceTexture, _CameraColorAfterPostProcessingName);
resourceData.destinationCameraColor = TextureHandle.nullHandle;
}
using (var builder = renderGraph.AddRasterRenderPass<UberPostPassData>(passName, out var passData, profilingSampler))
{
var srcDesc = sourceTexture.GetDescriptor(renderGraph);
#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 &= !Experimental.Rendering.XRSystem.foveatedRenderingCaps.HasFlag(FoveatedRenderingCaps.NonUniformRaster);
builder.EnableFoveatedRasterization(cameraData.xr.supportsFoveatedRendering && passSupportsFoveation);
// Multiview render regions are incompatible with the inner (foveal) pass in Quad View
if (!cameraData.xr.isQuadViewInnerPass)
{
builder.SetExtendedFeatureFlags(ExtendedFeatureFlags.MultiviewRenderRegionsCompatible);
}
}
#endif
builder.AllowGlobalStateModification(true);
passData.destinationTexture = destinationTexture;
builder.SetRenderAttachment(destinationTexture, 0, destinationAccessFlag);
passData.sourceTexture = sourceTexture;
builder.UseTexture(sourceTexture, AccessFlags.Read);
if(m_RenderOverlayUI && overlayUITexture.IsValid())
builder.UseTexture(overlayUITexture, AccessFlags.Read);
builder.UseTexture(internalColorLut, AccessFlags.Read);
if(userColorLut.IsValid())
builder.UseTexture(userColorLut, AccessFlags.Read);
if(bloomTexture.IsValid())
builder.UseTexture(bloomTexture, AccessFlags.Read);
passData.material = m_Material;
passData.cameraData = cameraData;
passData.useFastSRGBLinearConversion = postProcessingData.useFastSRGBLinearConversion;
passData.requireSRGBConversionBlit = m_RequireSRGBConversionBlit;
// HDR
passData.tonemapping = tonemapping;
passData.filteringOperation = m_FilteringOperation;
passData.hdrOperations = m_HdrOperations;
passData.isHdrGrading = postProcessingData.gradingMode == ColorGradingMode.HighDynamicRange;
passData.lut.Setup(colorAdjustments, colorLookup, postProcessingData.lutSize, internalColorLut, userColorLut);
passData.bloom.Setup(bloom, in srcDesc, bloomTexture);
passData.lensDistortion.Setup(lensDistortion, cameraData.isSceneViewCamera);
passData.chromaticAberration.Setup(chromaticAberration);
passData.vignette.Setup(vignette, srcDesc.width, srcDesc.height, cameraData.xr);
// Final pass effects
if (m_IsFinalPass)
{
passData.filmGrain.Setup(filmGrain, m_FilmGrainTextures, cameraData.pixelWidth, cameraData.pixelHeight);
passData.dither.Setup(m_DitherTexture, cameraData.pixelWidth, cameraData.pixelHeight);
}
passData.isActiveTargetBackBuffer = resourceData.isActiveTargetBackBuffer;
builder.SetRenderFunc(static (UberPostPassData data, RasterGraphContext context) =>
{
var cameraData = data.cameraData;
var material = data.material;
var filteringOperation = data.filteringOperation;
// Reset keywords
material.shaderKeywords = null;
switch (filteringOperation)
{
case FilteringOperation.Point:
material.EnableKeyword(ShaderKeywordStrings.PointSampling);
break;
case FilteringOperation.Linear: goto default;
default:
break;
}
data.lut.Apply(material);
if (data.bloom.IsActive())
data.bloom.Apply(material);
if(data.lensDistortion.IsActive())
data.lensDistortion.Apply(material);
if(data.chromaticAberration.IsActive())
data.chromaticAberration.Apply(material);
data.vignette.Apply(material);
if(data.filmGrain.IsActive())
data.filmGrain.Apply(material);
if(data.dither.IsActive())
data.dither.Apply(material);
if (data.requireSRGBConversionBlit)
material.EnableKeyword(ShaderKeywordStrings.LinearToSRGBConversion);
if (data.useFastSRGBLinearConversion)
material.EnableKeyword(ShaderKeywordStrings.UseFastSRGBLinearConversion);
if (cameraData.isAlphaOutputEnabled)
material.EnableKeyword(ShaderKeywordStrings._ENABLE_ALPHA_OUTPUT);
if (data.isHdrGrading)
{
material.EnableKeyword(ShaderKeywordStrings.HDRGrading);
}
else
{
switch (data.tonemapping.mode.value)
{
case TonemappingMode.Neutral: material.EnableKeyword(ShaderKeywordStrings.TonemapNeutral); break;
case TonemappingMode.ACES: material.EnableKeyword(ShaderKeywordStrings.TonemapACES); break;
default: break; // None
}
}
if(PostProcessUtils.RequireHDROutput(cameraData))
{
PostProcessUtils.SetupHDROutput(material, cameraData.hdrDisplayInformation, cameraData.hdrDisplayColorGamut, data.tonemapping, data.hdrOperations, cameraData.rendersOverlayUI);
RenderingUtils.SetupOffscreenUIViewportParams(material, ref cameraData.pixelRect, data.isActiveTargetBackBuffer);
}
#if ENABLE_VR && ENABLE_XR_MODULE
// Setup XR UV remapping for Quad View (used by all screen-space effects)
if (cameraData.xr != null && cameraData.xr.enabled && cameraData.xr.singlePassEnabled)
{
PostProcessUtils.SetupXRUVRemapping(material, cameraData.xr);
}
// Done with Uber, blit it
if (cameraData.xr.enabled && cameraData.xr.hasValidVisibleMesh)
PostProcessUtils.ScaleViewportAndDrawVisibilityMesh(context, data.sourceTexture, data.destinationTexture, data.cameraData, material, data.isActiveTargetBackBuffer);
else
#endif
PostProcessUtils.ScaleViewportAndBlit(context, data.sourceTexture, data.destinationTexture, data.cameraData, material, data.isActiveTargetBackBuffer);
});
}
if (!resourceData.isActiveTargetBackBuffer)
{
resourceData.cameraColor = destinationTexture;
}
}
#region ColorLut
TextureHandle TryGetCachedUserLutTextureHandle(RenderGraph renderGraph, ColorLookup colorLookup)
{
if (colorLookup.texture.value == null)
{
if (m_UserLut != null)
{
m_UserLut.Release();
m_UserLut = null;
}
}
else
{
if (m_UserLut == null || m_UserLut.externalTexture != colorLookup.texture.value)
{
m_UserLut?.Release();
m_UserLut = RTHandles.Alloc(colorLookup.texture.value);
}
}
return m_UserLut != null ? renderGraph.ImportTexture(m_UserLut) : TextureHandle.nullHandle;
}
internal struct LutParams
{
public TextureHandle internalLutTexture;
public TextureHandle activeUserLutTexture;
public Vector4 internalLutParams;
public Vector4 userLutParams;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Setup(ColorAdjustments colorAdjustments, ColorLookup colorLookup, int lutHeight, TextureHandle internalLutTexture, TextureHandle activeUserLutTexture)
{
this.internalLutTexture = internalLutTexture;
this.activeUserLutTexture = activeUserLutTexture;
CalcColorLutParams(colorAdjustments, colorLookup, lutHeight, out internalLutParams, out userLutParams);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Apply(Material material)
{
material.SetTexture(ShaderConstants._InternalLut, internalLutTexture);
material.SetTexture(ShaderConstants._UserLut, activeUserLutTexture);
material.SetVector(ShaderConstants._Lut_Params, internalLutParams);
material.SetVector(ShaderConstants._UserLut_Params, userLutParams);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void CalcColorLutParams(ColorAdjustments colorAdjustments, ColorLookup colorLookup, int lutHeight, out Vector4 internalLutParams, out Vector4 userLutParams)
{
Assertions.Assert.IsNotNull(colorAdjustments, "SetupColorLut colorAdjustments cannot be null.");
Assertions.Assert.IsNotNull(colorLookup, "SetupColorLut colorLookup cannot be null.");
int lutWidth = lutHeight * lutHeight;
float postExposureLinear = Mathf.Pow(2f, colorAdjustments.postExposure.value);
internalLutParams = new Vector4(1f / lutWidth, 1f / lutHeight, lutHeight - 1f, postExposureLinear);
userLutParams = !colorLookup.IsActive()
? Vector4.zero
: new Vector4(1f / colorLookup.texture.value.width,
1f / colorLookup.texture.value.height,
colorLookup.texture.value.height - 1f,
colorLookup.contribution.value);
}
}
#endregion
#region Bloom
internal struct BloomParams
{
public TextureHandle activeBloomTexture;
public Vector4 bloomParams;
public Texture dirtTexture;
public Vector4 dirtScaleOffset;
public float dirtIntensity;
public bool highQualityFiltering;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsActive()
{
return activeBloomTexture.IsValid();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Setup(Bloom bloom, in TextureDesc srcDesc, TextureHandle activeBloomTexture)
{
this.activeBloomTexture = activeBloomTexture;
CalcBloomParams(bloom, in srcDesc, out bloomParams, out highQualityFiltering, out dirtTexture, out dirtScaleOffset, out dirtIntensity);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Apply(Material material)
{
material.SetTexture(ShaderConstants._Bloom_Texture, activeBloomTexture);
material.SetVector(ShaderConstants._Bloom_Params, bloomParams);
material.SetTexture(ShaderConstants._LensDirt_Texture, dirtTexture);
material.SetVector(ShaderConstants._LensDirt_Params, dirtScaleOffset);
material.SetFloat(ShaderConstants._LensDirt_Intensity, dirtIntensity);
// Keyword setup - a bit convoluted as we're trying to save some variants in Uber...
if (highQualityFiltering)
material.EnableKeyword(dirtIntensity > 0f ? ShaderKeywordStrings.BloomHQDirt : ShaderKeywordStrings.BloomHQ);
else
material.EnableKeyword(dirtIntensity > 0f ? ShaderKeywordStrings.BloomLQDirt : ShaderKeywordStrings.BloomLQ);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void CalcBloomParams(Bloom bloom, in TextureDesc srcDesc, out Vector4 bloomParams, out bool highQualityFiltering, out Texture dirtTexture, out Vector4 dirtScaleOffset, out float dirtIntensity)
{
using (new ProfilingScope(URPProfilingSamplers.UberPostSetupBloomPass))
{
// Setup bloom on uber
var tint = bloom.tint.value.linear;
var luma = ColorUtils.Luminance(tint);
tint = luma > 0f ? tint * (1f / luma) : Color.white;
bloomParams = new Vector4(bloom.intensity.value, tint.r, tint.g, tint.b);
highQualityFiltering = bloom.highQualityFiltering.value;
// Setup lens dirtiness on uber
// Keep the aspect ratio correct & center the dirt texture, we don't want it to be
// stretched or squashed
dirtTexture = bloom.dirtTexture.value == null ? Texture2D.blackTexture : bloom.dirtTexture.value;
float dirtRatio = dirtTexture.width / (float)dirtTexture.height;
float screenRatio = srcDesc.width / (float)srcDesc.height;
dirtScaleOffset = new Vector4(1f, 1f, 0f, 0f);
// Must match _DIRT variant stripping in ShaderBuildPreprocessor.
dirtIntensity = bloom.dirtTexture.value == null ? 0f : 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;
}
}
}
}
#endregion
#region Lens Distortion
internal struct LensDistortionParams
{
public Vector4 lensDistortionParams1;
public Vector4 lensDistortionParams2;
public bool lensDistortionActive;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsActive()
{
return lensDistortionActive;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Setup(LensDistortion lensDistortion, bool isSceneViewCamera)
{
lensDistortionActive = lensDistortion.IsActive() && !isSceneViewCamera;
CalcLensDistortionParams(lensDistortion, out lensDistortionParams1, out lensDistortionParams2);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Apply(Material material)
{
material.SetVector(ShaderConstants._Distortion_Params1, lensDistortionParams1);
material.SetVector(ShaderConstants._Distortion_Params2, lensDistortionParams2);
material.EnableKeyword(ShaderKeywordStrings.Distortion);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static public void CalcLensDistortionParams(LensDistortion lensDistortion, out Vector4 lensDistortionParams1, out Vector4 lensDistortionParams2)
{
float amount = 1.6f * Mathf.Max(Mathf.Abs(lensDistortion.intensity.value * 100f), 1f);
float theta = Mathf.Deg2Rad * Mathf.Min(160f, amount);
float sigma = 2f * Mathf.Tan(theta * 0.5f);
var center = lensDistortion.center.value * 2f - Vector2.one;
lensDistortionParams1 = new Vector4(
center.x,
center.y,
Mathf.Max(lensDistortion.xMultiplier.value, 1e-4f),
Mathf.Max(lensDistortion.yMultiplier.value, 1e-4f)
);
lensDistortionParams2 = new Vector4(
lensDistortion.intensity.value >= 0f ? theta : 1f / theta,
sigma,
1f / lensDistortion.scale.value,
lensDistortion.intensity.value * 100f
);
}
}
#endregion
#region Chromatic Aberration
internal struct ChromaticAberrationParams
{
public float chromaticAberrationIntensity;
public bool chromaticAberrationActive;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsActive()
{
return chromaticAberrationActive;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Setup(ChromaticAberration chromaticAberration)
{
chromaticAberrationActive = chromaticAberration.IsActive();
chromaticAberrationIntensity = chromaticAberration.intensity.value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Apply(Material material)
{
material.SetFloat(ShaderConstants._Chroma_Params, chromaticAberrationIntensity * 0.05f);
material.EnableKeyword(ShaderKeywordStrings.ChromaticAberration);
}
}
#endregion
#region Vignette
internal struct VignetteParams
{
public Vector4 vignetteParams1;
public Vector4 vignetteParams2;
#if ENABLE_VR && ENABLE_XR_MODULE
public Vector4 vignetteXRCenter;
public bool hasXRCenter;
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Setup(Vignette vignette, int width, int height, Experimental.Rendering.XRPass xrPass)
{
CalcVignetteParams(vignette, width, height, xrPass, out vignetteParams1, out vignetteParams2);
#if ENABLE_VR && ENABLE_XR_MODULE
hasXRCenter = false;
if (xrPass != null && xrPass.enabled && xrPass.singlePassEnabled)
{
hasXRCenter = true;
Vector2 center = vignetteParams2;
var xrLayout = XRSystem.currentLayout;
if (xrPass.isQuadViewInnerPass)
{
// Second pass (inner views): Reuse the cached peripheral vignette center
// This ensures vignette is calculated in the outer UV space after remapping
vignetteXRCenter = xrLayout.quadView.cachedPeripheralVignetteCenter;
}
else
{
// First pass (peripheral/outer views): Calculate and cache the vignette center
vignetteXRCenter = xrPass.ApplyXRViewCenterOffset(center);
if (xrLayout != null)
xrLayout.quadView.cachedPeripheralVignetteCenter = vignetteXRCenter;
}
}
#endif
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Apply(Material material)
{
material.SetVector(ShaderConstants._Vignette_Params1, vignetteParams1);
material.SetVector(ShaderConstants._Vignette_Params2, vignetteParams2);
#if ENABLE_VR && ENABLE_XR_MODULE
if (hasXRCenter)
{
material.SetVector(ShaderConstants._Vignette_ParamsXR, vignetteXRCenter);
}
#endif
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static public void CalcVignetteParams(Vignette vignette, int width, int height, Experimental.Rendering.XRPass xrPass, out Vector4 vignetteParams1, out Vector4 vignetteParams2)
{
var color = vignette.color.value;
var center = vignette.center.value;
var aspectRatio = width / (float)height;
#if ENABLE_VR && ENABLE_XR_MODULE
if (xrPass != null && xrPass.enabled && !xrPass.singlePassEnabled)
{
// In multi-pass mode we need to modify the eye center with the values from .xy of the corrected
// center since the version of the shader that is not single-pass will use the value in _Vignette_Params2
center = xrPass.ApplyXRViewCenterOffset(center);
}
if (xrPass != null && xrPass.isQuadViewInnerPass)
{
// In quad view we need to also apply the aspect ratio correction to the vignette as the UV remapping will cause it to be stretched/squashed if not corrected
aspectRatio *= xrPass.uvScales.y / xrPass.uvScales.x;
}
#endif
vignetteParams1 = new Vector4(
color.r, color.g, color.b,
vignette.rounded.value ? aspectRatio : 1f
);
vignetteParams2 = new Vector4(
center.x, center.y,
vignette.intensity.value * 3f,
vignette.smoothness.value * 5f
);
}
}
#endregion
#region Film Grain
internal struct FilmGrainParams
{
public Texture activeGrainTexture;
public Vector4 tilingParams;
public Vector2 grainParams;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsActive()
{
return activeGrainTexture != null;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Setup(FilmGrain filmGrain, Texture2D[] filmGrainTextures, int pixelWidth, int pixelHeight)
{
activeGrainTexture = null; // Disable by default
if (filmGrain.IsActive())
{
CalcFilmGrainParams(filmGrain, filmGrainTextures, out activeGrainTexture, out grainParams);
tilingParams = PostProcessUtils.CalcNoiseTextureTilingParams(activeGrainTexture, pixelWidth, pixelHeight, PostProcessUtils.GetRandomOffset2D());
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Apply(Material material)
{
PostProcessUtils.ConfigureFilmGrainMaterial(material, activeGrainTexture, grainParams, tilingParams);
material.EnableKeyword(ShaderKeywordStrings.FilmGrain);
}
// NOTE: Procedural FilmGrain can be done using the custom texture with RenderTexture. No need to import it into the RenderGraph.
const float k_FilmGrainIntensityScale = 4f;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void CalcFilmGrainParams(FilmGrain filmGrain, Texture2D[] filmGrainTextures, out Texture grainTexture, out Vector2 grainParams)
{
if (filmGrain.type.value == FilmGrainLookup.Custom)
{
grainTexture = filmGrain.texture.value;
}
else
{
grainTexture = (filmGrainTextures != null && (int)filmGrain.type.value < filmGrainTextures.Length)
? filmGrainTextures[(int)filmGrain.type.value]
: null;
}
grainParams = new Vector2(filmGrain.intensity.value * k_FilmGrainIntensityScale, filmGrain.response.value);
}
}
#endregion
#region 8-bit Dithering
internal struct DitheringParams
{
public Texture activeDitherTexture;
public Vector4 tilingParams;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsActive()
{
return activeDitherTexture != null;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Setup(Texture ditherTexture, int pixelWidth, int pixelHeight)
{
activeDitherTexture = ditherTexture;
tilingParams = PostProcessUtils.CalcNoiseTextureTilingParams(ditherTexture, pixelWidth, pixelHeight, PostProcessUtils.GetRandomOffset2D());
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Apply(Material material)
{
PostProcessUtils.ConfigureDitheringMaterial(material, activeDitherTexture, tilingParams);
material.EnableKeyword(ShaderKeywordStrings.Dithering);
}
}
#endregion
// Precomputed shader ids to same some CPU cycles (mostly affects mobile)
internal static class ShaderConstants
{
// Uber
public static readonly int _Distortion_Params1 = Shader.PropertyToID("_Distortion_Params1");
public static readonly int _Distortion_Params2 = Shader.PropertyToID("_Distortion_Params2");
public static readonly int _Chroma_Params = Shader.PropertyToID("_Chroma_Params");
public static readonly int _Vignette_Params1 = Shader.PropertyToID("_Vignette_Params1");
public static readonly int _Vignette_Params2 = Shader.PropertyToID("_Vignette_Params2");
public static readonly int _Vignette_ParamsXR = Shader.PropertyToID("_Vignette_ParamsXR");
// Uber Lut
public static readonly int _InternalLut = Shader.PropertyToID("_InternalLut");
public static readonly int _Lut_Params = Shader.PropertyToID("_Lut_Params");
public static readonly int _UserLut = Shader.PropertyToID("_UserLut");
public static readonly int _UserLut_Params = Shader.PropertyToID("_UserLut_Params");
// Uber Bloom
public static readonly int _Bloom_Texture = Shader.PropertyToID("_Bloom_Texture");
public static readonly int _Bloom_Params = Shader.PropertyToID("_Bloom_Params");
public static readonly int _LensDirt_Texture = Shader.PropertyToID("_LensDirt_Texture");
public static readonly int _LensDirt_Params = Shader.PropertyToID("_LensDirt_Params");
public static readonly int _LensDirt_Intensity = Shader.PropertyToID("_LensDirt_Intensity");
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f2e2a7724f774533bd7fb6cc8d95b34d
timeCreated: 1748346510
@@ -0,0 +1,265 @@
using System;
using UnityEngine.Rendering.RenderGraphModule;
using System.Runtime.CompilerServices; // AggressiveInlining
namespace UnityEngine.Rendering.Universal
{
internal sealed class UpscalerPostProcessPass : PostProcessPass
{
public const string k_UpscaledColorTargetName = "CameraColorUpscaled";
Texture2D[] m_BlueNoise16LTex;
bool m_IsValid;
#if ENABLE_UPSCALER_FRAMEWORK
bool m_WarnedHardwareDrsTemporalUnsupported;
bool m_WarnedMissingMotionData;
#endif
public UpscalerPostProcessPass(Texture2D[] blueNoise16LTex)
{
this.renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing - 1;
this.profilingSampler = null; // Use default name
m_BlueNoise16LTex = blueNoise16LTex;
m_IsValid = m_BlueNoise16LTex != null && m_BlueNoise16LTex.Length > 0;
}
public override void Dispose()
{
m_IsValid = false;
}
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
#if ENABLE_UPSCALER_FRAMEWORK
if (!m_IsValid)
return;
UniversalPostProcessingData postProcessingData = frameData.Get<UniversalPostProcessingData>();
if (postProcessingData.activeUpscaler == null)
return;
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
if (cameraData.imageScalingMode != ImageScalingMode.Upscaling)
return;
// Skip temporal upscaling when the camera uses hardware DRS (ScalableBufferManager). A temporal upscaler
// reconstructs to full resolution mid-frame, but ScalableBufferManager is a single global scale with no per-stage
// render->display transition, so the post-upscale chain (UberPost, final blit) keeps writing into the
// ScalableBufferManager-scaled sub-rect and only that sub-rect of the screen updates.
// Gate on camera.allowDynamicResolution (the stable opt-in), not the live ScalableBufferManager factor,
// which would flip per-frame as the app crosses factor 1.0.
if (cameraData.camera.allowDynamicResolution && postProcessingData.activeUpscaler.isTemporal)
{
if (Debug.isDebugBuild && !m_WarnedHardwareDrsTemporalUnsupported)
{
m_WarnedHardwareDrsTemporalUnsupported = true;
Debug.LogWarning(
"Hardware Dynamic Resolution (Allow Dynamic Resolution / ScalableBufferManager) is not supported " +
$"with the temporal upscaler '{postProcessingData.activeUpscaler.name}' in URP yet (in any " +
"Resolution Mode); the upscaler is skipped and the camera falls back to hardware DRS without " +
"temporal upscaling. To use the temporal upscaler, disable Allow Dynamic Resolution on the camera" +
" and control the resolution via Render Scale or the upscaler's quality mode if available.");
}
return;
}
// Left the skip path: reset so re-entering it warns again.
if (Debug.isDebugBuild)
m_WarnedHardwareDrsTemporalUnsupported = false;
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
var sourceTexture = resourceData.cameraColor;
var srcDesc = sourceTexture.GetDescriptor(renderGraph);
// Create a context item containing upscaling inputs
UpscalingIO io = frameData.Create<UpscalingIO>();
io.cameraColor = sourceTexture;
io.cameraDepth = resourceData.cameraDepth;
io.motionVectorColor = resourceData.motionVectorColor;
io.motionVectorDomain = UpscalingIO.MotionVectorDomain.NDC;
io.motionVectorDirection = UpscalingIO.MotionVectorDirection.PreviousFrameToCurrentFrame;
io.jitteredMotionVectors = false; // URP has no jittering in MVs
// io.exposureTexture; // TODO: set exposure texture when available
io.preExposureValue = 1.0f; // TODO: set if exposure value is pre-multiplied
io.hdrDisplayInformation = cameraData.isHDROutputActive ? cameraData.hdrDisplayInformation : new HDROutputUtils.HDRDisplayInformation(-1, -1, -1, 160.0f);
io.postUpscaleResolution = new Vector2Int(cameraData.pixelWidth, cameraData.pixelHeight);
// Report DRS as active whenever the camera is aliased by hardware DRS / ScalableBufferManager (SBM)
// (descriptor.useDynamicScale), not only when the
// current factor is < 1.0: the upscaler bakes its dynamic-resolution state at context-creation time (e.g.
// FSR2's EnableDynamicResolution flag) and isn't recreated when the factor changes, so a context created at
// factor 1.0 would otherwise run with DRS off while later receiving a varying sub-rect. Use the SBM scale
// captured on cameraData at setup, not the live global, which another pass could mutate before this point.
Vector2 hwDrsScale = cameraData.hardwareDynamicResolutionScale;
bool cameraUsesHardwareDrs = cameraData.cameraTargetDescriptor.useDynamicScale;
io.dynamicResolution = cameraUsesHardwareDrs ? DynamicResolutionType.Hardware : (DynamicResolutionType?)null;
// preUpscaleResolution is the actually-rendered region (the descriptor scaled by the SBM factor), not the
// full descriptor allocation, so the upscaler reconstructs from what was rendered.
io.preUpscaleResolution = cameraUsesHardwareDrs
? new Vector2Int(
Mathf.CeilToInt(hwDrsScale.x * cameraData.cameraTargetDescriptor.width),
Mathf.CeilToInt(hwDrsScale.y * cameraData.cameraTargetDescriptor.height))
: new Vector2Int(cameraData.cameraTargetDescriptor.width, cameraData.cameraTargetDescriptor.height);
// The max render size = the full camera-target allocation (the descriptor). Under SBM the rendered region
// (preUpscaleResolution) is a sub-rect of it; without DRS they're equal. Upscalers allocate history at this.
io.maxPreUpscaleResolution = new Vector2Int(cameraData.cameraTargetDescriptor.width, cameraData.cameraTargetDescriptor.height);
cameraData.camera.TryGetComponent<UniversalAdditionalCameraData>(out var additionalCameraData);
MotionVectorsPersistentData motionData = additionalCameraData != null ? additionalCameraData.motionVectorsPersistentData : null;
if (motionData == null) // Per-camera motion data is required for upscaling (camera matrices, positions, previous-frame sizes).
{
if (Debug.isDebugBuild && !m_WarnedMissingMotionData)
{
m_WarnedMissingMotionData = true;
Debug.LogWarning("UpscalerPostProcessPass: camera has no UniversalAdditionalCameraData/" +
"MotionVectorsPersistentData, which the temporal upscaler requires; skipping upscaling for this camera.");
}
return;
}
if (Debug.isDebugBuild) // Saw valid motion data: reset so a later missing-data camera warns again.
m_WarnedMissingMotionData = false;
// Track the previous frame's render resolution for temporal upscalers via the per-camera persistent state.
io.previousPreUpscaleResolution = motionData.previousPreUpscaleResolution;
motionData.previousPreUpscaleResolution = io.preUpscaleResolution;
io.motionVectorTextureSize = io.preUpscaleResolution;
io.enableTexArray = cameraData.xr.enabled && cameraData.xr.singlePassEnabled;
io.cameraInstanceID = EntityId.ToULong(cameraData.camera.GetEntityId());
io.nearClipPlane = cameraData.camera.nearClipPlane;
io.farClipPlane = cameraData.camera.farClipPlane;
io.fieldOfViewDegrees = cameraData.camera.fieldOfView;
io.invertedDepth = SystemInfo.usesReversedZBuffer;
io.flippedY = SystemInfo.graphicsUVStartsAtTop;
io.flippedX = false;
io.hdrInput = Experimental.Rendering.GraphicsFormatUtility.IsHDRFormat(srcDesc.format);
io.numActiveViews = cameraData.xr.enabled ? cameraData.xr.viewCount : 1;
io.projectionMatrices = motionData.projectionStereo;
io.previousProjectionMatrices = motionData.previousProjectionStereo;
io.previousPreviousProjectionMatrices = motionData.previousPreviousProjectionStereo;
io.viewMatrices = motionData.viewStereo;
io.previousViewMatrices = motionData.previousViewStereo;
io.previousPreviousViewMatrices = motionData.previousPreviousViewStereo;
// Per-view world-space camera positions into persistent per-camera buffers (resized only on view-count
// change) to avoid a per-frame allocation. Mono reuses the stored camera position; each XR eye is at a
// distinct position, derived from its view matrix via CoreMatrixUtils.GetWorldPositionFromOrthonormalViewMatrix
// instead of a full inverse.
var camPosViews = motionData.GetWorldSpaceCameraPosViews(io.numActiveViews);
var prevCamPosViews = motionData.GetPreviousWorldSpaceCameraPosViews(io.numActiveViews);
var prevPrevCamPosViews = motionData.GetPreviousPreviousWorldSpaceCameraPosViews(io.numActiveViews);
if (io.numActiveViews == 1)
{
camPosViews[0] = motionData.worldSpaceCameraPos;
prevCamPosViews[0] = motionData.previousWorldSpaceCameraPos;
prevPrevCamPosViews[0] = motionData.previousPreviousWorldSpaceCameraPos;
}
else
{
for (int i = 0; i < io.numActiveViews; i++)
{
camPosViews[i] = CoreMatrixUtils.GetWorldPositionFromOrthonormalViewMatrix(motionData.viewStereo[i]);
prevCamPosViews[i] = CoreMatrixUtils.GetWorldPositionFromOrthonormalViewMatrix(motionData.previousViewStereo[i]);
prevPrevCamPosViews[i] = CoreMatrixUtils.GetWorldPositionFromOrthonormalViewMatrix(motionData.previousPreviousViewStereo[i]);
}
}
io.worldSpaceCameraPositions = camPosViews;
io.previousWorldSpaceCameraPositions = prevCamPosViews;
io.previousPreviousWorldSpaceCameraPositions = prevPrevCamPosViews;
io.resetHistory = cameraData.resetHistory;
io.frameIndex = TemporalAA.CalculateTaaFrameIndex(ref cameraData.taaSettings);
io.deltaTime = motionData.deltaTime;
io.previousDeltaTime = motionData.lastDeltaTime;
io.blueNoiseTextureSet = m_BlueNoise16LTex;
// The motion scaling feature is only active outside of test environments. If we allowed it to run
// during automated graphics tests, the results of each test run would be dependent on system
// performance.
#if LWRP_DEBUG_STATIC_POSTFX
io.enableMotionScaling = false;
#else
io.enableMotionScaling = true;
#endif
// Acquire the per-camera context for the active upscaler
// In XR multi-pass rendering, encode eye information into the camera ID to ensure separate contexts per eye
var upscaler = postProcessingData.activeUpscaler;
ulong viewId = io.cameraInstanceID;
if (cameraData.xr.enabled && !cameraData.xr.singlePassEnabled)
viewId = (ulong)HashCode.Combine(io.cameraInstanceID, cameraData.xr.multipassId);
// Fetch the framework-owned options for this upscaler (per-camera overrides are future work).
UpscalerOptions upscalerOptions = UniversalRenderPipeline.upscaling.GetGlobalOptions(upscaler);
io.context = UniversalRenderPipeline.upscaling.AcquireContext(
viewId,
upscaler,
upscalerOptions,
io.postUpscaleResolution
);
// Use jitter already computed during camera setup
io.subpixelJitter = cameraData.subpixelJitter;
// Per-frame settings (sharpness, etc.); upscalers read these from io.options, not from the context.
io.options = upscalerOptions;
// Insert the active upscaler's render graph passes
upscaler.RecordRenderGraph(renderGraph, frameData);
// Update the camera resolution to reflect the upscaled size
var dstDesc = io.cameraColor.GetDescriptor(renderGraph);
UpdateCameraResolution(renderGraph, cameraData, new Vector2Int(dstDesc.width, dstDesc.height));
// Use the output texture of upscaling
resourceData.cameraColor = io.cameraColor;
#endif
}
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).
static internal 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);
// Wrap constant modification into a pass to force graph execution timeline.
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
)
);
});
}
}
// Precomputed shader ids to same some CPU cycles (mostly affects mobile)
public static class ShaderConstants
{
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4083e05963a34a969d61631315469f1c
timeCreated: 1755806172
@@ -0,0 +1,75 @@
using System;
using UnityEngine.Rendering.RenderGraphModule;
namespace UnityEngine.Rendering.Universal
{
/// <summary>
/// Uses a compute shader to capture the depth and normal of the pixel under the cursor.
/// </summary>
internal class ProbeVolumeDebugPass : ScriptableRenderPass
{
ComputeShader m_ComputeShader;
/// <summary>
/// Creates a new <c>ProbeVolumeDebugPass</c> instance.
/// </summary>
public ProbeVolumeDebugPass(RenderPassEvent evt, ComputeShader computeShader)
{
profilingSampler = new ProfilingSampler("Dispatch APV Debug");
renderPassEvent = evt;
m_ComputeShader = computeShader;
}
class WriteApvData
{
public ComputeShader computeShader;
public BufferHandle resultBuffer;
public Vector2 clickCoordinates;
public TextureHandle depthBuffer;
public TextureHandle normalBuffer;
}
/// <summary>
/// Render graph entry point
/// </summary>
/// <param name="renderGraph"></param>
/// <param name="frameData"></param>
/// <param name="depthPyramidBuffer"></param>
/// <param name="normalBuffer"></param>
internal void Render(RenderGraph renderGraph, ContextContainer frameData, in TextureHandle depthPyramidBuffer, in TextureHandle normalBuffer)
{
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
if (!ProbeReferenceVolume.instance.isInitialized)
return;
if (ProbeReferenceVolume.instance.GetProbeSamplingDebugResources(cameraData.camera, out var resultBuffer, out Vector2 coords))
{
using (var builder = renderGraph.AddComputePass<WriteApvData>(passName, out var passData, profilingSampler))
{
passData.clickCoordinates = coords;
passData.computeShader = m_ComputeShader;
passData.resultBuffer = renderGraph.ImportBuffer(resultBuffer);
passData.depthBuffer = depthPyramidBuffer;
passData.normalBuffer = normalBuffer;
builder.UseBuffer(passData.resultBuffer, AccessFlags.Write);
builder.UseTexture(passData.depthBuffer, AccessFlags.Read);
builder.UseTexture(passData.normalBuffer, AccessFlags.Read);
builder.SetRenderFunc(static (WriteApvData data, ComputeGraphContext ctx) =>
{
int kernel = data.computeShader.FindKernel("ComputePositionNormal");
ctx.cmd.SetComputeTextureParam(data.computeShader, kernel, "_CameraDepthTexture", data.depthBuffer);
ctx.cmd.SetComputeTextureParam(data.computeShader, kernel, "_NormalBufferTexture", data.normalBuffer);
ctx.cmd.SetComputeVectorParam(data.computeShader, "_positionSS", new Vector4(data.clickCoordinates.x, data.clickCoordinates.y, 0.0f, 0.0f));
ctx.cmd.SetComputeBufferParam(data.computeShader, kernel, "_ResultBuffer", data.resultBuffer);
ctx.cmd.DispatchCompute(data.computeShader, kernel, 1, 1, 1);
});
}
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 14a56f221d48d58419103d8e8c070dd9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,364 @@
using System;
using System.Collections.Generic;
using UnityEngine.Rendering.RenderGraphModule;
using UnityEngine.Scripting.APIUpdating;
namespace UnityEngine.Rendering.Universal
{
/// <summary>
/// The scriptable render pass used with the render objects renderer feature.
/// </summary>
[MovedFrom(true, "UnityEngine.Experimental.Rendering.Universal")]
public partial class RenderObjectsPass : ScriptableRenderPass
{
RenderQueueType renderQueueType;
FilteringSettings m_FilteringSettings;
RenderObjects.CustomCameraSettings m_CameraSettings;
/// <summary>
/// The override material to use.
/// </summary>
public Material overrideMaterial { get; set; }
/// <summary>
/// The pass index to use with the override material.
/// </summary>
public int overrideMaterialPassIndex { get; set; }
/// <summary>
/// The override shader to use.
/// </summary>
public Shader overrideShader { get; set; }
/// <summary>
/// The pass index to use with the override shader.
/// </summary>
public int overrideShaderPassIndex { get; set; }
List<ShaderTagId> m_ShaderTagIdList = new List<ShaderTagId>();
private PassData m_PassData;
/// <summary>
/// Indicates whether this pass should use depth as input attachment.
/// When enabled, depth is read from tile memory instead of texture sampling (DX12, Vulkan only).
/// Requires depth to be read-only (no depth writes).
/// </summary>
private bool m_DepthInputAttachment;
/// <summary>
/// Sets the write and comparison function for depth.
/// </summary>
/// <param name="writeEnabled">Sets whether it should write to depth or not.</param>
/// <param name="function">The depth comparison function to use.</param>
[Obsolete("Use SetDepthState instead. #from(2023.1) #breakingFrom(2023.1)", true)]
public void SetDetphState(bool writeEnabled, CompareFunction function = CompareFunction.Less)
{
SetDepthState(writeEnabled, function);
}
/// <summary>
/// Sets the write and comparison function for depth.
/// </summary>
/// <param name="writeEnabled">Sets whether it should write to depth or not.</param>
/// <param name="function">The depth comparison function to use.</param>
public void SetDepthState(bool writeEnabled, CompareFunction function = CompareFunction.Less)
{
m_RenderStateBlock.mask |= RenderStateMask.Depth;
m_RenderStateBlock.depthState = new DepthState(writeEnabled, function);
}
/// <summary>
/// Sets whether this pass should use depth as input attachment.
/// This enables subpass depth reading on supported platforms (DX12, Vulkan).
/// When enabled, depth will be set as read-only in the RenderGraph.
/// </summary>
/// <param name="enable">True to enable depth input attachment</param>
internal void SetDepthInputAttachment(bool enable)
{
// The shader stripping relies on knowing about the feature, not the pass. So this can't correctly work if the pass is not added by the RenderObject feature.
m_DepthInputAttachment = enable;
}
/// <summary>
/// Sets up the stencil settings for the pass.
/// </summary>
/// <param name="reference">The stencil reference value.</param>
/// <param name="compareFunction">The comparison function to use.</param>
/// <param name="passOp">The stencil operation to use when the stencil test passes.</param>
/// <param name="failOp">The stencil operation to use when the stencil test fails.</param>
/// <param name="zFailOp">The stencil operation to use when the stencil test fails because of depth.</param>
public void SetStencilState(int reference, CompareFunction compareFunction, StencilOp passOp, StencilOp failOp, StencilOp zFailOp)
{
StencilState stencilState = StencilState.defaultValue;
stencilState.enabled = true;
stencilState.SetCompareFunction(compareFunction);
stencilState.SetPassOperation(passOp);
stencilState.SetFailOperation(failOp);
stencilState.SetZFailOperation(zFailOp);
m_RenderStateBlock.mask |= RenderStateMask.Stencil;
m_RenderStateBlock.stencilReference = reference;
m_RenderStateBlock.stencilState = stencilState;
}
RenderStateBlock m_RenderStateBlock;
/// <summary>
/// The constructor for render objects pass.
/// </summary>
/// <param name="profilerTag">The profiler tag used with the pass.</param>
/// <param name="renderPassEvent">Controls when the render pass executes.</param>
/// <param name="shaderTags">List of shader tags to render with.</param>
/// <param name="renderQueueType">The queue type for the objects to render.</param>
/// <param name="layerMask">The layer mask to use for creating filtering settings that control what objects get rendered.</param>
/// <param name="cameraSettings">The settings for custom cameras values.</param>
public RenderObjectsPass(string profilerTag, RenderPassEvent renderPassEvent, string[] shaderTags, RenderQueueType renderQueueType, int layerMask, RenderObjects.CustomCameraSettings cameraSettings)
{
profilingSampler = new ProfilingSampler(profilerTag);
Init(renderPassEvent, shaderTags, renderQueueType, layerMask, cameraSettings);
}
internal void Init(RenderPassEvent renderPassEvent, string[] shaderTags, RenderQueueType renderQueueType, int layerMask, RenderObjects.CustomCameraSettings cameraSettings)
{
m_PassData = new PassData();
this.renderPassEvent = renderPassEvent;
this.renderQueueType = renderQueueType;
this.overrideMaterial = null;
this.overrideMaterialPassIndex = 0;
this.overrideShader = null;
this.overrideShaderPassIndex = 0;
RenderQueueRange renderQueueRange = (renderQueueType == RenderQueueType.Transparent)
? RenderQueueRange.transparent
: RenderQueueRange.opaque;
m_FilteringSettings = new FilteringSettings(renderQueueRange, layerMask);
if (shaderTags != null && shaderTags.Length > 0)
{
foreach (var tag in shaderTags)
m_ShaderTagIdList.Add(new ShaderTagId(tag));
}
else
{
m_ShaderTagIdList.Add(new ShaderTagId("SRPDefaultUnlit"));
m_ShaderTagIdList.Add(new ShaderTagId("UniversalForward"));
m_ShaderTagIdList.Add(new ShaderTagId("UniversalForwardOnly"));
}
m_RenderStateBlock = new RenderStateBlock(RenderStateMask.Nothing);
m_CameraSettings = cameraSettings;
}
private static void ExecutePass(PassData passData, RasterCommandBuffer cmd, RendererList rendererList, bool isYFlipped)
{
Camera camera = passData.cameraData.camera;
if (passData.cameraData.xr.enabled && passData.isActiveTargetBackBuffer)
{
cmd.SetViewport(passData.cameraData.xr.GetViewport());
}
// In case of camera stacking we need to take the viewport rect from base camera
Rect pixelRect = passData.cameraData.pixelRect;
float cameraAspect = (float)pixelRect.width / (float)pixelRect.height;
if (passData.cameraSettings.overrideCamera)
{
if (passData.cameraData.xr.enabled)
{
Debug.LogWarning("RenderObjects pass is configured to override camera matrices. While rendering in stereo camera matrices cannot be overridden.");
}
else
{
Matrix4x4 projectionMatrix = Matrix4x4.Perspective(passData.cameraSettings.cameraFieldOfView, cameraAspect,
camera.nearClipPlane, camera.farClipPlane);
projectionMatrix = GL.GetGPUProjectionMatrix(projectionMatrix, isYFlipped);
Matrix4x4 viewMatrix = passData.cameraData.GetViewMatrix();
Vector4 cameraTranslation = viewMatrix.GetColumn(3);
viewMatrix.SetColumn(3, cameraTranslation + passData.cameraSettings.offset);
RenderingUtils.SetViewAndProjectionMatrices(cmd, viewMatrix, projectionMatrix, false);
}
}
var activeDebugHandler = GetActiveDebugHandler(passData.cameraData);
if (activeDebugHandler != null)
{
passData.debugRendererLists.DrawWithRendererList(cmd);
}
else
{
cmd.DrawRendererList(rendererList);
}
if (passData.cameraSettings.overrideCamera && passData.cameraSettings.restoreCamera && !passData.cameraData.xr.enabled)
{
RenderingUtils.SetViewAndProjectionMatrices(cmd, passData.cameraData.GetViewMatrix(), GL.GetGPUProjectionMatrix(passData.cameraData.GetProjectionMatrix(0), isYFlipped), false);
}
}
private class PassData
{
internal RenderObjects.CustomCameraSettings cameraSettings;
internal RenderPassEvent renderPassEvent;
internal TextureHandle color;
internal RendererListHandle rendererListHdl;
internal DebugRendererLists debugRendererLists;
internal UniversalCameraData cameraData;
// Required for code sharing purpose between RG and non-RG.
internal RendererList rendererList;
internal bool depthInputAttachment;
internal bool isActiveTargetBackBuffer;
}
private void InitPassData(UniversalCameraData cameraData, ref PassData passData, bool isActiveTargetBackBuffer = false)
{
passData.cameraSettings = m_CameraSettings;
passData.renderPassEvent = renderPassEvent;
passData.cameraData = cameraData;
passData.isActiveTargetBackBuffer = isActiveTargetBackBuffer;
passData.depthInputAttachment = false;
}
private void InitRendererLists(UniversalRenderingData renderingData, UniversalLightData lightData,
ref PassData passData, RenderGraph renderGraph)
{
SortingCriteria sortingCriteria = (renderQueueType == RenderQueueType.Transparent)
? SortingCriteria.CommonTransparent
: passData.cameraData.defaultOpaqueSortFlags;
DrawingSettings drawingSettings = RenderingUtils.CreateDrawingSettings(m_ShaderTagIdList, renderingData,
passData.cameraData, lightData, sortingCriteria);
drawingSettings.overrideMaterial = overrideMaterial;
drawingSettings.overrideMaterialPassIndex = overrideMaterialPassIndex;
drawingSettings.overrideShader = overrideShader;
drawingSettings.overrideShaderPassIndex = overrideShaderPassIndex;
var activeDebugHandler = GetActiveDebugHandler(passData.cameraData);
if (activeDebugHandler != null)
{
passData.debugRendererLists = activeDebugHandler.CreateRendererListsWithDebugRenderState(renderGraph,
ref renderingData.cullResults, ref drawingSettings, ref m_FilteringSettings, ref m_RenderStateBlock);
}
else
{
RenderingUtils.CreateRendererListWithRenderStateBlock(renderGraph, ref renderingData.cullResults, drawingSettings,
m_FilteringSettings, m_RenderStateBlock, ref passData.rendererListHdl);
}
}
/// <inheritdoc />
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
UniversalRenderingData renderingData = frameData.Get<UniversalRenderingData>();
UniversalLightData lightData = frameData.Get<UniversalLightData>();
using (var builder = renderGraph.AddRasterRenderPass<PassData>(passName, out var passData, profilingSampler))
{
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
InitPassData(cameraData, ref passData, resourceData.isActiveTargetBackBuffer);
passData.color = resourceData.activeColorTexture;
builder.SetRenderAttachment(resourceData.activeColorTexture, 0, AccessFlags.Write);
if (resourceData.activeDepthTexture.IsValid())
{
// Configure depth attachment based on input attachment setting
if (m_DepthInputAttachment && SystemInfo.supportsDepthAttachmentAsInputAttachment)
{
// Input attachment mode: depth is read-only, accessed from tile memory
builder.SetExtendedFeatureFlags(ExtendedFeatureFlags.DepthAttachmentAsInputAttachment);
builder.SetInputAttachment(resourceData.activeDepthTexture, 0, AccessFlags.Read);
passData.depthInputAttachment = true;
}
else if (cameraData.imageScalingMode != ImageScalingMode.Upscaling || passData.renderPassEvent != RenderPassEvent.AfterRenderingPostProcessing)
{
builder.SetRenderAttachmentDepth(resourceData.activeDepthTexture, AccessFlags.ReadWrite);
}
}
TextureHandle mainShadowsTexture = resourceData.mainShadowsTexture;
TextureHandle additionalShadowsTexture = resourceData.additionalShadowsTexture;
if (mainShadowsTexture.IsValid())
builder.UseTexture(mainShadowsTexture, AccessFlags.Read);
if (additionalShadowsTexture.IsValid())
builder.UseTexture(additionalShadowsTexture, AccessFlags.Read);
TextureHandle[] dBufferHandles = resourceData.dBuffer;
for (int i = 0; i < dBufferHandles.Length; ++i)
{
TextureHandle dBuffer = dBufferHandles[i];
if (dBuffer.IsValid())
builder.UseTexture(dBuffer, AccessFlags.Read);
}
TextureHandle ssaoTexture = resourceData.ssaoTexture;
if (ssaoTexture.IsValid())
builder.UseTexture(ssaoTexture, AccessFlags.Read);
InitRendererLists(renderingData, lightData, ref passData, renderGraph);
var activeDebugHandler = GetActiveDebugHandler(passData.cameraData);
if (activeDebugHandler != null)
{
passData.debugRendererLists.PrepareRendererListForRasterPass(builder);
}
else
{
builder.UseRendererList(passData.rendererListHdl);
}
builder.AllowGlobalStateModification(true);
if (cameraData.xr.enabled)
{
bool passSupportsFoveation = cameraData.xrUniversal.canFoveateIntermediatePasses || resourceData.isActiveTargetBackBuffer;
builder.EnableFoveatedRasterization(cameraData.xr.supportsFoveatedRendering && passSupportsFoveation);
// Multiview render regions are incompatible with the inner (foveal) pass in Quad View
if (!cameraData.xr.isQuadViewInnerPass)
{
builder.SetExtendedFeatureFlags(ExtendedFeatureFlags.MultiviewRenderRegionsCompatible);
}
}
builder.SetRenderFunc(static (PassData data, RasterGraphContext rgContext) =>
{
var isYFlipped = RenderingUtils.IsHandleYFlipped(rgContext, in data.color);
// Set shader keywords for depth input attachment
if (data.depthInputAttachment)
{
switch (data.cameraData.cameraTargetDescriptor.msaaSamples)
{
case 8:
case 4:
case 2:
rgContext.cmd.SetKeyword(ShaderGlobalKeywords.DEPTH_AS_INPUT_ATTACHMENT, false);
rgContext.cmd.SetKeyword(ShaderGlobalKeywords.DEPTH_AS_INPUT_ATTACHMENT_MSAA, true);
break;
// MSAA disabled
default:
rgContext.cmd.SetKeyword(ShaderGlobalKeywords.DEPTH_AS_INPUT_ATTACHMENT, true);
rgContext.cmd.SetKeyword(ShaderGlobalKeywords.DEPTH_AS_INPUT_ATTACHMENT_MSAA, false);
break;
}
}
else
{
// Ensure keywords are disabled
rgContext.cmd.SetKeyword(ShaderGlobalKeywords.DEPTH_AS_INPUT_ATTACHMENT, false);
rgContext.cmd.SetKeyword(ShaderGlobalKeywords.DEPTH_AS_INPUT_ATTACHMENT_MSAA, false);
}
ExecutePass(data, rgContext.cmd, data.rendererListHdl, isYFlipped);
});
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dbd10d839f22d4127aeb9851dfed8caa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bf53d7784bab3d449a906d9816d992bb
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,144 @@
using System;
using UnityEngine.Rendering.RenderGraphModule;
namespace UnityEngine.Rendering.Universal
{
// Alchemy Ambient Occlusion (AAO) Pass - Standard SSAO mode using raster shaders.
internal class AAOPass : ScriptableRenderPass, IDisposable
{
private readonly bool m_SupportsR8RenderTextureFormat = SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.R8);
private int m_BlueNoiseTextureIndex = 0;
private Material m_Material;
private Texture2D[] m_BlueNoiseTextures;
private SSAOUtils.CameraViewData m_CameraViewData = SSAOUtils.CameraViewData.Create();
private SSAOUtils.BlurTypes m_BlurType = SSAOUtils.BlurTypes.Bilateral;
private ProfilingSampler m_ProfilingSampler = URPProfilingSamplers.SSAO;
private ScreenSpaceAmbientOcclusionSettings m_CurrentSettings;
private SSAOUtils.SSAOMaterialParams m_SSAOParamsPrev = new SSAOUtils.SSAOMaterialParams();
private bool m_WarnedMissingTextures;
internal AAOPass(Shader shader, Texture2D[] blueNoiseTextures)
{
m_CurrentSettings = new ScreenSpaceAmbientOcclusionSettings();
m_Material = CoreUtils.CreateEngineMaterial(shader);
Debug.Assert(m_Material != null, "AAOPass: Failed to create material from shader.");
m_BlueNoiseTextures = blueNoiseTextures;
}
// Sets up AAO pass using renderer feature settings. Returns false if the pass should be skipped.
internal bool Setup(ScreenSpaceAmbientOcclusionSettings featureSettings, ScreenSpaceAmbientOcclusionSettings.DepthSource depthSource)
{
m_CurrentSettings = featureSettings;
m_CurrentSettings.Source = depthSource;
#if MODERN_SSAO
m_CurrentSettings.Mode = ScreenSpaceAmbientOcclusionMode.Standard;
#endif
m_BlurType = SSAOUtils.GetBlurType(m_CurrentSettings.BlurQuality);
return m_CurrentSettings.Intensity > 0.0f
&& m_CurrentSettings.Radius > 0.0f
&& m_CurrentSettings.Falloff > 0.0f;
}
#if MODERN_SSAO
// Sets up AAO pass using active volume for Standard mode. Returns false if the pass should be skipped.
internal bool Setup(ScreenSpaceAmbientOcclusionVolumeOverride ssaoVolume, bool usesDeferredLighting)
{
ApplyVolumeSettings(m_CurrentSettings, ssaoVolume, usesDeferredLighting);
m_BlurType = SSAOUtils.GetBlurType(m_CurrentSettings.BlurQuality);
return ssaoVolume.IsActive();
}
private static void ApplyVolumeSettings(ScreenSpaceAmbientOcclusionSettings settings, ScreenSpaceAmbientOcclusionVolumeOverride volume, bool usesDeferredLighting)
{
settings.Mode = ScreenSpaceAmbientOcclusionMode.Standard;
settings.Intensity = volume.intensity;
settings.Radius = volume.radius;
settings.DirectLightingStrength = volume.directLightingStrength;
settings.Falloff = volume.falloffDistance;
settings.Downsample = volume.downsample;
settings.AfterOpaque = volume.afterOpaque;
settings.AOMethod = volume.method == ScreenSpaceAmbientOcclusionNoiseMethod.BlueNoise ? ScreenSpaceAmbientOcclusionSettings.AOMethodOptions.BlueNoise : ScreenSpaceAmbientOcclusionSettings.AOMethodOptions.InterleavedGradient;
settings.Samples = volume.sampleCount switch
{
ScreenSpaceAmbientOcclusionSampleCount.Low => ScreenSpaceAmbientOcclusionSettings.AOSampleOption.Low,
ScreenSpaceAmbientOcclusionSampleCount.High => ScreenSpaceAmbientOcclusionSettings.AOSampleOption.High,
_ => ScreenSpaceAmbientOcclusionSettings.AOSampleOption.Medium
};
settings.Source = volume.depthSource == ScreenSpaceAmbientOcclusionDepthSource.Depth ? ScreenSpaceAmbientOcclusionSettings.DepthSource.Depth : ScreenSpaceAmbientOcclusionSettings.DepthSource.DepthNormals;
settings.NormalSamples = volume.normalQuality switch
{
ScreenSpaceAmbientOcclusionNormalQuality.Low => ScreenSpaceAmbientOcclusionSettings.NormalQuality.Low,
ScreenSpaceAmbientOcclusionNormalQuality.High => ScreenSpaceAmbientOcclusionSettings.NormalQuality.High,
_ => ScreenSpaceAmbientOcclusionSettings.NormalQuality.Medium
};
settings.BlurQuality = volume.blurQuality switch
{
ScreenSpaceAmbientOcclusionBlurQuality.Low => ScreenSpaceAmbientOcclusionSettings.BlurQualityOptions.Low,
ScreenSpaceAmbientOcclusionBlurQuality.High => ScreenSpaceAmbientOcclusionSettings.BlurQualityOptions.High,
_ => ScreenSpaceAmbientOcclusionSettings.BlurQualityOptions.Medium
};
// Force to use Depth + Normals textures if deferred rendering
if (usesDeferredLighting)
settings.Source = ScreenSpaceAmbientOcclusionSettings.DepthSource.DepthNormals;
}
#endif
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
TextureHandle cameraDepthTexture = resourceData.cameraDepthTexture;
TextureHandle cameraNormalsTexture = resourceData.cameraNormalsTexture;
if (!cameraDepthTexture.IsValid() || (m_CurrentSettings.Source == ScreenSpaceAmbientOcclusionSettings.DepthSource.DepthNormals && !cameraNormalsTexture.IsValid()))
{
if (Debug.isDebugBuild && !m_WarnedMissingTextures)
{
m_WarnedMissingTextures = true;
Debug.LogWarning("Screen Space Ambient Occlusion: skipped because the active renderer does not provide the required camera depth/normals textures.");
}
return;
}
if (Debug.isDebugBuild) // Saw valid textures: reset so a later renderer without them warns again.
m_WarnedMissingTextures = false;
// Builds a TextureDesc compatible with the active camera color target, safe for both intermediate render textures and imported back-buffer handles.
TextureDesc cameraColorDesc = SSAOUtils.GetCameraColorDescriptor(renderGraph, resourceData.activeColorTexture, cameraData.camera.allowDynamicResolution);
SSAOUtils.CreateRenderTextureHandles(renderGraph, resourceData, cameraColorDesc,
m_CurrentSettings, m_SupportsR8RenderTextureFormat, m_BlurType, enableRandomWrite: false,
out TextureHandle aoTexture, out TextureHandle blurTexture, out TextureHandle temporalTexture, out TextureHandle finalTexture);
SSAOUtils.SetupCameraViewMatrices(cameraData, ref m_CameraViewData);
m_BlueNoiseTextureIndex = SSAOUtils.AdvanceBlueNoiseIndex(m_CurrentSettings, m_BlueNoiseTextures, m_BlueNoiseTextureIndex);
SSAOUtils.SetupKeywordsAndParameters(m_Material, m_CurrentSettings, cameraData, cameraColorDesc, ref m_CameraViewData, m_BlueNoiseTextures, m_BlueNoiseTextureIndex, ref m_SSAOParamsPrev);
SSAOUtils.RecordRasterAOPass(renderGraph, m_ProfilingSampler, m_Material, m_CurrentSettings, aoTexture, cameraDepthTexture, cameraNormalsTexture, cameraColorDesc);
SSAOUtils.RecordBlurChain(renderGraph, m_ProfilingSampler, m_Material, m_CurrentSettings, cameraData, aoTexture, blurTexture, finalTexture);
if (!m_CurrentSettings.AfterOpaque)
SSAOUtils.RecordCleanupPass(renderGraph, m_ProfilingSampler, m_CurrentSettings.DirectLightingStrength, finalTexture);
}
public override void OnCameraCleanup(CommandBuffer cmd)
{
if (cmd == null)
throw new ArgumentNullException("cmd");
if (!m_CurrentSettings.AfterOpaque)
cmd.SetKeyword(ShaderGlobalKeywords.ScreenSpaceOcclusion, false);
}
public void Dispose()
{
CoreUtils.Destroy(m_Material);
m_Material = null;
m_SSAOParamsPrev = default;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 60bde6145e88f154a99bf3cb5a724641
@@ -0,0 +1,802 @@
#if MODERN_SSAO
using System;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
namespace UnityEngine.Rendering.Universal
{
// Ground Truth Ambient Occlusion (GTAO) Pass - Handles GTAO mode with compute shader and raster-fragment fallback.
internal class GTAOPass : ScriptableRenderPass, IDisposable
{
// Shared state
private readonly bool m_SupportsR8RenderTextureFormat = SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.R8);
private readonly bool m_SupportsComputeShader = SystemInfo.supportsComputeShaders;
private int m_BlueNoiseTextureIndex = 0;
private Material m_Material;
private Texture2D[] m_BlueNoiseTextures;
private SSAOUtils.CameraViewData m_CameraViewData = SSAOUtils.CameraViewData.Create();
private SSAOUtils.BlurTypes m_BlurType = SSAOUtils.BlurTypes.Bilateral;
private ProfilingSampler m_ProfilingSampler = URPProfilingSamplers.SSAO;
private ScreenSpaceAmbientOcclusionSettings m_CurrentSettings;
private bool m_WarnedMissingTextures;
// Raster path state
private SSAOUtils.SSAOMaterialParams m_SSAOParamsPrev = new SSAOUtils.SSAOMaterialParams();
// Compute path state
private ComputePathState m_ComputePathState;
// Constants
private const float k_DegreesPerRotation = 360.0f;
private const int k_TemporalOffsetCount = 4;
// GTAO-only Shader Property IDs
internal static class ShaderIDs
{
// Compute kernel I/O bindings
public static readonly int _CameraDepthTexture = Shader.PropertyToID("_CameraDepthTexture");
public static readonly int _AOOutput = Shader.PropertyToID("_AOOutput");
public static readonly int _BlurOutput = Shader.PropertyToID("_BlurOutput");
public static readonly int _OcclusionTexture = Shader.PropertyToID("_OcclusionTexture");
public static readonly int _AOOutputHistory = Shader.PropertyToID("_AOOutputHistory");
public static readonly int _FinalOutput = Shader.PropertyToID("_FinalOutput");
// Temporal filter
public static readonly int _AOPackedHistory = Shader.PropertyToID("_AOPackedHistory");
public static readonly int _MotionVectorTexture = Shader.PropertyToID("_MotionVectorTexture");
public static readonly int _SSAOHistoryLength = Shader.PropertyToID("_SSAOHistoryLength");
public static readonly int _SSAOGhostingMitigation = Shader.PropertyToID("_SSAOGhostingMitigation");
public static readonly int _SSAOTemporalRotation = Shader.PropertyToID("_SSAOTemporalRotation");
public static readonly int _SSAOTemporalOffset = Shader.PropertyToID("_SSAOTemporalOffset");
// GTAO compute parameters
public static readonly int _GTAODirectionCount = Shader.PropertyToID("_GTAODirectionCount");
public static readonly int _GTAOStepCount = Shader.PropertyToID("_GTAOStepCount");
public static readonly int _GTAOSpatialFilter = Shader.PropertyToID("_GTAOSpatialFilter");
public static readonly int _GTAOBoxFilterStep = Shader.PropertyToID("_GTAOBoxFilterStep");
}
// Temporal rotation/offset arrays for GTAO temporal filtering
private static readonly float[] s_TemporalRotations = { 60.0f, 300.0f, 180.0f, 240.0f, 120.0f, 0.0f };
// Structs
private struct LocalKeywordSet
{
public LocalKeyword blueNoise;
public LocalKeyword interleavedGradient;
public LocalKeyword temporalFiltering;
public LocalKeyword orthographic;
public void Init(ComputeShader cs)
{
blueNoise = new(cs, ScreenSpaceAmbientOcclusionKeywords.k_AOBlueNoiseKeyword);
interleavedGradient = new(cs, ScreenSpaceAmbientOcclusionKeywords.k_AOInterleavedGradientKeyword);
temporalFiltering = new(cs, ScreenSpaceAmbientOcclusionKeywords.k_TemporalFilteringKeyword);
orthographic = new(cs, ScreenSpaceAmbientOcclusionKeywords.k_OrthographicCameraKeyword);
}
}
private struct ComputePathState
{
public ComputeShader shader;
public int gtaoKernel;
public int blurHKernel;
public int blurVKernel;
public int boxSpatialFilterKernel;
public int temporalKernel;
public int copyHistoryKernel;
public int finalBlitKernel;
public LocalKeywordSet keywords;
public RTHandle blueNoiseRTHandle;
public void Init(ComputeShader cs)
{
Debug.Assert(SystemInfo.supportsComputeShaders, "ComputePathState must not be initialized when the platform does not support compute shaders.");
shader = cs;
if (shader == null)
return;
Debug.Assert(
shader.HasKernel("ComputeAO") &&
shader.HasKernel("BilateralBlurH") &&
shader.HasKernel("BilateralBlurV") &&
shader.HasKernel("BoxSpatialFilter") &&
shader.HasKernel("TemporalFilter") &&
shader.HasKernel("CopyHistory") &&
shader.HasKernel("FinalBlit"),
"The GTAO compute shader does not have the expected kernels.");
gtaoKernel = shader.FindKernel("ComputeAO");
blurHKernel = shader.FindKernel("BilateralBlurH");
blurVKernel = shader.FindKernel("BilateralBlurV");
boxSpatialFilterKernel = shader.FindKernel("BoxSpatialFilter");
temporalKernel = shader.FindKernel("TemporalFilter");
copyHistoryKernel = shader.FindKernel("CopyHistory");
finalBlitKernel = shader.FindKernel("FinalBlit");
keywords.Init(shader);
}
public void Release()
{
blueNoiseRTHandle?.Release();
blueNoiseRTHandle = null;
}
}
private struct GTAOComputeParams
{
public Vector4 ssaoParams;
public Vector4 ssaoParams2;
public Vector4 depthToViewParams;
public Vector4 sourceSize;
public Vector4 blueNoiseParams;
public float historyLength;
public float ghostingMitigation;
public Vector4 projectionParams2;
public bool orthographicCamera;
public float temporalRotation;
public int temporalOffset;
}
private struct TemporalHistoryState
{
public TextureHandle historyReadTexture;
public TextureHandle historyWriteTexture;
public SSAOHistory ssaoHistory;
public bool historyReady;
public bool isNewFrame;
}
// Pass Data Classes
private class GTAOComputePassData
{
public ComputeShader cs;
public int kernel;
public Vector2Int dispatchSize;
public bool useBlueNoise;
public LocalKeywordSet localKeywords;
public bool willRunTemporalFilter;
public int directionCount;
public int stepCount;
public ScreenSpaceAmbientOcclusionSpatialFilter spatialFilter;
public GTAOComputeParams computeParams;
public Matrix4x4[] cameraViewProjections = new Matrix4x4[2];
public Vector4[] cameraTopLeftCorner = new Vector4[2];
public Vector4[] cameraXExtent = new Vector4[2];
public Vector4[] cameraYExtent = new Vector4[2];
public Vector4[] cameraZExtent = new Vector4[2];
public TextureHandle depthTexture;
public TextureHandle normalsTexture;
public TextureHandle blueNoiseTexture;
public TextureHandle aoTexture;
}
private class GTAOSingleDispatchPassData
{
public ComputeShader cs;
public int kernel;
public int dstPropertyId;
public Vector2Int dispatchSize;
public TextureHandle srcTexture;
public TextureHandle depthTexture;
public TextureHandle dstTexture;
public float boxFilterStep;
}
private class GTAOCopyHistoryPassData
{
public ComputeShader cs;
public int kernel;
public Vector2Int dispatchSize;
public TextureHandle srcTexture;
public TextureHandle depthTexture;
public TextureHandle historyTexture;
}
private class GTAOTemporalPassData
{
public ComputeShader cs;
public int kernel;
public Vector2Int dispatchSize;
public TextureHandle aoTexture;
public TextureHandle depthTexture;
public TextureHandle historyReadTexture;
public TextureHandle historyWriteTexture;
public TextureHandle motionVectorTexture;
public TextureHandle temporalTexture;
public ScreenSpaceAmbientOcclusionSpatialFilter spatialFilter;
}
private class GTAOFinalBlitPassData
{
public ComputeShader cs;
public int kernel;
public Vector2Int dispatchSize;
public float directLightingStrength;
public TextureHandle srcTexture;
public TextureHandle finalTexture;
}
private class GTAOAfterOpaqueBlitPassData
{
public Material material;
public Vector4 sourceSize;
public Vector4 ssaoParams;
public int shaderPass;
public TextureHandle sourceTexture;
public TextureHandle targetTexture;
}
internal GTAOPass(Shader shader, Texture2D[] blueNoiseTextures)
{
m_CurrentSettings = new ScreenSpaceAmbientOcclusionSettings();
m_Material = CoreUtils.CreateEngineMaterial(shader);
m_BlueNoiseTextures = blueNoiseTextures;
if (m_SupportsComputeShader && GraphicsSettings.TryGetRenderPipelineSettings<ScreenSpaceAmbientOcclusionCoreResources>(out var ssaoCoreResources))
m_ComputePathState.Init(ssaoCoreResources.GTAOComputeShader);
}
// Sets up GTAO pass using active volume. Returns false if SSAO should be skipped.
internal bool Setup(ScreenSpaceAmbientOcclusionVolumeOverride ssaoVolume)
{
ApplyVolumeSettings(m_CurrentSettings, ssaoVolume);
m_BlurType = SSAOUtils.GetBlurType(m_CurrentSettings.BlurQuality);
return ssaoVolume.IsActive();
}
private static void ApplyVolumeSettings(ScreenSpaceAmbientOcclusionSettings settings, ScreenSpaceAmbientOcclusionVolumeOverride volume)
{
// Common parameters
settings.Mode = ScreenSpaceAmbientOcclusionMode.GTAO;
settings.UseComputeShader = volume.useComputeShader;
settings.Intensity = volume.intensity;
settings.Radius = volume.radius;
settings.DirectLightingStrength = volume.directLightingStrength;
settings.Falloff = volume.falloffDistance;
settings.Downsample = volume.downsample;
settings.AfterOpaque = volume.afterOpaque;
settings.AOMethod = volume.method == ScreenSpaceAmbientOcclusionNoiseMethod.BlueNoise ? ScreenSpaceAmbientOcclusionSettings.AOMethodOptions.BlueNoise : ScreenSpaceAmbientOcclusionSettings.AOMethodOptions.InterleavedGradient;
settings.Samples = volume.sampleCount switch
{
ScreenSpaceAmbientOcclusionSampleCount.Low => ScreenSpaceAmbientOcclusionSettings.AOSampleOption.Low,
ScreenSpaceAmbientOcclusionSampleCount.High => ScreenSpaceAmbientOcclusionSettings.AOSampleOption.High,
_ => ScreenSpaceAmbientOcclusionSettings.AOSampleOption.Medium
};
// GTAO mode parameters
settings.GTAOMinimumRadiusInPixels = volume.minimumRadiusInPixels;
settings.Source = ScreenSpaceAmbientOcclusionSettings.DepthSource.DepthNormals;
settings.BlurQuality = ScreenSpaceAmbientOcclusionSettings.BlurQualityOptions.High;
settings.NormalSamples = ScreenSpaceAmbientOcclusionSettings.NormalQuality.High;
if (volume.useComputeShader)
{
settings.GTAODirectionCount = volume.directionCount;
settings.GTAOStepCount = volume.stepCount;
settings.GTAOSpatialFilter = volume.spatialFilter;
settings.GTAOTemporalFilterEnabled = volume.temporalFilter;
settings.GTAOGhostingMitigation = volume.ghostingMitigation;
settings.GTAOHistoryLength = volume.historyLength;
}
}
// ---- GTAO-specific helpers ----
internal static void CalculateGTAOViewParams(ScreenSpaceAmbientOcclusionSettings settings, UniversalCameraData cameraData,
bool orthographicCamera, float radius, in TextureDesc cameraColorDesc, out Vector4 ssaoParams2, out Vector4 depthToViewParams)
{
if (settings.Mode == ScreenSpaceAmbientOcclusionMode.Standard)
{
ssaoParams2 = Vector4.zero;
depthToViewParams = Vector4.zero;
return;
}
float invHalfTanFOV = cameraData.camera.projectionMatrix.m11;
float downsampleMultiplier = 1.0f / (settings.Downsample ? 2 : 1);
Vector2 runningRes = new Vector2(cameraColorDesc.width, cameraColorDesc.height) * downsampleMultiplier;
float aspectRatio = runningRes.y / runningRes.x;
float fovCorrection = runningRes.y * invHalfTanFOV * 0.5f;
ssaoParams2 = new Vector4(settings.GTAOMinimumRadiusInPixels * downsampleMultiplier, 0.0f, fovCorrection, 0.0f);
depthToViewParams = new Vector4(
2.0f / (invHalfTanFOV * aspectRatio * runningRes.x),
2.0f / (invHalfTanFOV * runningRes.y),
1.0f / (invHalfTanFOV * aspectRatio),
1.0f / invHalfTanFOV
);
}
private static void CalculateTemporalParams(ScreenSpaceAmbientOcclusionSettings settings, out float temporalRotation, out int temporalOffset)
{
temporalRotation = 0.0f;
temporalOffset = 0;
if (!settings.IsTemporalFilterActive)
return;
uint temporalRotationCount = (uint)s_TemporalRotations.Length;
temporalRotation = s_TemporalRotations[(uint)Time.frameCount % temporalRotationCount] / k_DegreesPerRotation;
temporalOffset = (int)(((uint)Time.frameCount / temporalRotationCount) % k_TemporalOffsetCount);
}
private static Vector2Int CalculateDispatchSize(Vector2Int textureSize, ComputeShader cs, int kernelIndex)
{
cs.GetKernelThreadGroupSizes(kernelIndex, out uint groupX, out uint groupY, out _);
return new Vector2Int(
Mathf.CeilToInt(textureSize.x / (float)groupX),
Mathf.CeilToInt(textureSize.y / (float)groupY)
);
}
private static GTAOComputeParams CreateGTAOComputeParams(ScreenSpaceAmbientOcclusionSettings settings, UniversalCameraData cameraData, in TextureDesc cameraColorDesc, Texture2D blueNoiseTexture)
{
GTAOComputeParams computeParams = new GTAOComputeParams();
float radius = SSAOUtils.CalculateRadius(settings);
computeParams.orthographicCamera = cameraData.camera.orthographic;
computeParams.ssaoParams = SSAOUtils.CalculateCommonParams(settings, radius);
CalculateGTAOViewParams(settings, cameraData, computeParams.orthographicCamera, radius, cameraColorDesc, out computeParams.ssaoParams2, out computeParams.depthToViewParams);
computeParams.sourceSize = PostProcessUtils.CalcShaderSourceSize(cameraColorDesc.width, cameraColorDesc.height, cameraColorDesc.useDynamicScale);
computeParams.projectionParams2 = SSAOUtils.CalculateProjectionParams2(cameraData);
if (settings.AOMethod == ScreenSpaceAmbientOcclusionSettings.AOMethodOptions.BlueNoise && blueNoiseTexture != null)
computeParams.blueNoiseParams = SSAOUtils.CalculateBlueNoiseParams(cameraData, blueNoiseTexture);
computeParams.historyLength = settings.GTAOHistoryLength;
computeParams.ghostingMitigation = settings.GTAOGhostingMitigation;
CalculateTemporalParams(settings, out computeParams.temporalRotation, out computeParams.temporalOffset);
return computeParams;
}
private static TemporalHistoryState GetTemporalHistoryState(RenderGraph renderGraph, ScreenSpaceAmbientOcclusionSettings settings, UniversalCameraData cameraData, bool useComputeShader)
{
var state = new TemporalHistoryState
{
historyReadTexture = TextureHandle.nullHandle,
historyWriteTexture = TextureHandle.nullHandle,
};
if (!settings.IsTemporalFilterActive || cameraData.historyManager == null)
return state;
cameraData.historyManager.RequestAccess<SSAOHistory>();
state.ssaoHistory = cameraData.historyManager.GetHistoryForWrite<SSAOHistory>();
if (state.ssaoHistory == null)
return state;
bool xrMultipassEnabled = false;
#if ENABLE_VR && ENABLE_XR_MODULE
xrMultipassEnabled = cameraData.xr.enabled && !cameraData.xr.singlePassEnabled;
#endif
bool wasReallocated = state.ssaoHistory.Update(cameraData, settings.Downsample, useComputeShader, xrMultipassEnabled);
int multipassId = 0;
#if ENABLE_VR && ENABLE_XR_MODULE
multipassId = cameraData.xr.multipassId;
#endif
int accumulationVersion = state.ssaoHistory.GetAccumulationVersion(multipassId);
bool isPreview = cameraData.camera.cameraType == CameraType.Preview;
bool isRenderRequest = cameraData.camera.isProcessingRenderRequest;
state.isNewFrame = !isPreview && !isRenderRequest && accumulationVersion != Time.frameCount;
RTHandle historyWrite = state.ssaoHistory.GetCurrentTexture(multipassId);
RTHandle historyRead = state.ssaoHistory.GetPreviousTexture(multipassId);
Debug.Assert(historyWrite != null && historyRead != null, "SSAO temporal history textures should be allocated here.");
state.historyWriteTexture = renderGraph.ImportTexture(historyWrite);
state.historyReadTexture = renderGraph.ImportTexture(historyRead);
state.historyReady = !wasReallocated && accumulationVersion >= 0;
return state;
}
private TextureHandle ImportBlueNoiseTexture(RenderGraph renderGraph, Texture2D blueNoiseTexture)
{
if (blueNoiseTexture == null)
return TextureHandle.nullHandle;
if (m_ComputePathState.blueNoiseRTHandle == null || m_ComputePathState.blueNoiseRTHandle.externalTexture != blueNoiseTexture)
{
m_ComputePathState.blueNoiseRTHandle?.Release();
m_ComputePathState.blueNoiseRTHandle = RTHandles.Alloc(blueNoiseTexture);
}
return renderGraph.ImportTexture(m_ComputePathState.blueNoiseRTHandle);
}
// ---- RecordRenderGraph ----
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
if (!resourceData.cameraDepthTexture.IsValid() || !resourceData.cameraNormalsTexture.IsValid())
{
if (Debug.isDebugBuild && !m_WarnedMissingTextures)
{
m_WarnedMissingTextures = true;
Debug.LogWarning("Screen Space Ambient Occlusion: skipped because the active renderer does not provide the required camera depth/normals textures.");
}
return;
}
if (Debug.isDebugBuild) // Saw valid textures: reset so a later renderer without them warns again.
m_WarnedMissingTextures = false;
// Builds a TextureDesc compatible with the active camera color target, safe for both intermediate render textures and imported back-buffer handles.
TextureDesc cameraColorDesc = SSAOUtils.GetCameraColorDescriptor(renderGraph, resourceData.activeColorTexture, cameraData.camera.allowDynamicResolution);
bool useComputeShader = (m_CurrentSettings.NeedsComputeShader && m_SupportsComputeShader) && m_ComputePathState.shader != null && m_ComputePathState.gtaoKernel >= 0;
SSAOUtils.CreateRenderTextureHandles(renderGraph, resourceData, cameraColorDesc,
m_CurrentSettings, m_SupportsR8RenderTextureFormat, m_BlurType, enableRandomWrite: useComputeShader,
out TextureHandle aoTexture, out TextureHandle blurTexture, out TextureHandle temporalTexture, out TextureHandle finalTexture);
var temporalHistory = GetTemporalHistoryState(renderGraph, m_CurrentSettings, cameraData, useComputeShader);
SSAOUtils.SetupCameraViewMatrices(cameraData, ref m_CameraViewData);
m_BlueNoiseTextureIndex = SSAOUtils.AdvanceBlueNoiseIndex(m_CurrentSettings, m_BlueNoiseTextures, m_BlueNoiseTextureIndex);
if (useComputeShader)
{
RecordGTAOComputePass(renderGraph, aoTexture, blurTexture, temporalTexture, finalTexture, temporalHistory, cameraData, resourceData, cameraColorDesc);
}
else
{
// GTAO raster-fragment fallback - reuse shared raster recording
TextureHandle cameraDepthTexture = resourceData.cameraDepthTexture;
TextureHandle cameraNormalsTexture = resourceData.cameraNormalsTexture;
SSAOUtils.SetupKeywordsAndParameters(m_Material, m_CurrentSettings, cameraData, cameraColorDesc, ref m_CameraViewData, m_BlueNoiseTextures, m_BlueNoiseTextureIndex, ref m_SSAOParamsPrev);
SSAOUtils.RecordRasterAOPass(renderGraph, m_ProfilingSampler, m_Material, m_CurrentSettings, aoTexture, cameraDepthTexture, cameraNormalsTexture, cameraColorDesc);
SSAOUtils.RecordBlurChain(renderGraph, m_ProfilingSampler, m_Material, m_CurrentSettings, cameraData, aoTexture, blurTexture, finalTexture);
if (!m_CurrentSettings.AfterOpaque)
SSAOUtils.RecordCleanupPass(renderGraph, m_ProfilingSampler, m_CurrentSettings.DirectLightingStrength, finalTexture);
}
}
// ---- Compute Pass Recording ----
private void RecordGTAOComputePass(RenderGraph renderGraph, TextureHandle aoTexture, TextureHandle blurTexture, TextureHandle temporalTexture, TextureHandle finalTexture,
in TemporalHistoryState temporalHistory, UniversalCameraData cameraData, UniversalResourceData resourceData, in TextureDesc cameraColorDesc)
{
TextureHandle cameraDepthTexture = resourceData.cameraDepthTexture;
TextureHandle cameraNormalsTexture = resourceData.cameraNormalsTexture;
TextureHandle motionVectorTexture = resourceData.motionVectorColor;
bool temporalEnabled = m_CurrentSettings.IsTemporalFilterActive;
bool isTemporalTextureValid = temporalTexture.IsValid();
bool isHistoryReadTextureValid = temporalHistory.historyReadTexture.IsValid();
bool isHistoryWriteTextureValid = temporalHistory.historyWriteTexture.IsValid();
bool hasTemporalResources = temporalEnabled && isTemporalTextureValid && isHistoryReadTextureValid && isHistoryWriteTextureValid;
bool shouldSeedHistory = hasTemporalResources && temporalHistory.isNewFrame && !temporalHistory.historyReady;
bool willRunTemporalFilter = hasTemporalResources && (temporalHistory.historyReady || shouldSeedHistory);
TextureDesc aoDesc = aoTexture.GetDescriptor(renderGraph);
Vector2Int aoSize = new Vector2Int(aoDesc.width, aoDesc.height);
Vector2Int aoDispatchSize = CalculateDispatchSize(aoSize, m_ComputePathState.shader, m_ComputePathState.gtaoKernel);
Texture2D blueNoiseTexture = SSAOUtils.GetBlueNoiseTexture(m_BlueNoiseTextures, m_BlueNoiseTextureIndex);
if (m_CurrentSettings.AOMethod == ScreenSpaceAmbientOcclusionSettings.AOMethodOptions.BlueNoise)
Debug.Assert(blueNoiseTexture != null, "Blue noise texture is null. Blue noise mode requires a valid blue noise texture.");
TextureHandle blueNoiseHandle = ImportBlueNoiseTexture(renderGraph, blueNoiseTexture ?? Texture2D.blackTexture);
var computeParams = CreateGTAOComputeParams(m_CurrentSettings, cameraData, cameraColorDesc, blueNoiseTexture);
// GTAO
RecordGTAOMainPass(renderGraph, cameraData, computeParams, blueNoiseHandle, cameraDepthTexture, cameraNormalsTexture, aoTexture, aoDispatchSize, willRunTemporalFilter);
// Spatial filter
TextureHandle spatialFilteredTexture = aoTexture;
Debug.Assert(blurTexture.IsValid(), "GTAO compute path requires an allocated blur texture.");
if (m_CurrentSettings.GTAOSpatialFilter == ScreenSpaceAmbientOcclusionSpatialFilter.Box)
{
RecordComputeDispatchPass(renderGraph, "GTAO Box Spatial Filter 1", m_ComputePathState.boxSpatialFilterKernel, aoDispatchSize, aoTexture, blurTexture, ShaderIDs._BlurOutput, cameraDepthTexture, 4.0f);
RecordComputeDispatchPass(renderGraph, "GTAO Box Spatial Filter 2", m_ComputePathState.boxSpatialFilterKernel, aoDispatchSize, blurTexture, aoTexture, ShaderIDs._BlurOutput, cameraDepthTexture, 2.0f);
RecordComputeDispatchPass(renderGraph, "GTAO Box Spatial Filter 3", m_ComputePathState.boxSpatialFilterKernel, aoDispatchSize, aoTexture, blurTexture, ShaderIDs._BlurOutput, cameraDepthTexture, 1.0f);
spatialFilteredTexture = blurTexture;
}
else
{
RecordComputeDispatchPass(renderGraph, "GTAO BlurH", m_ComputePathState.blurHKernel, aoDispatchSize, aoTexture, blurTexture, ShaderIDs._BlurOutput);
RecordComputeDispatchPass(renderGraph, "GTAO BlurV", m_ComputePathState.blurVKernel, aoDispatchSize, blurTexture, aoTexture, ShaderIDs._BlurOutput);
spatialFilteredTexture = aoTexture;
}
// Temporal filter and history update
if (willRunTemporalFilter)
{
if (shouldSeedHistory)
RecordCopyHistoryPass(renderGraph, "GTAO SeedHistory", aoDispatchSize, spatialFilteredTexture, cameraDepthTexture, temporalHistory.historyReadTexture);
// Scene View and editor redraws can render the same frame again after history has been advanced.
// Keep resolving against history, but stop reprojection motion for those redraws.
TextureHandle activeMotionVectorTexture = temporalHistory.isNewFrame ? motionVectorTexture : renderGraph.defaultResources.blackTexture;
RecordTemporalFilterPass(renderGraph, aoDispatchSize, spatialFilteredTexture, cameraDepthTexture, temporalHistory.historyReadTexture, temporalHistory.historyWriteTexture, activeMotionVectorTexture, temporalTexture);
}
// Determine the final source for downstream passes
TextureHandle resultTexture = willRunTemporalFilter ? temporalTexture : spatialFilteredTexture;
// Final / AfterOpaque blit
if (m_CurrentSettings.AfterOpaque)
{
bool useBlitAfterOpaquePass = m_CurrentSettings.GTAOSpatialFilter == ScreenSpaceAmbientOcclusionSpatialFilter.Box;
int afterOpaqueShaderPass = useBlitAfterOpaquePass
? (int)SSAOUtils.ShaderPasses.BoxAfterOpaque
: (int)SSAOUtils.ShaderPasses.BilateralAfterOpaque;
RecordAfterOpaqueBlitPass(renderGraph, computeParams, resultTexture, resourceData.activeColorTexture, afterOpaqueShaderPass);
}
else if (finalTexture.IsValid())
{
TextureDesc finalDesc = finalTexture.GetDescriptor(renderGraph);
Vector2Int finalSize = new Vector2Int(finalDesc.width, finalDesc.height);
Vector2Int finalDispatchSize = CalculateDispatchSize(finalSize, m_ComputePathState.shader, m_ComputePathState.finalBlitKernel);
RecordFinalBlitPass(renderGraph, resultTexture, finalTexture, m_CurrentSettings.DirectLightingStrength, finalDispatchSize);
}
if (willRunTemporalFilter && temporalHistory.isNewFrame && temporalHistory.ssaoHistory != null)
{
int multipassId = 0;
#if ENABLE_VR && ENABLE_XR_MODULE
multipassId = cameraData.xr.multipassId;
#endif
temporalHistory.ssaoHistory.SetAccumulationVersion(multipassId, Time.frameCount);
}
}
private void RecordGTAOMainPass(RenderGraph renderGraph, UniversalCameraData cameraData, GTAOComputeParams computeParams,
TextureHandle blueNoiseTexture, TextureHandle depthTexture, TextureHandle normalsTexture, TextureHandle aoTexture, Vector2Int dispatchSize, bool willRunTemporalFilter)
{
using (var builder = renderGraph.AddComputePass<GTAOComputePassData>("GTAO Compute", out var passData, m_ProfilingSampler))
{
passData.cs = m_ComputePathState.shader;
passData.kernel = m_ComputePathState.gtaoKernel;
passData.dispatchSize = dispatchSize;
passData.computeParams = computeParams;
passData.useBlueNoise = m_CurrentSettings.AOMethod == ScreenSpaceAmbientOcclusionSettings.AOMethodOptions.BlueNoise;
passData.localKeywords = m_ComputePathState.keywords;
passData.willRunTemporalFilter = willRunTemporalFilter;
passData.directionCount = m_CurrentSettings.GTAODirectionCount;
passData.stepCount = m_CurrentSettings.GTAOStepCount;
passData.spatialFilter = m_CurrentSettings.GTAOSpatialFilter;
#if ENABLE_VR && ENABLE_XR_MODULE
int eyeCount = cameraData.xr.enabled && cameraData.xr.singlePassEnabled ? 2 : 1;
#else
int eyeCount = 1;
#endif
Array.Copy(m_CameraViewData.viewProjections, passData.cameraViewProjections, eyeCount);
Array.Copy(m_CameraViewData.topLeftCorner, passData.cameraTopLeftCorner, eyeCount);
Array.Copy(m_CameraViewData.xExtent, passData.cameraXExtent, eyeCount);
Array.Copy(m_CameraViewData.yExtent, passData.cameraYExtent, eyeCount);
Array.Copy(m_CameraViewData.zExtent, passData.cameraZExtent, eyeCount);
passData.aoTexture = aoTexture;
builder.UseTexture(aoTexture, AccessFlags.Write);
passData.depthTexture = depthTexture;
builder.UseTexture(depthTexture, AccessFlags.Read);
passData.normalsTexture = normalsTexture;
builder.UseTexture(normalsTexture, AccessFlags.Read);
passData.blueNoiseTexture = blueNoiseTexture;
builder.UseTexture(blueNoiseTexture, AccessFlags.Read);
builder.AllowGlobalStateModification(true);
builder.SetRenderFunc(static (GTAOComputePassData data, ComputeGraphContext ctx) =>
{
var cmd = ctx.cmd;
var cs = data.cs;
ref var computeParams = ref data.computeParams;
cmd.SetKeyword(cs, data.localKeywords.blueNoise, data.useBlueNoise);
cmd.SetKeyword(cs, data.localKeywords.interleavedGradient, !data.useBlueNoise);
cmd.SetKeyword(cs, data.localKeywords.temporalFiltering, data.willRunTemporalFilter);
cmd.SetKeyword(cs, data.localKeywords.orthographic, computeParams.orthographicCamera);
cmd.SetComputeVectorParam(cs, SSAOUtils.ShaderConstants._SSAOParams, computeParams.ssaoParams);
cmd.SetComputeVectorParam(cs, SSAOUtils.ShaderConstants._SSAOParams2, computeParams.ssaoParams2);
cmd.SetComputeVectorParam(cs, SSAOUtils.ShaderConstants._AODepthToViewParams, computeParams.depthToViewParams);
cmd.SetComputeVectorParam(cs, SSAOUtils.ShaderConstants._SourceSize, computeParams.sourceSize);
cmd.SetComputeVectorParam(cs, SSAOUtils.ShaderConstants._ProjectionParams2, computeParams.projectionParams2);
cmd.SetComputeMatrixArrayParam(cs, SSAOUtils.ShaderConstants._CameraViewProjections, data.cameraViewProjections);
cmd.SetComputeVectorArrayParam(cs, SSAOUtils.ShaderConstants._CameraViewTopLeftCorner, data.cameraTopLeftCorner);
cmd.SetComputeVectorArrayParam(cs, SSAOUtils.ShaderConstants._CameraViewXExtent, data.cameraXExtent);
cmd.SetComputeVectorArrayParam(cs, SSAOUtils.ShaderConstants._CameraViewYExtent, data.cameraYExtent);
cmd.SetComputeVectorArrayParam(cs, SSAOUtils.ShaderConstants._CameraViewZExtent, data.cameraZExtent);
cmd.SetComputeIntParam(cs, ShaderIDs._GTAODirectionCount, data.directionCount);
cmd.SetComputeIntParam(cs, ShaderIDs._GTAOStepCount, data.stepCount);
cmd.SetComputeIntParam(cs, ShaderIDs._GTAOSpatialFilter, (int)data.spatialFilter);
cmd.SetComputeVectorParam(cs, SSAOUtils.ShaderConstants._SSAOBlueNoiseParams, computeParams.blueNoiseParams);
cmd.SetComputeTextureParam(cs, data.kernel, SSAOUtils.ShaderConstants._BlueNoiseTexture, data.blueNoiseTexture);
if (data.willRunTemporalFilter)
{
cmd.SetComputeFloatParam(cs, ShaderIDs._SSAOHistoryLength, computeParams.historyLength);
cmd.SetComputeFloatParam(cs, ShaderIDs._SSAOGhostingMitigation, computeParams.ghostingMitigation);
cmd.SetComputeFloatParam(cs, ShaderIDs._SSAOTemporalRotation, computeParams.temporalRotation);
cmd.SetComputeIntParam(cs, ShaderIDs._SSAOTemporalOffset, computeParams.temporalOffset);
}
cmd.SetComputeTextureParam(cs, data.kernel, ShaderIDs._CameraDepthTexture, data.depthTexture);
cmd.SetComputeTextureParam(cs, data.kernel, SSAOUtils.ShaderConstants._CameraNormalsTexture, data.normalsTexture);
cmd.SetComputeTextureParam(cs, data.kernel, ShaderIDs._AOOutput, data.aoTexture);
cmd.DispatchCompute(cs, data.kernel, data.dispatchSize.x, data.dispatchSize.y, 1);
});
}
}
private void RecordComputeDispatchPass(RenderGraph renderGraph, string passName, int kernel, Vector2Int dispatchSize, TextureHandle srcTexture, TextureHandle dstTexture, int dstPropertyId, TextureHandle depthTexture = default, float boxFilterStep = 0.0f)
{
using (var builder = renderGraph.AddComputePass<GTAOSingleDispatchPassData>(passName, out var passData, m_ProfilingSampler))
{
passData.cs = m_ComputePathState.shader;
passData.kernel = kernel;
passData.dispatchSize = dispatchSize;
passData.srcTexture = srcTexture;
passData.depthTexture = depthTexture;
passData.dstTexture = dstTexture;
passData.dstPropertyId = dstPropertyId;
passData.boxFilterStep = boxFilterStep;
builder.UseTexture(srcTexture, AccessFlags.Read);
if (depthTexture.IsValid())
builder.UseTexture(depthTexture, AccessFlags.Read);
builder.UseTexture(dstTexture, AccessFlags.Write);
builder.SetRenderFunc(static (GTAOSingleDispatchPassData data, ComputeGraphContext ctx) =>
{
ctx.cmd.SetComputeTextureParam(data.cs, data.kernel, SSAOUtils.ShaderConstants._BlitTexture, data.srcTexture);
if (data.depthTexture.IsValid())
ctx.cmd.SetComputeTextureParam(data.cs, data.kernel, ShaderIDs._CameraDepthTexture, data.depthTexture);
ctx.cmd.SetComputeFloatParam(data.cs, ShaderIDs._GTAOBoxFilterStep, data.boxFilterStep);
ctx.cmd.SetComputeTextureParam(data.cs, data.kernel, data.dstPropertyId, data.dstTexture);
ctx.cmd.DispatchCompute(data.cs, data.kernel, data.dispatchSize.x, data.dispatchSize.y, 1);
});
}
}
private void RecordCopyHistoryPass(RenderGraph renderGraph, string passName, Vector2Int dispatchSize, TextureHandle srcTexture, TextureHandle depthTexture, TextureHandle historyTexture)
{
using (var builder = renderGraph.AddComputePass<GTAOCopyHistoryPassData>(passName, out var passData, m_ProfilingSampler))
{
passData.cs = m_ComputePathState.shader;
passData.kernel = m_ComputePathState.copyHistoryKernel;
passData.dispatchSize = dispatchSize;
passData.srcTexture = srcTexture;
passData.depthTexture = depthTexture;
passData.historyTexture = historyTexture;
builder.UseTexture(srcTexture, AccessFlags.Read);
builder.UseTexture(depthTexture, AccessFlags.Read);
builder.UseTexture(historyTexture, AccessFlags.Write);
builder.SetRenderFunc(static (GTAOCopyHistoryPassData data, ComputeGraphContext ctx) =>
{
var cmd = ctx.cmd;
cmd.SetComputeTextureParam(data.cs, data.kernel, SSAOUtils.ShaderConstants._BlitTexture, data.srcTexture);
cmd.SetComputeTextureParam(data.cs, data.kernel, ShaderIDs._CameraDepthTexture, data.depthTexture);
cmd.SetComputeTextureParam(data.cs, data.kernel, ShaderIDs._AOOutputHistory, data.historyTexture);
cmd.DispatchCompute(data.cs, data.kernel, data.dispatchSize.x, data.dispatchSize.y, 1);
});
}
}
private void RecordTemporalFilterPass(RenderGraph renderGraph, Vector2Int dispatchSize, TextureHandle aoTexture, TextureHandle depthTexture, TextureHandle historyReadTexture, TextureHandle historyWriteTexture, TextureHandle motionVectorTexture, TextureHandle temporalTexture)
{
using (var builder = renderGraph.AddComputePass<GTAOTemporalPassData>("GTAO Temporal", out var passData, m_ProfilingSampler))
{
passData.cs = m_ComputePathState.shader;
passData.kernel = m_ComputePathState.temporalKernel;
passData.dispatchSize = dispatchSize;
passData.aoTexture = aoTexture;
passData.depthTexture = depthTexture;
passData.historyReadTexture = historyReadTexture;
passData.historyWriteTexture = historyWriteTexture;
passData.temporalTexture = temporalTexture;
passData.spatialFilter = m_CurrentSettings.GTAOSpatialFilter;
builder.UseTexture(aoTexture, AccessFlags.Read);
builder.UseTexture(depthTexture, AccessFlags.Read);
builder.UseTexture(historyReadTexture, AccessFlags.Read);
builder.UseTexture(historyWriteTexture, AccessFlags.Write);
builder.UseTexture(temporalTexture, AccessFlags.Write);
Debug.Assert(motionVectorTexture.IsValid(), "Motion vector texture is invalid. GTAO temporal filter requires a motion vector texture.");
passData.motionVectorTexture = motionVectorTexture;
builder.UseTexture(motionVectorTexture, AccessFlags.Read);
builder.SetRenderFunc(static (GTAOTemporalPassData data, ComputeGraphContext ctx) =>
{
var cmd = ctx.cmd;
cmd.SetComputeTextureParam(data.cs, data.kernel, SSAOUtils.ShaderConstants._BlitTexture, data.aoTexture);
cmd.SetComputeTextureParam(data.cs, data.kernel, ShaderIDs._CameraDepthTexture, data.depthTexture);
cmd.SetComputeTextureParam(data.cs, data.kernel, ShaderIDs._AOPackedHistory, data.historyReadTexture);
cmd.SetComputeTextureParam(data.cs, data.kernel, ShaderIDs._AOOutputHistory, data.historyWriteTexture);
cmd.SetComputeTextureParam(data.cs, data.kernel, ShaderIDs._MotionVectorTexture, data.motionVectorTexture);
cmd.SetComputeTextureParam(data.cs, data.kernel, ShaderIDs._OcclusionTexture, data.temporalTexture);
cmd.SetComputeIntParam(data.cs, ShaderIDs._GTAOSpatialFilter, (int)data.spatialFilter);
cmd.DispatchCompute(data.cs, data.kernel, data.dispatchSize.x, data.dispatchSize.y, 1);
});
}
}
private void RecordFinalBlitPass(RenderGraph renderGraph, TextureHandle srcTexture, TextureHandle finalTexture, float directLightingStrength, Vector2Int finalDispatchSize)
{
using (var builder = renderGraph.AddComputePass<GTAOFinalBlitPassData>("GTAO FinalBlit", out var passData, m_ProfilingSampler))
{
passData.cs = m_ComputePathState.shader;
passData.kernel = m_ComputePathState.finalBlitKernel;
passData.dispatchSize = finalDispatchSize;
passData.directLightingStrength = directLightingStrength;
passData.srcTexture = srcTexture;
passData.finalTexture = finalTexture;
builder.UseTexture(srcTexture, AccessFlags.Read);
builder.UseTexture(finalTexture, AccessFlags.Write);
builder.SetGlobalTextureAfterPass(finalTexture, SSAOUtils.ShaderConstants._SSAOFinalTexture);
builder.AllowGlobalStateModification(true);
builder.SetRenderFunc(static (GTAOFinalBlitPassData data, ComputeGraphContext ctx) =>
{
ctx.cmd.SetComputeTextureParam(data.cs, data.kernel, SSAOUtils.ShaderConstants._BlitTexture, data.srcTexture);
ctx.cmd.SetComputeTextureParam(data.cs, data.kernel, ShaderIDs._FinalOutput, data.finalTexture);
ctx.cmd.DispatchCompute(data.cs, data.kernel, data.dispatchSize.x, data.dispatchSize.y, 1);
ctx.cmd.SetKeyword(ShaderGlobalKeywords.ScreenSpaceOcclusion, true);
ctx.cmd.SetGlobalVector(SSAOUtils.ShaderConstants._AmbientOcclusionParam, new Vector4(1f, 0f, 0f, data.directLightingStrength));
});
}
}
private void RecordAfterOpaqueBlitPass(RenderGraph renderGraph, GTAOComputeParams computeParams, TextureHandle sourceTexture, TextureHandle targetTexture, int shaderPass)
{
using (var builder = renderGraph.AddRasterRenderPass<GTAOAfterOpaqueBlitPassData>("GTAO AfterOpaque Blit", out var passData, m_ProfilingSampler))
{
passData.material = m_Material;
passData.sourceSize = computeParams.sourceSize;
passData.ssaoParams = computeParams.ssaoParams;
passData.shaderPass = shaderPass;
passData.sourceTexture = sourceTexture;
passData.targetTexture = targetTexture;
builder.UseTexture(passData.sourceTexture, AccessFlags.Read);
builder.SetRenderAttachment(passData.targetTexture, 0, AccessFlags.Write);
builder.SetRenderFunc(static (GTAOAfterOpaqueBlitPassData data, RasterGraphContext ctx) =>
{
Vector4 viewScaleBias = SSAOUtils.ComputeScaleBias(data.sourceTexture, SSAOUtils.IsYFlip(ctx, in data.sourceTexture, in data.targetTexture));
data.material.SetVector(SSAOUtils.ShaderConstants._SourceSize, data.sourceSize);
data.material.SetVector(SSAOUtils.ShaderConstants._SSAOParams, data.ssaoParams);
Blitter.BlitTexture(ctx.cmd, data.sourceTexture, viewScaleBias, data.material, data.shaderPass);
});
}
}
public override void OnCameraCleanup(CommandBuffer cmd)
{
if (cmd == null)
throw new ArgumentNullException("cmd");
if (!m_CurrentSettings.AfterOpaque)
cmd.SetKeyword(ShaderGlobalKeywords.ScreenSpaceOcclusion, false);
}
public void Dispose()
{
m_ComputePathState.Release();
CoreUtils.Destroy(m_Material);
m_Material = null;
m_SSAOParamsPrev = default;
}
}
}
#endif
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 50b950943744ab94c835f03c29638265
@@ -0,0 +1,615 @@
using System;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
namespace UnityEngine.Rendering.Universal
{
internal static class SSAOUtils
{
// Constants
internal const string k_SSAOTextureName = "_ScreenSpaceOcclusionTexture";
// Shared Shader Property IDs
internal static class ShaderConstants
{
public static readonly int _AmbientOcclusionParam = Shader.PropertyToID("_AmbientOcclusionParam");
public static readonly int _SSAOParams = Shader.PropertyToID("_SSAOParams");
public static readonly int _SSAOBlueNoiseParams = Shader.PropertyToID("_SSAOBlueNoiseParams");
public static readonly int _BlueNoiseTexture = Shader.PropertyToID("_BlueNoiseTexture");
public static readonly int _SSAOFinalTexture = Shader.PropertyToID(k_SSAOTextureName);
public static readonly int _CameraViewXExtent = Shader.PropertyToID("_CameraViewXExtent");
public static readonly int _CameraViewYExtent = Shader.PropertyToID("_CameraViewYExtent");
public static readonly int _CameraViewZExtent = Shader.PropertyToID("_CameraViewZExtent");
public static readonly int _ProjectionParams2 = Shader.PropertyToID("_ProjectionParams2");
public static readonly int _CameraViewProjections = Shader.PropertyToID("_CameraViewProjections");
public static readonly int _CameraViewTopLeftCorner = Shader.PropertyToID("_CameraViewTopLeftCorner");
public static readonly int _CameraNormalsTexture = Shader.PropertyToID("_CameraNormalsTexture");
public static readonly int _BlitScaleBias = Shader.PropertyToID("_BlitScaleBias");
public static readonly int _BlitTexture = Shader.PropertyToID("_BlitTexture");
public static readonly int _SourceSize = Shader.PropertyToID("_SourceSize");
#if MODERN_SSAO
public static readonly int _SSAOParams2 = Shader.PropertyToID("_SSAOParams2");
public static readonly int _AODepthToViewParams = Shader.PropertyToID("_AODepthToViewParams");
#endif
}
// Enums
internal enum BlurTypes
{
Bilateral,
Gaussian,
Kawase,
}
internal enum ShaderPasses
{
AmbientOcclusion = 0,
BilateralBlurHorizontal = 1,
BilateralBlurVertical = 2,
BilateralBlurFinal = 3,
BilateralAfterOpaque = 4,
GaussianBlurHorizontal = 5,
GaussianBlurVertical = 6,
GaussianAfterOpaque = 7,
KawaseBlur = 8,
KawaseAfterOpaque = 9,
BoxAfterOpaque = 10,
}
// Camera view data aggregated into a struct to avoid scattered arrays.
internal struct CameraViewData
{
public Vector4[] topLeftCorner;
public Vector4[] xExtent;
public Vector4[] yExtent;
public Vector4[] zExtent;
public Matrix4x4[] viewProjections;
public static CameraViewData Create()
{
return new CameraViewData
{
topLeftCorner = new Vector4[2],
xExtent = new Vector4[2],
yExtent = new Vector4[2],
zExtent = new Vector4[2],
viewProjections = new Matrix4x4[2],
};
}
}
// Structs
internal readonly struct SSAOMaterialParams
{
internal readonly bool orthographicCamera;
internal readonly bool aoBlueNoise;
internal readonly bool aoInterleavedGradient;
internal readonly bool sampleCountHigh;
internal readonly bool sampleCountMedium;
internal readonly bool sampleCountLow;
internal readonly bool sourceDepthNormals;
internal readonly bool sourceDepthHigh;
internal readonly bool sourceDepthMedium;
internal readonly bool sourceDepthLow;
internal readonly Vector4 ssaoParams;
#if MODERN_SSAO
internal readonly Vector4 ssaoParams2;
internal readonly Vector4 depthToViewParams;
internal readonly bool isGTAOMode;
#endif
internal SSAOMaterialParams(ScreenSpaceAmbientOcclusionSettings settings, UniversalCameraData cameraData, in TextureDesc cameraColorDesc)
{
float radius = CalculateRadius(settings);
orthographicCamera = cameraData.camera.orthographic;
sampleCountHigh = settings.Samples == ScreenSpaceAmbientOcclusionSettings.AOSampleOption.High;
sampleCountMedium = settings.Samples == ScreenSpaceAmbientOcclusionSettings.AOSampleOption.Medium;
sampleCountLow = settings.Samples == ScreenSpaceAmbientOcclusionSettings.AOSampleOption.Low;
#if MODERN_SSAO
isGTAOMode = settings.Mode != ScreenSpaceAmbientOcclusionMode.Standard;
if (isGTAOMode)
{
// GTAO always uses depth normals
sourceDepthNormals = true;
aoBlueNoise = settings.AOMethod == ScreenSpaceAmbientOcclusionSettings.AOMethodOptions.BlueNoise;
aoInterleavedGradient = settings.AOMethod == ScreenSpaceAmbientOcclusionSettings.AOMethodOptions.InterleavedGradient;
sourceDepthHigh = false;
sourceDepthMedium = false;
sourceDepthLow = false;
GTAOPass.CalculateGTAOViewParams(settings, cameraData, orthographicCamera, radius, cameraColorDesc, out ssaoParams2, out depthToViewParams);
}
else
{
#endif
// Standard mode
bool isUsingDepthNormals = settings.Source == ScreenSpaceAmbientOcclusionSettings.DepthSource.DepthNormals;
aoBlueNoise = settings.AOMethod == ScreenSpaceAmbientOcclusionSettings.AOMethodOptions.BlueNoise;
aoInterleavedGradient = settings.AOMethod == ScreenSpaceAmbientOcclusionSettings.AOMethodOptions.InterleavedGradient;
sourceDepthNormals = settings.Source == ScreenSpaceAmbientOcclusionSettings.DepthSource.DepthNormals;
sourceDepthHigh = !isUsingDepthNormals && settings.NormalSamples == ScreenSpaceAmbientOcclusionSettings.NormalQuality.High;
sourceDepthMedium = !isUsingDepthNormals && settings.NormalSamples == ScreenSpaceAmbientOcclusionSettings.NormalQuality.Medium;
sourceDepthLow = !isUsingDepthNormals && settings.NormalSamples == ScreenSpaceAmbientOcclusionSettings.NormalQuality.Low;
#if MODERN_SSAO
ssaoParams2 = Vector4.zero;
depthToViewParams = Vector4.zero;
}
#endif
ssaoParams = CalculateCommonParams(settings, radius);
}
internal bool Equals(in SSAOMaterialParams other)
{
return orthographicCamera == other.orthographicCamera
&& aoBlueNoise == other.aoBlueNoise
&& aoInterleavedGradient == other.aoInterleavedGradient
&& sampleCountHigh == other.sampleCountHigh
&& sampleCountMedium == other.sampleCountMedium
&& sampleCountLow == other.sampleCountLow
&& sourceDepthNormals == other.sourceDepthNormals
&& sourceDepthHigh == other.sourceDepthHigh
&& sourceDepthMedium == other.sourceDepthMedium
&& sourceDepthLow == other.sourceDepthLow
&& ssaoParams == other.ssaoParams
#if MODERN_SSAO
&& ssaoParams2 == other.ssaoParams2
&& depthToViewParams == other.depthToViewParams
&& isGTAOMode == other.isGTAOMode
#endif
;
}
}
// ---- Static Utility Methods ----
internal static float CalculateRadius(ScreenSpaceAmbientOcclusionSettings settings)
{
float radiusMultiplier = settings.AOMethod == ScreenSpaceAmbientOcclusionSettings.AOMethodOptions.BlueNoise ? 1.5f : 1.0f;
return settings.Radius * radiusMultiplier;
}
internal static Vector4 CalculateCommonParams(ScreenSpaceAmbientOcclusionSettings settings, float radius)
{
return new Vector4(settings.Intensity, radius, 1.0f / (settings.Downsample ? 2 : 1), settings.Falloff);
}
internal static Vector4 CalculateProjectionParams2(UniversalCameraData cameraData)
{
return new Vector4(1.0f / cameraData.camera.nearClipPlane, 0.0f, 0.0f, 0.0f);
}
internal static Vector2 GetR2Offset()
{
// R2 sequence from https://extremelearning.com.au/unreasonable-effectiveness-of-quasirandom-sequences/
// x_n = (n * (1/phi_2)) mod 1, y_n = (n * (1/phi_2^2)) mod 1
//
// Use uint multiplication so wrap-around handles "mod 1" exactly,
// regardless of how large Time.frameCount grows. Constants are the
// irrational R2 ratios scaled by 2^32 and rounded:
// round(2^32 * 0.7548776662466927) = 3242174889
// round(2^32 * 0.5698402909980532) = 2447445413
const uint k_R2SequenceX = 3242174889u;
const uint k_R2SequenceY = 2447445413u;
const float k_InvScale = 1.0f / 4294967296.0f; // 1 / 2^32
uint sampleIndex = (uint)(Time.frameCount + 1);
uint offsetX = sampleIndex * k_R2SequenceX; // wraps mod 2^32 ⇔ mod 1
uint offsetY = sampleIndex * k_R2SequenceY;
return new Vector2(offsetX * k_InvScale, offsetY * k_InvScale);
}
internal static Vector4 CalculateBlueNoiseParams(UniversalCameraData cameraData, Texture2D noiseTexture)
{
#if UNITY_INCLUDE_TESTS
return new Vector4(
cameraData.pixelWidth / (float)noiseTexture.width,
cameraData.pixelHeight / (float)noiseTexture.height,
1.0f,
1.0f);
#else
Vector2 blueNoiseOffset = GetR2Offset();
return new Vector4(
cameraData.pixelWidth / (float)noiseTexture.width,
cameraData.pixelHeight / (float)noiseTexture.height,
blueNoiseOffset.x,
blueNoiseOffset.y);
#endif
}
internal static Texture2D GetBlueNoiseTexture(Texture2D[] blueNoiseTextures, int index)
{
if (blueNoiseTextures == null || blueNoiseTextures.Length == 0)
return null;
return blueNoiseTextures[index % blueNoiseTextures.Length];
}
internal static int AdvanceBlueNoiseIndex(ScreenSpaceAmbientOcclusionSettings settings, Texture2D[] blueNoiseTextures, int currentIndex)
{
if (settings.AOMethod != ScreenSpaceAmbientOcclusionSettings.AOMethodOptions.BlueNoise ||
blueNoiseTextures == null || blueNoiseTextures.Length == 0)
{
return currentIndex;
}
#if UNITY_INCLUDE_TESTS
return 0;
#else
return (currentIndex + 1) % blueNoiseTextures.Length;
#endif
}
internal static BlurTypes GetBlurType(ScreenSpaceAmbientOcclusionSettings.BlurQualityOptions quality)
{
return quality switch
{
ScreenSpaceAmbientOcclusionSettings.BlurQualityOptions.High => BlurTypes.Bilateral,
ScreenSpaceAmbientOcclusionSettings.BlurQualityOptions.Medium => BlurTypes.Gaussian,
ScreenSpaceAmbientOcclusionSettings.BlurQualityOptions.Low => BlurTypes.Kawase,
_ => throw new ArgumentOutOfRangeException(nameof(quality))
};
}
internal static void SetupCameraViewMatrices(UniversalCameraData cameraData, ref CameraViewData viewData)
{
#if ENABLE_VR && ENABLE_XR_MODULE
int eyeCount = cameraData.xr.enabled && cameraData.xr.singlePassEnabled ? 2 : 1;
#else
int eyeCount = 1;
#endif
for (int eyeIndex = 0; eyeIndex < eyeCount; eyeIndex++)
{
Matrix4x4 view = cameraData.GetViewMatrix(eyeIndex);
Matrix4x4 proj = cameraData.GetProjectionMatrix(eyeIndex);
viewData.viewProjections[eyeIndex] = proj * view;
// camera view space without translation, used by SSAO.hlsl ReconstructViewPos() to calculate view vector.
Matrix4x4 camView = view;
camView.SetColumn(3, new Vector4(0.0f, 0.0f, 0.0f, 1.0f));
Matrix4x4 camViewProj = proj * camView;
Matrix4x4 camViewProjInv = camViewProj.inverse;
Vector4 topLeftCorner = camViewProjInv.MultiplyPoint(new Vector4(-1, 1, -1, 1));
Vector4 topRightCorner = camViewProjInv.MultiplyPoint(new Vector4(1, 1, -1, 1));
Vector4 bottomLeftCorner = camViewProjInv.MultiplyPoint(new Vector4(-1, -1, -1, 1));
Vector4 farCentre = camViewProjInv.MultiplyPoint(new Vector4(0, 0, 1, 1));
viewData.topLeftCorner[eyeIndex] = topLeftCorner;
viewData.xExtent[eyeIndex] = topRightCorner - topLeftCorner;
viewData.yExtent[eyeIndex] = bottomLeftCorner - topLeftCorner;
viewData.zExtent[eyeIndex] = farCentre;
}
}
internal static bool IsYFlip(RasterGraphContext ctx, in TextureHandle srcTexture, in TextureHandle dstTexture)
{
return ctx.GetTextureUVOrigin(srcTexture) != ctx.GetTextureUVOrigin(dstTexture);
}
internal static Vector4 ComputeScaleBias(in TextureHandle source, bool yFlip)
{
RTHandle srcRTHandle = source;
Vector2 viewportScale;
if (srcRTHandle is { useScaling: true })
{
var scale = srcRTHandle.rtHandleProperties.rtHandleScale;
viewportScale.x = scale.x;
viewportScale.y = scale.y;
}
else
{
viewportScale = Vector2.one;
}
if (yFlip)
return new Vector4(viewportScale.x, -viewportScale.y, 0, viewportScale.y);
else
return new Vector4(viewportScale.x, viewportScale.y, 0, 0);
}
// ---- Shared Raster Recording Methods ----
internal static void SetupKeywordsAndParameters(
Material material,
ScreenSpaceAmbientOcclusionSettings settings,
UniversalCameraData cameraData,
in TextureDesc cameraColorDesc,
ref CameraViewData viewData,
Texture2D[] blueNoiseTextures,
int blueNoiseTextureIndex,
ref SSAOMaterialParams prevParams)
{
material.SetVector(ShaderConstants._ProjectionParams2, CalculateProjectionParams2(cameraData));
material.SetMatrixArray(ShaderConstants._CameraViewProjections, viewData.viewProjections);
material.SetVectorArray(ShaderConstants._CameraViewTopLeftCorner, viewData.topLeftCorner);
material.SetVectorArray(ShaderConstants._CameraViewXExtent, viewData.xExtent);
material.SetVectorArray(ShaderConstants._CameraViewYExtent, viewData.yExtent);
material.SetVectorArray(ShaderConstants._CameraViewZExtent, viewData.zExtent);
if (settings.AOMethod == ScreenSpaceAmbientOcclusionSettings.AOMethodOptions.BlueNoise)
{
Texture2D noiseTexture = GetBlueNoiseTexture(blueNoiseTextures, blueNoiseTextureIndex);
Debug.Assert(noiseTexture != null, "Blue noise texture is null. Blue noise mode requires a valid blue noise texture.");
Vector4 blueNoiseParams = CalculateBlueNoiseParams(cameraData, noiseTexture);
material.SetTexture(ShaderConstants._BlueNoiseTexture, noiseTexture);
material.SetVector(ShaderConstants._SSAOBlueNoiseParams, blueNoiseParams);
}
else
{
material.SetVector(ShaderConstants._SSAOBlueNoiseParams, Vector4.zero);
}
// Setting keywords can be somewhat expensive on low-end platforms.
// Previous params are cached to avoid setting the same keywords every frame.
SSAOMaterialParams matParams = new SSAOMaterialParams(settings, cameraData, cameraColorDesc);
bool ssaoParamsDirty = !prevParams.Equals(in matParams);
bool isParamsPropertySet = material.HasProperty(ShaderConstants._SSAOParams);
if (!ssaoParamsDirty && isParamsPropertySet)
return;
prevParams = matParams;
CoreUtils.SetKeyword(material, ScreenSpaceAmbientOcclusionKeywords.k_SampleCountHighKeyword, matParams.sampleCountHigh);
CoreUtils.SetKeyword(material, ScreenSpaceAmbientOcclusionKeywords.k_SampleCountMediumKeyword, matParams.sampleCountMedium);
CoreUtils.SetKeyword(material, ScreenSpaceAmbientOcclusionKeywords.k_SampleCountLowKeyword, matParams.sampleCountLow);
CoreUtils.SetKeyword(material, ScreenSpaceAmbientOcclusionKeywords.k_SourceDepthNormalsKeyword, matParams.sourceDepthNormals);
CoreUtils.SetKeyword(material, ScreenSpaceAmbientOcclusionKeywords.k_SourceDepthHighKeyword, matParams.sourceDepthHigh);
CoreUtils.SetKeyword(material, ScreenSpaceAmbientOcclusionKeywords.k_SourceDepthMediumKeyword, matParams.sourceDepthMedium);
CoreUtils.SetKeyword(material, ScreenSpaceAmbientOcclusionKeywords.k_SourceDepthLowKeyword, matParams.sourceDepthLow);
CoreUtils.SetKeyword(material, ScreenSpaceAmbientOcclusionKeywords.k_OrthographicCameraKeyword, matParams.orthographicCamera);
CoreUtils.SetKeyword(material, ScreenSpaceAmbientOcclusionKeywords.k_AOBlueNoiseKeyword, matParams.aoBlueNoise);
CoreUtils.SetKeyword(material, ScreenSpaceAmbientOcclusionKeywords.k_AOInterleavedGradientKeyword, matParams.aoInterleavedGradient);
material.SetVector(ShaderConstants._SSAOParams, matParams.ssaoParams);
#if MODERN_SSAO
CoreUtils.SetKeyword(material, ScreenSpaceAmbientOcclusionKeywords.k_GTAOModeKeyword, matParams.isGTAOMode);
if (matParams.isGTAOMode)
{
material.SetVector(ShaderConstants._SSAOParams2, matParams.ssaoParams2);
material.SetVector(ShaderConstants._AODepthToViewParams, matParams.depthToViewParams);
}
#endif
}
// Pass data classes for shared raster recording
internal class RasterAOPassData
{
internal bool afterOpaque;
internal ScreenSpaceAmbientOcclusionSettings.BlurQualityOptions blurQuality;
internal Material material;
internal float directLightingStrength;
internal Vector4 sourceSize;
internal TextureHandle aoTexture;
internal TextureHandle finalTexture;
internal TextureHandle blurTexture;
internal TextureHandle cameraNormalsTexture;
}
internal class BlurPassData
{
internal TextureHandle srcTexture;
internal TextureHandle dstTexture;
internal Material material;
internal UniversalCameraData cameraData;
internal int pass;
internal ScreenSpaceAmbientOcclusionSettings.BlurQualityOptions blurQuality;
internal bool afterOpaque;
}
internal class CleanupPassData
{
internal float directLightingStrength;
}
internal static void RecordRasterAOPass(
RenderGraph renderGraph,
ProfilingSampler profilingSampler,
Material material,
ScreenSpaceAmbientOcclusionSettings settings,
TextureHandle aoTexture,
TextureHandle cameraDepthTexture,
TextureHandle cameraNormalsTexture,
in TextureDesc cameraColorDesc)
{
using (var builder = renderGraph.AddRasterRenderPass<RasterAOPassData>("Blit SSAO", out var passData, profilingSampler))
{
builder.AllowGlobalStateModification(true);
passData.material = material;
passData.blurQuality = settings.BlurQuality;
passData.afterOpaque = settings.AfterOpaque;
passData.directLightingStrength = settings.DirectLightingStrength;
passData.sourceSize = PostProcessUtils.CalcShaderSourceSize(cameraColorDesc.width, cameraColorDesc.height, cameraColorDesc.useDynamicScale);
passData.aoTexture = aoTexture;
builder.SetRenderAttachment(passData.aoTexture, 0, AccessFlags.WriteAll);
builder.UseTexture(cameraDepthTexture, AccessFlags.Read);
if (settings.Source == ScreenSpaceAmbientOcclusionSettings.DepthSource.DepthNormals)
{
builder.UseTexture(cameraNormalsTexture, AccessFlags.Read);
passData.cameraNormalsTexture = cameraNormalsTexture;
}
builder.SetRenderFunc(static (RasterAOPassData data, RasterGraphContext ctx) =>
{
ctx.cmd.SetGlobalVector(ShaderConstants._SourceSize, data.sourceSize);
if (data.cameraNormalsTexture.IsValid())
data.material.SetTexture(ShaderConstants._CameraNormalsTexture, data.cameraNormalsTexture);
Vector4 viewScaleBias = new(1, 1, 0, 0);
Blitter.BlitTexture(ctx.cmd, viewScaleBias, data.material, (int)ShaderPasses.AmbientOcclusion);
});
}
}
internal static void RecordBlurStep(
RenderGraph renderGraph,
ProfilingSampler profilingSampler,
Material material,
ScreenSpaceAmbientOcclusionSettings settings,
UniversalCameraData cameraData,
string blurPassName,
in TextureHandle src,
in TextureHandle dst,
int pass,
bool isLastPass)
{
using (var builder = renderGraph.AddRasterRenderPass<BlurPassData>(blurPassName, out var passData, profilingSampler))
{
passData.material = material;
passData.blurQuality = settings.BlurQuality;
passData.afterOpaque = settings.AfterOpaque;
passData.srcTexture = src;
passData.dstTexture = dst;
passData.cameraData = cameraData;
passData.pass = pass;
builder.UseTexture(passData.srcTexture);
AccessFlags finalDstAccess = passData.afterOpaque && isLastPass ? AccessFlags.Write : AccessFlags.WriteAll;
builder.SetRenderAttachment(passData.dstTexture, 0, finalDstAccess);
builder.SetRenderFunc(static (BlurPassData data, RasterGraphContext ctx) =>
{
Vector4 viewScaleBias = ComputeScaleBias(data.srcTexture, IsYFlip(ctx, in data.srcTexture, in data.dstTexture));
Blitter.BlitTexture(ctx.cmd, data.srcTexture, viewScaleBias, data.material, data.pass);
});
}
}
internal static void RecordBlurChain(
RenderGraph renderGraph,
ProfilingSampler profilingSampler,
Material material,
ScreenSpaceAmbientOcclusionSettings settings,
UniversalCameraData cameraData,
TextureHandle aoTexture,
TextureHandle blurTexture,
TextureHandle finalTexture)
{
switch (settings.BlurQuality)
{
case ScreenSpaceAmbientOcclusionSettings.BlurQualityOptions.High:
RecordBlurStep(renderGraph, profilingSampler, material, settings, cameraData, "Blur SSAO Horizontal (High)", aoTexture, blurTexture, (int)ShaderPasses.BilateralBlurHorizontal, false);
RecordBlurStep(renderGraph, profilingSampler, material, settings, cameraData, "Blur SSAO Vertical (High)", blurTexture, aoTexture, (int)ShaderPasses.BilateralBlurVertical, false);
RecordBlurStep(renderGraph, profilingSampler, material, settings, cameraData, "Blur SSAO Final (High)", aoTexture, finalTexture, (int)(settings.AfterOpaque ? ShaderPasses.BilateralAfterOpaque : ShaderPasses.BilateralBlurFinal), true);
break;
case ScreenSpaceAmbientOcclusionSettings.BlurQualityOptions.Medium:
RecordBlurStep(renderGraph, profilingSampler, material, settings, cameraData, "Blur SSAO Horizontal (Medium)", aoTexture, blurTexture, (int)ShaderPasses.GaussianBlurHorizontal, false);
RecordBlurStep(renderGraph, profilingSampler, material, settings, cameraData, "Blur SSAO Final (Medium)", blurTexture, finalTexture, (int)(settings.AfterOpaque ? ShaderPasses.GaussianAfterOpaque : ShaderPasses.GaussianBlurVertical), true);
break;
case ScreenSpaceAmbientOcclusionSettings.BlurQualityOptions.Low:
RecordBlurStep(renderGraph, profilingSampler, material, settings, cameraData, "Blur SSAO (Low)", aoTexture, finalTexture, (int)(settings.AfterOpaque ? ShaderPasses.KawaseAfterOpaque : ShaderPasses.KawaseBlur), true);
break;
default:
throw new ArgumentOutOfRangeException();
}
}
internal static void RecordCleanupPass(
RenderGraph renderGraph,
ProfilingSampler profilingSampler,
float directLightingStrength,
TextureHandle finalTexture)
{
// Add cleanup pass to:
// - Set global keywords for next passes
// - Set global texture as there is a limitation in Render Graph where an input texture cannot be set as a global texture after the pass runs
// A Raster pass is used so it can be merged easily with the blur passes.
using (var builder = renderGraph.AddRasterRenderPass<CleanupPassData>("Cleanup SSAO", out var passData, profilingSampler))
{
passData.directLightingStrength = directLightingStrength;
builder.AllowGlobalStateModification(true);
builder.UseTexture(finalTexture, AccessFlags.Read);
builder.SetGlobalTextureAfterPass(finalTexture, ShaderConstants._SSAOFinalTexture);
builder.SetRenderFunc(static (CleanupPassData data, RasterGraphContext ctx) =>
{
// We only want URP shaders to sample SSAO if After Opaque is disabled...
ctx.cmd.SetKeyword(ShaderGlobalKeywords.ScreenSpaceOcclusion, true);
ctx.cmd.SetGlobalVector(ShaderConstants._AmbientOcclusionParam, new Vector4(1f, 0f, 0f, data.directLightingStrength));
});
}
}
// ---- Texture Creation ----
internal static TextureDesc GetCameraColorDescriptor(RenderGraph renderGraph, in TextureHandle source, bool useDynamicScale)
{
var info = renderGraph.GetRenderTargetInfo(source);
return new TextureDesc(info.width, info.height)
{
format = info.format,
slices = info.volumeDepth,
dimension = info.volumeDepth > 1 ? TextureDimension.Tex2DArray : TextureDimension.Tex2D,
bindTextureMS = info.bindMS,
useDynamicScale = useDynamicScale,
};
}
internal static void CreateRenderTextureHandles(
RenderGraph renderGraph,
UniversalResourceData resourceData,
in TextureDesc cameraColorDesc,
ScreenSpaceAmbientOcclusionSettings settings,
bool supportsR8,
BlurTypes blurType,
bool enableRandomWrite,
out TextureHandle aoTexture,
out TextureHandle blurTexture,
out TextureHandle temporalTexture,
out TextureHandle finalTexture)
{
// Descriptor for the final blur pass
TextureDesc finalTextureDescriptor = PostProcessUtils.GetCompatibleDescriptor(
cameraColorDesc,
cameraColorDesc.width,
cameraColorDesc.height,
supportsR8 ? GraphicsFormat.R8_UNorm : GraphicsFormat.R8G8B8A8_UNorm);
finalTextureDescriptor.enableRandomWrite = enableRandomWrite;
// Descriptor for the AO and Blur passes
int downsampleDivider = settings.Downsample ? 2 : 1;
bool useRedComponentOnly = supportsR8 && blurType > BlurTypes.Bilateral;
TextureDesc aoBlurDescriptor = PostProcessUtils.GetCompatibleDescriptor(
cameraColorDesc,
cameraColorDesc.width / downsampleDivider,
cameraColorDesc.height / downsampleDivider,
useRedComponentOnly ? GraphicsFormat.R8_UNorm : GraphicsFormat.R8G8B8A8_UNorm);
aoBlurDescriptor.enableRandomWrite = enableRandomWrite;
// Handles
aoTexture = UniversalRenderer.CreateRenderGraphTexture(renderGraph, aoBlurDescriptor, "_SSAO_OcclusionTexture0", false, Color.clear, FilterMode.Bilinear);
finalTexture = settings.AfterOpaque ? resourceData.activeColorTexture : UniversalRenderer.CreateRenderGraphTexture(renderGraph, finalTextureDescriptor, k_SSAOTextureName, false, Color.clear, FilterMode.Bilinear);
if (settings.BlurQuality != ScreenSpaceAmbientOcclusionSettings.BlurQualityOptions.Low)
blurTexture = UniversalRenderer.CreateRenderGraphTexture(renderGraph, aoBlurDescriptor, "_SSAO_OcclusionTexture1", false, Color.clear, FilterMode.Bilinear);
else
blurTexture = TextureHandle.nullHandle;
#if MODERN_SSAO
if (settings.IsTemporalFilterActive)
temporalTexture = UniversalRenderer.CreateRenderGraphTexture(renderGraph, aoBlurDescriptor, "_SSAO_TemporalTexture", false, Color.clear, FilterMode.Bilinear);
else
temporalTexture = TextureHandle.nullHandle;
#else
temporalTexture = TextureHandle.nullHandle;
#endif
if (!settings.AfterOpaque)
resourceData.ssaoTexture = finalTexture;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: af110458c684fbf45b1e546360ddf6b4
@@ -0,0 +1,436 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using Unity.Collections;
using Unity.Scripting.LifecycleManagement;
using UnityEngine.Scripting.APIUpdating;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
namespace UnityEngine.Rendering.Universal
{
/// <summary>
/// Input requirements for <c>ScriptableRenderPass</c>.
///
/// URP adds render passes to generate the inputs, or reuses inputs that are already available from earlier in the frame.
///
/// URP binds the inputs as global shader texture properties.
/// </summary>
/// <seealso cref="ConfigureInput"/>
[Flags]
public enum ScriptableRenderPassInput
{
/// <summary>
/// Used when a <c>ScriptableRenderPass</c> does not require any texture.
/// </summary>
None = 0,
/// <summary>
/// Used when a <c>ScriptableRenderPass</c> requires a depth texture.
///
/// To sample the depth texture in a shader, include `Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl`, then use the `SampleSceneDepth` method.
/// </summary>
Depth = 1 << 0,
/// <summary>
/// Used when a <c>ScriptableRenderPass</c> requires a normal texture.
///
/// To sample the normals texture in a shader, include `Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareNormalsTexture.hlsl`, then use the `SampleSceneNormals` method.
/// </summary>
Normal = 1 << 1,
/// <summary>
/// Used when a <c>ScriptableRenderPass</c> requires a color texture.
///
/// To sample the color texture in a shader, include `Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareOpaqueTexture.hlsl`, then use the `SampleSceneColor` method.
///
/// **Note:** The opaque texture might be a downscaled copy of the framebuffer from before rendering transparent objects.
/// </summary>
Color = 1 << 2,
/// <summary>
/// Used when a <c>ScriptableRenderPass</c> requires a motion vectors texture.
///
/// To sample the motion vectors texture in a shader, use `TEXTURE2D_X(_MotionVectorTexture)`, then `LOAD_TEXTURE2D_X_LOD(_MotionVectorTexture, pixelCoords, 0).xy`.
/// </summary>
Motion = 1 << 3,
}
// Note: Spaced built-in events so we can add events in between them
// We need to leave room as we sort render passes based on event.
// Users can also inject render pass events in a specific point by doing RenderPassEvent + offset
/// <summary>
/// Controls when the render pass executes.
/// </summary>
public enum RenderPassEvent
{
/// <summary>
/// Executes a <c>ScriptableRenderPass</c> before rendering any other passes in the pipeline.
/// Camera matrices and stereo rendering are not setup this point.
/// You can use this to draw to custom input textures used later in the pipeline, f.ex LUT textures.
/// </summary>
BeforeRendering = 0,
/// <summary>
/// Executes a <c>ScriptableRenderPass</c> before rendering shadowmaps.
/// Camera matrices and stereo rendering are not setup this point.
/// </summary>
BeforeRenderingShadows = 50,
/// <summary>
/// Executes a <c>ScriptableRenderPass</c> after rendering shadowmaps.
/// Camera matrices and stereo rendering are not setup this point.
/// </summary>
AfterRenderingShadows = 100,
/// <summary>
/// Executes a <c>ScriptableRenderPass</c> before rendering prepasses, f.ex, depth prepass.
/// Camera matrices and stereo rendering are already setup at this point.
/// </summary>
BeforeRenderingPrePasses = 150,
/// <summary>
/// Executes a <c>ScriptableRenderPass</c> after rendering prepasses, f.ex, depth prepass.
/// Camera matrices and stereo rendering are already setup at this point.
/// </summary>
AfterRenderingPrePasses = 200,
/// <summary>
/// Executes a <c>ScriptableRenderPass</c> before rendering gbuffer pass.
/// </summary>
BeforeRenderingGbuffer = 210,
/// <summary>
/// Executes a <c>ScriptableRenderPass</c> after rendering gbuffer pass.
/// </summary>
AfterRenderingGbuffer = 220,
/// <summary>
/// Executes a <c>ScriptableRenderPass</c> before rendering deferred shading pass.
/// </summary>
BeforeRenderingDeferredLights = 230,
/// <summary>
/// Executes a <c>ScriptableRenderPass</c> after rendering deferred shading pass.
/// </summary>
AfterRenderingDeferredLights = 240,
/// <summary>
/// Executes a <c>ScriptableRenderPass</c> before rendering opaque objects.
/// </summary>
BeforeRenderingOpaques = 250,
/// <summary>
/// Executes a <c>ScriptableRenderPass</c> after rendering opaque objects.
/// </summary>
AfterRenderingOpaques = 300,
/// <summary>
/// Executes a <c>ScriptableRenderPass</c> before rendering the sky.
/// </summary>
BeforeRenderingSkybox = 350,
/// <summary>
/// Executes a <c>ScriptableRenderPass</c> after rendering the sky.
/// </summary>
AfterRenderingSkybox = 400,
/// <summary>
/// Executes a <c>ScriptableRenderPass</c> before rendering transparent objects.
/// </summary>
BeforeRenderingTransparents = 450,
/// <summary>
/// Executes a <c>ScriptableRenderPass</c> after rendering transparent objects.
/// </summary>
AfterRenderingTransparents = 500,
/// <summary>
/// Executes a <c>ScriptableRenderPass</c> before rendering post-processing effects.
/// </summary>
BeforeRenderingPostProcessing = 550,
/// <summary>
/// Executes a <c>ScriptableRenderPass</c> after rendering post-processing effects but before final blit, post-processing AA effects and color grading.
/// </summary>
AfterRenderingPostProcessing = 600,
/// <summary>
/// Executes a <c>ScriptableRenderPass</c> after rendering all effects.
/// </summary>
AfterRendering = 1000,
}
/// <summary>
/// Framebuffer fetch events in Universal RP
/// </summary>
internal enum FramebufferFetchEvent
{
None = 0,
FetchGbufferInDeferred = 1
}
internal static class RenderPassEventsEnumValues
{
// we cache the values in this array at construction time to avoid runtime allocations, which we would cause if we accessed valuesInternal directly
[NoAutoStaticsCleanup] // Clearing the array is not necessary when entering play mode because it's reflection data.
public static readonly int[] values;
static RenderPassEventsEnumValues()
{
System.Array valuesInternal = Enum.GetValues(typeof(RenderPassEvent));
values = new int[valuesInternal.Length];
int index = 0;
foreach (int value in valuesInternal)
{
values[index] = value;
index++;
}
}
}
/// <summary>
/// <c>ScriptableRenderPass</c> implements a logical rendering pass that can be used to extend Universal RP renderer.
/// </summary>
/// <remarks>
/// To implement your own rendering pass you need to take the following steps:
/// 1. Create a new Subclass from ScriptableRenderPass that implements the rendering logic.
/// 2. Create an instance of your subclass and set up the relevant parameters such as <c>ScriptableRenderPass.renderPassEvent</c> in the constructor or initialization code.
/// 3. Ensure your pass instance gets picked up by URP, this can be done through a <c>ScriptableRendererFeature</c> or by calling <c>ScriptableRenderer.EnqueuePass</c> from an event callback like <c>RenderPipelineManager.beginCameraRendering</c>
///
/// See [link] for more info on working with a <c>ScriptableRendererFeature</c> or [link] for more info on working with <c>ScriptableRenderer.EnqueuePass</c>.
/// </remarks>
public abstract partial class ScriptableRenderPass : IRenderGraphRecorder
{
/// <summary>
/// The event when the render pass executes.
/// </summary>
public RenderPassEvent renderPassEvent { get; set; }
/// <summary>
/// The input requirements for the <c>ScriptableRenderPass</c>, which has been set using <c>ConfigureInput</c>
/// </summary>
/// <seealso cref="ConfigureInput"/>
public ScriptableRenderPassInput input => m_Input;
/// <summary>
/// Setting this property to true forces rendering of all passes in the URP frame via an intermediate texture. Use this option for passes that do not support rendering directly to the backbuffer or that require sampling the active color target. Using this option might have a significant performance impact on untethered VR platforms.
/// </summary>
public bool requiresIntermediateTexture { get; set; }
private ProfilingSampler m_ProfingSampler;
private string m_PassName;
/// <summary>
/// A ProfilingSampler for the entire render pass. Used as a profiling name by <c>ScriptableRenderer</c> when executing the pass.
/// The default is named as the class type of the sub-class.
/// Set <c>base.profilingSampler</c> from the sub-class constructor to set a different profiling name for a custom <c>ScriptableRenderPass
/// This returns null in the Release managed code variant.</c>.
/// </summary>
protected internal ProfilingSampler profilingSampler
{
get
{
#if UNITY_INCLUDE_INSTRUMENTATION
return m_ProfingSampler;
#else
return null;
#endif
}
set
{
m_ProfingSampler = value;
m_PassName = (value != null) ? value.name : this.GetType().Name;
}
}
/// <summary>
/// The name of the pass that will show up in profiler and other tools. This will be identical to the
/// name of <c>profilingSampler</c>. <c>profilingSampler</c> is set to null in the Release managed code variant
/// so this <c>passName</c> property is the safe way to access the name and use it consistently. This will always return a valid string.
/// </summary>
protected internal string passName{ get { return m_PassName; } }
internal bool isBlitRenderPass { get; set; }
// index to track the position in the current frame
internal int renderPassQueueIndex { get; set; }
internal NativeArray<int> m_ColorAttachmentIndices;
internal NativeArray<int> m_InputAttachmentIndices;
internal GraphicsFormat[] renderTargetFormat { get; set; }
ScriptableRenderPassInput m_Input = ScriptableRenderPassInput.None;
static internal DebugHandler GetActiveDebugHandler(UniversalCameraData cameraData)
{
var debugHandler = cameraData.renderer.DebugHandler;
if ((debugHandler != null) && debugHandler.IsActiveForCamera(cameraData.isPreviewCamera))
return debugHandler;
return null;
}
/// <summary>
/// Creates a new <c>ScriptableRenderPass"</c> instance.
/// </summary>
public ScriptableRenderPass()
{
renderPassEvent = RenderPassEvent.AfterRenderingOpaques;
profilingSampler = new ProfilingSampler(this.GetType().Name);
}
/// <summary>
/// Configures Input Requirements for this render pass.
/// This method should be called inside <c>ScriptableRendererFeature.AddRenderPasses</c>.
/// </summary>
/// <param name="passInput">ScriptableRenderPassInput containing information about what requirements the pass needs.</param>
/// <seealso cref="ScriptableRendererFeature.AddRenderPasses"/>
public void ConfigureInput(ScriptableRenderPassInput passInput)
{
m_Input = passInput;
}
/// <summary>
/// Called upon finish rendering a camera. You can use this callback to release any resources created
/// by this render
/// pass that need to be cleanup once camera has finished rendering.
/// This method should be called for all cameras in a camera stack.
/// </summary>
/// <param name="cmd">Use this CommandBuffer to cleanup any generated data</param>
public virtual void OnCameraCleanup(CommandBuffer cmd)
{
}
/// <inheritdoc cref="IRenderGraphRecorder.RecordRenderGraph"/>
public virtual void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
Debug.LogWarning("The render pass " + this.ToString() + " does not have an implementation of the RecordRenderGraph method. Please implement this method, or consider turning on Compatibility Mode (RenderGraph disabled) in the menu Edit > Project Settings > Graphics > URP. Otherwise the render pass will have no effect. For more information, refer to https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@latest/index.html?subfolder=/manual/customizing-urp.html.");
}
/// <summary>
/// Creates <c>DrawingSettings</c> based on current the rendering state.
/// </summary>
/// <param name="shaderTagId">Shader pass tag to render.</param>
/// <param name="renderingData">Current rendering state.</param>
/// <param name="sortingCriteria">Criteria to sort objects being rendered.</param>
/// <returns>Returns the draw settings created.</returns>
/// <seealso cref="DrawingSettings"/>
public DrawingSettings CreateDrawingSettings(ShaderTagId shaderTagId, ref RenderingData renderingData, SortingCriteria sortingCriteria)
{
ContextContainer frameData = renderingData.frameData;
UniversalRenderingData universalRenderingData = frameData.Get<UniversalRenderingData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
UniversalLightData lightData = frameData.Get<UniversalLightData>();
return RenderingUtils.CreateDrawingSettings(shaderTagId, universalRenderingData, cameraData, lightData, sortingCriteria);
}
/// <summary>
/// Creates <c>DrawingSettings</c> based on current the rendering state.
/// </summary>
/// <param name="shaderTagId">Shader pass tag to render.</param>
/// <param name="renderingData">Current rendering state.</param>
/// <param name="cameraData">Current camera state.</param>
/// <param name="lightData">Current light state.</param>
/// <param name="sortingCriteria">Criteria to sort objects being rendered.</param>
/// <returns>Returns the draw settings created.</returns>
/// <seealso cref="DrawingSettings"/>
public DrawingSettings CreateDrawingSettings(ShaderTagId shaderTagId, UniversalRenderingData renderingData,
UniversalCameraData cameraData, UniversalLightData lightData, SortingCriteria sortingCriteria)
{
return RenderingUtils.CreateDrawingSettings(shaderTagId, renderingData, cameraData, lightData, sortingCriteria);
}
/// <summary>
/// Creates <c>DrawingSettings</c> based on current rendering state.
/// </summary>
/// <param name="shaderTagIdList">List of shader pass tag to render.</param>
/// <param name="renderingData">Current rendering state.</param>
/// <param name="sortingCriteria">Criteria to sort objects being rendered.</param>
/// <returns>Returns the draw settings created.</returns>
/// <seealso cref="DrawingSettings"/>
public DrawingSettings CreateDrawingSettings(List<ShaderTagId> shaderTagIdList,
ref RenderingData renderingData, SortingCriteria sortingCriteria)
{
ContextContainer frameData = renderingData.frameData;
UniversalRenderingData universalRenderingData = frameData.Get<UniversalRenderingData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
UniversalLightData lightData = frameData.Get<UniversalLightData>();
return RenderingUtils.CreateDrawingSettings(shaderTagIdList, universalRenderingData, cameraData, lightData, sortingCriteria);
}
/// <summary>
/// Creates <c>DrawingSettings</c> based on current rendering state.
/// </summary>
/// <param name="shaderTagIdList">List of shader pass tag to render.</param>
/// <param name="renderingData">Current rendering state.</param>
/// <param name="cameraData">Current camera state.</param>
/// <param name="lightData">Current light state.</param>
/// <param name="sortingCriteria">Criteria to sort objects being rendered.</param>
/// <returns>Returns the draw settings created.</returns>
/// <seealso cref="DrawingSettings"/>
public DrawingSettings CreateDrawingSettings(List<ShaderTagId> shaderTagIdList,
UniversalRenderingData renderingData, UniversalCameraData cameraData,
UniversalLightData lightData, SortingCriteria sortingCriteria)
{
return RenderingUtils.CreateDrawingSettings(shaderTagIdList, renderingData, cameraData, lightData, sortingCriteria);
}
/// <summary>
/// Compares two instances of <c>ScriptableRenderPass</c> by their <c>RenderPassEvent</c> and returns if <paramref name="lhs"/> is executed before <paramref name="rhs"/>.
/// </summary>
/// <param name="lhs"></param>
/// <param name="rhs"></param>
/// <returns></returns>
public static bool operator <(ScriptableRenderPass lhs, ScriptableRenderPass rhs)
{
return lhs.renderPassEvent < rhs.renderPassEvent;
}
/// <summary>
/// Compares two instances of <c>ScriptableRenderPass</c> by their <c>RenderPassEvent</c> and returns if <paramref name="lhs"/> is executed after <paramref name="rhs"/>.
/// </summary>
/// <param name="lhs"></param>
/// <param name="rhs"></param>
/// <returns></returns>
public static bool operator >(ScriptableRenderPass lhs, ScriptableRenderPass rhs)
{
return lhs.renderPassEvent > rhs.renderPassEvent;
}
internal static int GetRenderPassEventRange(RenderPassEvent renderPassEvent)
{
int numEvents = RenderPassEventsEnumValues.values.Length;
int currentIndex = 0;
// find the index of the renderPassEvent in the values array
for(int i = 0; i < numEvents; ++i)
{
if (RenderPassEventsEnumValues.values[currentIndex] == (int)renderPassEvent)
break;
currentIndex++;
}
if (currentIndex >= numEvents)
{
Debug.LogError("GetRenderPassEventRange: invalid renderPassEvent value cannot be found in the RenderPassEvent enumeration");
return 0;
}
if (currentIndex + 1 >= numEvents)
return 50; // if this was the last event in the enum, then add 50 as the range
int nextValue = RenderPassEventsEnumValues.values[currentIndex + 1];
return nextValue - (int) renderPassEvent;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: edba24b6007b9dd41824b4656ed8ebcf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,87 @@
using System;
using Unity.Collections;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
namespace UnityEngine.Rendering.Universal
{
/// <summary>
/// Write stencil bits for stencil-based cross-fade LOD.
/// </summary>
sealed class StencilCrossFadeRenderPass
{
private Material[] m_StencilDitherMaskSeedMaterials;
private readonly int _StencilDitherPattern = Shader.PropertyToID("_StencilDitherPattern");
private readonly int _StencilRefDitherMask = Shader.PropertyToID("_StencilRefDitherMask");
private readonly int _StencilWriteDitherMask = Shader.PropertyToID("_StencilWriteDitherMask");
private readonly ProfilingSampler m_ProfilingSampler;
internal StencilCrossFadeRenderPass(Shader shader)
{
m_StencilDitherMaskSeedMaterials = new Material[3];
m_ProfilingSampler = new ProfilingSampler("StencilDitherMaskSeed");
int[] stencilRefs = {
(int)UniversalRendererStencilRef.CrossFadeStencilRef_0,
(int)UniversalRendererStencilRef.CrossFadeStencilRef_1,
(int)UniversalRendererStencilRef.CrossFadeStencilRef_All };
int writeMask = (int)UniversalRendererStencilRef.CrossFadeStencilRef_All;
Debug.Assert(writeMask < 0x100); // 8 bits for stencil
for (int i = 0; i < m_StencilDitherMaskSeedMaterials.Length; ++i)
{
m_StencilDitherMaskSeedMaterials[i] = CoreUtils.CreateEngineMaterial(shader);
m_StencilDitherMaskSeedMaterials[i].SetInteger(_StencilDitherPattern, i + 1);
m_StencilDitherMaskSeedMaterials[i].SetFloat(_StencilWriteDitherMask, (float)writeMask);
m_StencilDitherMaskSeedMaterials[i].SetFloat(_StencilRefDitherMask, (float)stencilRefs[i]);
}
}
public void Dispose()
{
foreach (var m in m_StencilDitherMaskSeedMaterials)
CoreUtils.Destroy(m);
m_StencilDitherMaskSeedMaterials = null;
}
/// <summary>
/// Shared pass data for render graph
/// </summary>
private class PassData
{
public TextureHandle depthTarget;
public Material[] stencilDitherMaskSeedMaterials;
}
/// <summary>
/// Set render graph pass
/// </summary>
public void Render(RenderGraph renderGraph, ScriptableRenderContext context, in TextureHandle depthTarget)
{
using (var builder = renderGraph.AddRasterRenderPass<PassData>("Prepare Cross Fade Stencil", out var passData, m_ProfilingSampler))
{
builder.SetRenderAttachmentDepth(depthTarget, AccessFlags.Write);
passData.stencilDitherMaskSeedMaterials = m_StencilDitherMaskSeedMaterials;
passData.depthTarget = depthTarget;
builder.SetRenderFunc(static (PassData data, RasterGraphContext context) =>
{
ExecutePass(context.cmd, data.depthTarget, data.stencilDitherMaskSeedMaterials);
});
}
}
private static void ExecutePass(RasterCommandBuffer cmd, RTHandle depthTarget, Material[] stencilDitherMaskSeedMaterials)
{
Vector2Int scaledViewportSize = depthTarget.GetScaledSize(depthTarget.rtHandleProperties.currentViewportSize);
Rect viewport = new Rect(0.0f, 0.0f, scaledViewportSize.x, scaledViewportSize.y);
cmd.SetViewport(viewport);
// render one stencil value in each pass because SV_StencilRef is not fully supported.
for (int i = 0; i < stencilDitherMaskSeedMaterials.Length; ++i)
{
cmd.DrawProcedural(Matrix4x4.identity, stencilDitherMaskSeedMaterials[i], 0, MeshTopology.Triangles, 3, 1);
}
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: a246c6096818ee346906368fd723c112
@@ -0,0 +1,40 @@
using System;
using UnityEngine.Rendering.Universal.Internal;
namespace UnityEngine.Rendering.Universal
{
/// <summary>
/// Applies relevant settings before rendering transparent objects
/// </summary>
internal class TransparentSettingsPass : ScriptableRenderPass
{
bool m_shouldReceiveShadows;
public TransparentSettingsPass(RenderPassEvent evt, bool shadowReceiveSupported)
{
profilingSampler = new ProfilingSampler("Set Transparent Parameters");
renderPassEvent = evt;
m_shouldReceiveShadows = shadowReceiveSupported;
}
public bool Setup()
{
// Currently we only need to enqueue this pass when the user
// doesn't want transparent objects to receive shadows
return !m_shouldReceiveShadows;
}
public static void ExecutePass(RasterCommandBuffer rasterCommandBuffer)
{
// -----------------------------------------------------------
// This pass is only used when transparent objects should not
// receive shadows using the setting on the URP Renderer.
// This is controlled in the public bool Setup() function above.
// -----------------------------------------------------------
MainLightShadowCasterPass.SetShadowParamsForEmptyShadowmap(rasterCommandBuffer);
AdditionalLightsShadowCasterPass.SetShadowParamsForEmptyShadowmap(rasterCommandBuffer);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: faf05873da6e6fb489aa73a76d780c5a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,311 @@
#if ENABLE_VR && ENABLE_XR_MODULE
using System;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
namespace UnityEngine.Rendering.Universal
{
/// <summary>
/// Render all objects that have a 'XRMotionVectors' pass into the given depth buffer and motionvec buffer.
/// </summary>
public class XRDepthMotionPass : ScriptableRenderPass
{
public const string k_MotionOnlyShaderTagIdName = "XRMotionVectors";
private static readonly ShaderTagId k_MotionOnlyShaderTagId = new ShaderTagId(k_MotionOnlyShaderTagIdName);
private static readonly int k_SpaceWarpNDCModifier = Shader.PropertyToID("_SpaceWarpNDCModifier");
private RTHandle m_XRMotionVectorColor;
private TextureHandle xrMotionVectorColor;
private RTHandle m_XRMotionVectorDepth;
private TextureHandle xrMotionVectorDepth;
private bool m_XRSpaceWarpRightHandedNDC;
private LayerMask m_transparentlayerMask;
/// <summary>
/// Creates a new <c>XRDepthMotionPass</c> instance.
/// </summary>
/// <param name="evt">The <c>RenderPassEvent</c> to use.</param>
/// <param name="xrMotionVector">The Shader used for rendering XR camera motion vector.</param>
/// <seealso cref="RenderPassEvent"/>
public XRDepthMotionPass(RenderPassEvent evt, Shader xrMotionVector, LayerMask transparentLayerMask)
{
base.profilingSampler = new ProfilingSampler(nameof(XRDepthMotionPass));
renderPassEvent = evt;
ResetMotionData();
m_XRMotionVectorMaterial = CoreUtils.CreateEngineMaterial(xrMotionVector);
xrMotionVectorColor = TextureHandle.nullHandle;
m_XRMotionVectorColor = null;
xrMotionVectorDepth = TextureHandle.nullHandle;
m_XRMotionVectorDepth = null;
m_transparentlayerMask = transparentLayerMask;
}
private const int k_XRViewCountPerPass = 2;
private class PassData
{
internal RendererListHandle objMotionRendererList;
internal RendererListHandle objTransparentMotionRendererList;
internal Matrix4x4[] previousViewProjectionStereo = new Matrix4x4[k_XRViewCountPerPass];
internal Matrix4x4[] viewProjectionStereo = new Matrix4x4[k_XRViewCountPerPass];
internal Material xrMotionVector;
}
/// View projection data
private Matrix4x4[] m_StagingMatrixArray = new Matrix4x4[k_XRViewCountPerPass]; // Staging matrix to avoid allocating memory every frame when setting shader properties.
private Matrix4x4[] m_PreviousStagingMatrixArray = new Matrix4x4[k_XRViewCountPerPass]; // Staging matrix to avoid allocating memory every frame when setting shader properties.
private const int k_XRViewCount = 4;
private Matrix4x4[] m_ViewProjection = new Matrix4x4[k_XRViewCount];
private Matrix4x4[] m_PreviousViewProjection = new Matrix4x4[k_XRViewCount];
private int m_LastFrameIndex;
// Motion Vector
private Material m_XRMotionVectorMaterial;
private static DrawingSettings GetObjectMotionDrawingSettings(Camera camera, bool isTransparent = false)
{
var sortingSettings = new SortingSettings(camera) { criteria = isTransparent ? SortingCriteria.CommonTransparent : SortingCriteria.CommonOpaque };
// Notes: Usually, PerObjectData.MotionVectors will filter the renderer nodes to only draw moving objects.
// In our case, we use forceAllMotionVectorObjects in the filteringSettings to draw idle objects as well to populate depth.
var drawingSettings = new DrawingSettings(k_MotionOnlyShaderTagId, sortingSettings)
{
perObjectData = PerObjectData.MotionVectors,
enableInstancing = true,
};
drawingSettings.SetShaderPassName(0, k_MotionOnlyShaderTagId);
return drawingSettings;
}
private void InitObjectMotionRendererLists(ref PassData passData, ref CullingResults cullResults, RenderGraph renderGraph, Camera camera)
{
var objectMotionDrawingSettings = GetObjectMotionDrawingSettings(camera);
var filteringSettings = new FilteringSettings(RenderQueueRange.opaque, camera.cullingMask);
// Also render game objects that are not moved since last frame to save depth prepass requirement for camera motion.
filteringSettings.forceAllMotionVectorObjects = true;
var renderStateBlock = new RenderStateBlock(RenderStateMask.Nothing);
RenderingUtils.CreateRendererListWithRenderStateBlock(renderGraph, ref cullResults, objectMotionDrawingSettings, filteringSettings, renderStateBlock, ref passData.objMotionRendererList);
}
private void InitTransparentObjectMotionRendererLists(ref PassData passData, ref CullingResults cullResults, RenderGraph renderGraph, Camera camera)
{
var objectMotionDrawingSettings = GetObjectMotionDrawingSettings(camera, true);
var filteringSettings = new FilteringSettings(RenderQueueRange.transparent, m_transparentlayerMask);
// Also render game objects that are not moved since last frame to save depth prepass requirement for camera motion.
filteringSettings.forceAllMotionVectorObjects = true;
var renderStateBlock = new RenderStateBlock(RenderStateMask.Nothing);
RenderingUtils.CreateRendererListWithRenderStateBlock(renderGraph, ref cullResults, objectMotionDrawingSettings, filteringSettings, renderStateBlock, ref passData.objTransparentMotionRendererList);
}
/// <summary>
/// Initialize the RenderGraph pass data.
/// </summary>
/// <param name="passData"></param>
private void InitPassData(ref PassData passData, UniversalCameraData cameraData)
{
// XRTODO: Use XRSystem prevViewMatrix that is compatible with late latching. Currently blocked due to late latching engine side issue.
//var gpuP0 = GL.GetGPUProjectionMatrix(cameraData.xr.GetProjMatrix(0), false);
//var gpuP1 = GL.GetGPUProjectionMatrix(cameraData.xr.GetProjMatrix(1), false);
//passData.viewProjectionStereo[0] = gpuP0 * cameraData.xr.GetViewMatrix(0);
//passData.viewProjectionStereo[1] = gpuP1 * cameraData.xr.GetViewMatrix(1);
//passData.previousViewProjectionStereo[0] = gpuP0 * cameraData.xr.GetPrevViewMatrix(0);
//passData.previousViewProjectionStereo[1] = gpuP0 * cameraData.xr.GetPrevViewMatrix(1);
// Setup matrices and shader
var xr = cameraData.xr;
var viewStartIndex = xr.viewCount * xr.multipassId;
Array.Copy(m_PreviousViewProjection, viewStartIndex, m_PreviousStagingMatrixArray, 0, xr.viewCount);
passData.previousViewProjectionStereo = m_PreviousStagingMatrixArray;
Array.Copy(m_ViewProjection, viewStartIndex, m_StagingMatrixArray, 0, xr.viewCount);
passData.viewProjectionStereo = m_StagingMatrixArray;
// Setup camera motion material
passData.xrMotionVector = m_XRMotionVectorMaterial;
}
/// <summary>
/// Import the XR motion color and depth targets into the RenderGraph.
/// </summary>
/// <param name="cameraData"> UniversalCameraData that holds XR pass data. </param>
private void ImportXRMotionColorAndDepth(RenderGraph renderGraph, UniversalCameraData cameraData)
{
var rtMotionId = cameraData.xr.motionVectorRenderTarget;
if (m_XRMotionVectorColor == null)
{
m_XRMotionVectorColor = RTHandles.Alloc(rtMotionId);
}
else if (m_XRMotionVectorColor.nameID != rtMotionId)
{
RTHandleStaticHelpers.SetRTHandleUserManagedWrapper(ref m_XRMotionVectorColor, rtMotionId);
}
// ID is the same since a RenderTexture encapsulates all the attachments, including both color+depth.
var depthId = cameraData.xr.motionVectorRenderTarget;
if (m_XRMotionVectorDepth == null)
{
m_XRMotionVectorDepth = RTHandles.Alloc(depthId);
}
else if (m_XRMotionVectorDepth.nameID != depthId)
{
RTHandleStaticHelpers.SetRTHandleUserManagedWrapper(ref m_XRMotionVectorDepth, depthId);
}
// Import motion color and depth into the render graph.
RenderTargetInfo importInfo = new RenderTargetInfo();
importInfo.width = cameraData.xr.motionVectorRenderTargetDesc.width;
importInfo.height = cameraData.xr.motionVectorRenderTargetDesc.height;
importInfo.volumeDepth = cameraData.xr.motionVectorRenderTargetDesc.volumeDepth;
importInfo.msaaSamples = cameraData.xr.motionVectorRenderTargetDesc.msaaSamples;
importInfo.format = cameraData.xr.motionVectorRenderTargetDesc.graphicsFormat;
RenderTargetInfo importInfoDepth = new RenderTargetInfo();
importInfoDepth = importInfo;
importInfoDepth.format = cameraData.xr.motionVectorRenderTargetDesc.depthStencilFormat;
ImportResourceParams importMotionColorParams = new ImportResourceParams();
importMotionColorParams.clearOnFirstUse = true;
importMotionColorParams.clearColor = Color.black;
importMotionColorParams.discardOnLastUse = false;
ImportResourceParams importMotionDepthParams = new ImportResourceParams();
importMotionDepthParams.clearOnFirstUse = true;
importMotionDepthParams.clearColor = Color.black;
importMotionDepthParams.discardOnLastUse = false;
xrMotionVectorColor = renderGraph.ImportTexture(m_XRMotionVectorColor, importInfo, importMotionColorParams);
xrMotionVectorDepth = renderGraph.ImportTexture(m_XRMotionVectorDepth, importInfoDepth, importMotionDepthParams);
m_XRSpaceWarpRightHandedNDC = cameraData.xr.spaceWarpRightHandedNDC;
}
#region Recording
internal void Render(RenderGraph renderGraph, ContextContainer frameData)
{
UniversalRenderingData renderingData = frameData.Get<UniversalRenderingData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
// XR should be enabled and single pass should be enabled.
if (!cameraData.xr.enabled || !cameraData.xr.singlePassEnabled)
{
Debug.LogWarning("XRDepthMotionPass::Render is skipped because either XR is not enabled or singlepass rendering is not enabled.");
return;
}
// XR motion vector pass should be enabled.
if (!cameraData.xr.hasMotionVectorPass)
{
Debug.LogWarning("XRDepthMotionPass::Render is skipped because XR motion vector is not enabled for the current XRPass.");
return;
}
// First, import XR motion color and depth targets into the RenderGraph
ImportXRMotionColorAndDepth(renderGraph, cameraData);
// These flags are still required in SRP or the engine won't compute previous model matrices...
// If the flag hasn't been set yet on this camera, motion vectors will skip a frame.
cameraData.camera.depthTextureMode |= DepthTextureMode.MotionVectors | DepthTextureMode.Depth;
// Start recording the pass
using (var builder = renderGraph.AddRasterRenderPass<PassData>("XR Motion Pass", out var passData, base.profilingSampler))
{
builder.EnableFoveatedRasterization(cameraData.xr.supportsFoveatedRendering);
// Multiview render regions are incompatible with the inner (foveal) pass in Quad View
if (!cameraData.xr.isQuadViewInnerPass)
{
builder.SetExtendedFeatureFlags(ExtendedFeatureFlags.MultiviewRenderRegionsCompatible);
}
// Setup Color and Depth attachments
builder.SetRenderAttachment(xrMotionVectorColor, 0, AccessFlags.Write);
builder.SetRenderAttachmentDepth(xrMotionVectorDepth, AccessFlags.ReadWrite);
// Setup RendererList
InitObjectMotionRendererLists(ref passData, ref renderingData.cullResults, renderGraph, cameraData.camera);
builder.UseRendererList(passData.objMotionRendererList);
InitTransparentObjectMotionRendererLists(ref passData, ref renderingData.cullResults, renderGraph, cameraData.camera);
builder.UseRendererList(passData.objTransparentMotionRendererList);
// Allow setting up global matrix array
builder.AllowGlobalStateModification(true);
// Setup rest of the passData
InitPassData(ref passData, cameraData);
builder.SetRenderFunc((PassData data, RasterGraphContext context) =>
{
// Setup camera stereo buffer
context.cmd.SetGlobalMatrixArray(ShaderPropertyId.previousViewProjectionNoJitterStereo, data.previousViewProjectionStereo);
context.cmd.SetGlobalMatrixArray(ShaderPropertyId.viewProjectionNoJitterStereo, data.viewProjectionStereo);
// SpaceWarp is only available on Vulkan, so these values are always true. This is to support 2 versions of spacewarp
// One expects OpenGL NDC space motion vectors, the other expects Vulkan NDC space
context.cmd.SetGlobalFloat(k_SpaceWarpNDCModifier, m_XRSpaceWarpRightHandedNDC ? -1.0f : 1.0f);
// Object Motion for both static and dynamic objects, fill stencil for mv filled pixels.
context.cmd.DrawRendererList(passData.objMotionRendererList);
// Fill mv texturew with camera motion for pixels that don't have mv stencil bit.
context.cmd.DrawProcedural(Matrix4x4.identity, data.xrMotionVector, 0, MeshTopology.Triangles, 3, 1);
// Transparent Object Motion for both static and dynamic objects, fill stencil for mv filled pixels.
context.cmd.SetKeyword(ShaderGlobalKeywords.APPLICATION_SPACE_WARP_MOTION_TRANSPARENT, true);
context.cmd.DrawRendererList(passData.objTransparentMotionRendererList);
context.cmd.SetKeyword(ShaderGlobalKeywords.APPLICATION_SPACE_WARP_MOTION_TRANSPARENT, false);
});
}
}
#endregion
private void ResetMotionData()
{
for (int i = 0; i < k_XRViewCount; i++)
{
m_ViewProjection[i] = Matrix4x4.identity;
m_PreviousViewProjection[i] = Matrix4x4.identity;
}
m_LastFrameIndex = -1;
}
/// <summary>
/// Update XRDepthMotionPass to use camera's view and projection matrix for motion vector calculation.
/// </summary>
/// <param name="cameraData"> The cameraData used for rendering to XR moition textures. </param>
public void Update(ref UniversalCameraData cameraData)
{
if (!cameraData.xr.enabled || !cameraData.xr.singlePassEnabled)
{
Debug.LogWarning("XRDepthMotionPass::Update is skipped because either XR is not enabled or singlepass rendering is not enabled.");
return;
}
if (m_LastFrameIndex != Time.frameCount)
{
{
var gpuVP0 = GL.GetGPUProjectionMatrix(cameraData.GetProjectionMatrixNoJitter(0), renderIntoTexture: false) * cameraData.GetViewMatrix(0);
var gpuVP1 = GL.GetGPUProjectionMatrix(cameraData.GetProjectionMatrixNoJitter(1), renderIntoTexture: false) * cameraData.GetViewMatrix(1);
var xr = cameraData.xr;
var viewStartIndex = xr.viewCount * xr.multipassId;
m_PreviousViewProjection[viewStartIndex] = m_ViewProjection[viewStartIndex];
m_PreviousViewProjection[viewStartIndex + 1] = m_ViewProjection[viewStartIndex + 1];
m_ViewProjection[viewStartIndex] = gpuVP0;
m_ViewProjection[viewStartIndex + 1] = gpuVP1;
}
if (cameraData.xr.isLastCameraPass)
m_LastFrameIndex = Time.frameCount;
}
}
/// <summary>
/// Cleans up resources used by the pass.
/// </summary>
public void Dispose()
{
m_XRMotionVectorColor?.Release();
m_XRMotionVectorDepth?.Release();
CoreUtils.Destroy(m_XRMotionVectorMaterial);
}
}
}
#endif
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 56d37d33776d0f44586207d08936dd58
@@ -0,0 +1,76 @@
#if ENABLE_VR && ENABLE_XR_MODULE
using System;
using UnityEngine.Rendering.RenderGraphModule;
using UnityEngine.Experimental.Rendering;
namespace UnityEngine.Rendering.Universal
{
/// <summary>
/// Draw the XR occlusion mesh into the current depth buffer when XR is enabled.
/// </summary>
public partial class XROcclusionMeshPass : ScriptableRenderPass
{
public XROcclusionMeshPass(RenderPassEvent evt)
{
profilingSampler = new ProfilingSampler("Draw XR Occlusion Mesh");
renderPassEvent = evt;
}
private static void ExecutePass(RasterCommandBuffer cmd, PassData data)
{
if (data.xr.hasValidOcclusionMesh)
{
if (data.isActiveTargetBackBuffer)
cmd.SetViewport(data.xr.GetViewport());
data.xr.RenderOcclusionMesh(cmd, renderIntoTexture: data.shouldYFlip);
}
}
private class PassData
{
internal XRPass xr;
internal bool isActiveTargetBackBuffer;
internal bool shouldYFlip;
internal TextureHandle cameraColorAttachment;
}
internal void Render(RenderGraph renderGraph, ContextContainer frameData, in TextureHandle cameraColorAttachment, in TextureHandle cameraDepthAttachment)
{
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
using (var builder = renderGraph.AddRasterRenderPass<PassData>(passName, out var passData, profilingSampler))
{
passData.xr = cameraData.xr;
passData.cameraColorAttachment = cameraColorAttachment;
builder.SetRenderAttachment(cameraColorAttachment, 0);
builder.SetRenderAttachmentDepth(cameraDepthAttachment, AccessFlags.ReadWrite);
passData.isActiveTargetBackBuffer = resourceData.isActiveTargetBackBuffer;
builder.AllowGlobalStateModification(true);
if (cameraData.xr.enabled)
{
bool passSupportsFoveation = cameraData.xrUniversal.canFoveateIntermediatePasses || resourceData.isActiveTargetBackBuffer;
builder.EnableFoveatedRasterization(cameraData.xr.supportsFoveatedRendering && passSupportsFoveation);
// Multiview render regions are incompatible with the inner (foveal) pass in Quad View
if (!cameraData.xr.isQuadViewInnerPass)
{
builder.SetExtendedFeatureFlags(ExtendedFeatureFlags.MultiviewRenderRegionsCompatible);
}
}
builder.SetRenderFunc((PassData data, RasterGraphContext context) =>
{
passData.shouldYFlip = RenderingUtils.IsHandleYFlipped(context, in data.cameraColorAttachment);
ExecutePass(context.cmd, data);
});
return;
}
}
}
}
#endif
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fc46a25201ce7e743bfdca7d07707357
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: