testss
This commit is contained in:
@@ -0,0 +1,954 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Collections;
|
||||
|
||||
namespace UnityEngine.Rendering.Universal.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// Renders a shadow map atlas for additional shadow-casting Lights.
|
||||
/// </summary>
|
||||
public partial class AdditionalLightsShadowCasterPass : ScriptableRenderPass
|
||||
{
|
||||
private static class AdditionalShadowsConstantBuffer
|
||||
{
|
||||
public static int _AdditionalLightsWorldToShadow;
|
||||
public static int _AdditionalShadowParams;
|
||||
public static int _AdditionalShadowOffset0;
|
||||
public static int _AdditionalShadowOffset1;
|
||||
public static int _AdditionalShadowOffset2;
|
||||
public static int _AdditionalShadowOffset3;
|
||||
public static int _AdditionalShadowmapSize;
|
||||
}
|
||||
|
||||
internal struct ShadowResolutionRequest
|
||||
{
|
||||
public int visibleLightIndex;
|
||||
public int perLightShadowSliceIndex;
|
||||
public int requestedResolution;
|
||||
public bool softShadow; // otherwise it's hard-shadow (no filtering)
|
||||
public bool pointLightShadow; // otherwise it's spot light shadow (1 shadow slice instead of 6)
|
||||
|
||||
public int offsetX; // x coordinate of the square area allocated in the atlas for this shadow map
|
||||
public int offsetY; // y coordinate of the square area allocated in the atlas for this shadow map
|
||||
public int allocatedResolution; // width of the square area allocated in the atlas for this shadow map
|
||||
|
||||
public ShadowResolutionRequest(int _visibleLightIndex, int _perLightShadowSliceIndex, int _requestedResolution, bool _softShadow , bool _pointLightShadow)
|
||||
{
|
||||
visibleLightIndex = _visibleLightIndex;
|
||||
perLightShadowSliceIndex = _perLightShadowSliceIndex;
|
||||
requestedResolution = _requestedResolution;
|
||||
softShadow = _softShadow;
|
||||
pointLightShadow = _pointLightShadow;
|
||||
|
||||
offsetX = 0;
|
||||
offsetY = 0;
|
||||
allocatedResolution = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int m_AdditionalLightsWorldToShadow_SSBO;
|
||||
static int m_AdditionalShadowParams_SSBO;
|
||||
bool m_UseStructuredBuffer;
|
||||
|
||||
const int k_ShadowmapBufferBits = 16;
|
||||
private RenderTargetHandle m_AdditionalLightsShadowmap;
|
||||
RenderTexture m_AdditionalLightsShadowmapTexture;
|
||||
|
||||
int m_ShadowmapWidth;
|
||||
int m_ShadowmapHeight;
|
||||
|
||||
ShadowSliceData[] m_AdditionalLightsShadowSlices = null;
|
||||
|
||||
int[] m_VisibleLightIndexToAdditionalLightIndex = null; // maps a "global" visible light index (index to renderingData.lightData.visibleLights) to an "additional light index" (index to arrays _AdditionalLightsPosition, _AdditionalShadowParams, ...), or -1 if it is not an additional light (i.e if it is the main light)
|
||||
int[] m_AdditionalLightIndexToVisibleLightIndex = null; // maps additional light index (index to arrays _AdditionalLightsPosition, _AdditionalShadowParams, ...) to its "global" visible light index (index to renderingData.lightData.visibleLights)
|
||||
List<int> m_ShadowSliceToAdditionalLightIndex = new List<int>(); // For each shadow slice, store the "additional light indices" of the punctual light that casts it
|
||||
List<int> m_GlobalShadowSliceIndexToPerLightShadowSliceIndex = new List<int>(); // For each shadow slice, store its "per-light shadow slice index" in the punctual light that casts it (can be up to 5 for point lights)
|
||||
|
||||
Vector4[] m_AdditionalLightIndexToShadowParams = null; // per-additional-light shadow info passed to the lighting shader
|
||||
Matrix4x4[] m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix = null; // per-shadow-slice info passed to the lighting shader
|
||||
|
||||
List<ShadowResolutionRequest> m_ShadowResolutionRequests = new List<ShadowResolutionRequest>(); // intermediate array used to compute the final resolution of each shadow slice rendered in the frame
|
||||
float[] m_VisibleLightIndexToCameraSquareDistance = null; // 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
|
||||
ShadowResolutionRequest[] m_SortedShadowResolutionRequests = null;
|
||||
int[] m_VisibleLightIndexToSortedShadowResolutionRequestsFirstSliceIndex = null; // for each visible light, store the index of its first shadow slice in m_SortedShadowResolutionRequests (for quicker access)
|
||||
List<RectInt> m_UnusedAtlasSquareAreas = new List<RectInt>(); // this list tracks space available in the atlas
|
||||
|
||||
bool m_SupportsBoxFilterForShadows;
|
||||
ProfilingSampler m_ProfilingSetupSampler = new ProfilingSampler("Setup Additional Shadows");
|
||||
|
||||
int MAX_PUNCTUAL_LIGHT_SHADOW_SLICES_IN_UBO // keep in sync with MAX_PUNCTUAL_LIGHT_SHADOW_SLICES_IN_UBO in Shadows.hlsl
|
||||
{
|
||||
get
|
||||
{
|
||||
if (UniversalRenderPipeline.maxVisibleAdditionalLights != UniversalRenderPipeline.k_MaxVisibleAdditionalLightsNonMobile)
|
||||
// Reduce uniform block size on Mobile/GL to avoid shader performance or compilation issues - keep in sync with MAX_PUNCTUAL_LIGHT_SHADOW_SLICES_IN_UBO in Shadows.hlsl
|
||||
return UniversalRenderPipeline.maxVisibleAdditionalLights;
|
||||
else
|
||||
return 545; // keep in sync with MAX_PUNCTUAL_LIGHT_SHADOW_SLICES_IN_UBO in Shadows.hlsl
|
||||
}
|
||||
}
|
||||
|
||||
public AdditionalLightsShadowCasterPass(RenderPassEvent evt)
|
||||
{
|
||||
base.profilingSampler = new ProfilingSampler(nameof(AdditionalLightsShadowCasterPass));
|
||||
renderPassEvent = evt;
|
||||
|
||||
AdditionalShadowsConstantBuffer._AdditionalLightsWorldToShadow = Shader.PropertyToID("_AdditionalLightsWorldToShadow");
|
||||
AdditionalShadowsConstantBuffer._AdditionalShadowParams = Shader.PropertyToID("_AdditionalShadowParams");
|
||||
AdditionalShadowsConstantBuffer._AdditionalShadowOffset0 = Shader.PropertyToID("_AdditionalShadowOffset0");
|
||||
AdditionalShadowsConstantBuffer._AdditionalShadowOffset1 = Shader.PropertyToID("_AdditionalShadowOffset1");
|
||||
AdditionalShadowsConstantBuffer._AdditionalShadowOffset2 = Shader.PropertyToID("_AdditionalShadowOffset2");
|
||||
AdditionalShadowsConstantBuffer._AdditionalShadowOffset3 = Shader.PropertyToID("_AdditionalShadowOffset3");
|
||||
AdditionalShadowsConstantBuffer._AdditionalShadowmapSize = Shader.PropertyToID("_AdditionalShadowmapSize");
|
||||
m_AdditionalLightsShadowmap.Init("_AdditionalLightsShadowmapTexture");
|
||||
|
||||
m_AdditionalLightsWorldToShadow_SSBO = Shader.PropertyToID("_AdditionalLightsWorldToShadow_SSBO");
|
||||
m_AdditionalShadowParams_SSBO = Shader.PropertyToID("_AdditionalShadowParams_SSBO");
|
||||
|
||||
m_UseStructuredBuffer = RenderingUtils.useStructuredBuffer;
|
||||
m_SupportsBoxFilterForShadows = Application.isMobilePlatform || SystemInfo.graphicsDeviceType == GraphicsDeviceType.Switch;
|
||||
|
||||
// Preallocated a fixed size. CommandBuffer.SetGlobal* does allow this data to grow.
|
||||
int maxVisibleAdditionalLights = UniversalRenderPipeline.maxVisibleAdditionalLights;
|
||||
const int maxMainLights = 1;
|
||||
int maxVisibleLights = UniversalRenderPipeline.maxVisibleAdditionalLights + maxMainLights;
|
||||
|
||||
// These array sizes should be as big as ScriptableCullingParameters.maximumVisibleLights (that is defined during ScriptableRenderer.SetupCullingParameters).
|
||||
// We initialize these array sizes with the number of visible lights allowed by the ForwardRenderer.
|
||||
// The number of visible lights can become much higher when using the Deferred rendering path, we resize the arrays during Setup() if required.
|
||||
m_AdditionalLightIndexToVisibleLightIndex = new int[maxVisibleLights];
|
||||
m_VisibleLightIndexToAdditionalLightIndex = new int[maxVisibleLights];
|
||||
m_VisibleLightIndexToSortedShadowResolutionRequestsFirstSliceIndex = new int[maxVisibleLights];
|
||||
m_AdditionalLightIndexToShadowParams = new Vector4[maxVisibleLights];
|
||||
m_VisibleLightIndexToCameraSquareDistance = new float[maxVisibleLights];
|
||||
|
||||
if (!m_UseStructuredBuffer)
|
||||
{
|
||||
// Uniform buffers are faster on some platforms, but they have stricter size limitations
|
||||
|
||||
m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix = new Matrix4x4[MAX_PUNCTUAL_LIGHT_SHADOW_SLICES_IN_UBO];
|
||||
m_UnusedAtlasSquareAreas.Capacity = MAX_PUNCTUAL_LIGHT_SHADOW_SLICES_IN_UBO;
|
||||
m_ShadowResolutionRequests.Capacity = MAX_PUNCTUAL_LIGHT_SHADOW_SLICES_IN_UBO;
|
||||
}
|
||||
}
|
||||
|
||||
private int GetPunctualLightShadowSlicesCount(in LightType lightType)
|
||||
{
|
||||
switch (lightType)
|
||||
{
|
||||
case LightType.Spot:
|
||||
return 1;
|
||||
case LightType.Point:
|
||||
return 6;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Magic numbers used to identify light type when rendering shadow receiver.
|
||||
// Keep in sync with AdditionalLightRealtimeShadow code in com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl
|
||||
private const float LightTypeIdentifierInShadowParams_Spot = 0;
|
||||
private const float LightTypeIdentifierInShadowParams_Point = 1;
|
||||
|
||||
|
||||
// Returns the guard angle that must be added to a frustum angle covering a projection map of resolution sliceResolutionInTexels,
|
||||
// in order to also cover a guard band of size guardBandSizeInTexels around the projection map.
|
||||
// Formula illustrated in https://i.ibb.co/wpW5Mnf/Calc-Guard-Angle.png
|
||||
internal static float CalcGuardAngle(float frustumAngleInDegrees, float guardBandSizeInTexels, float sliceResolutionInTexels)
|
||||
{
|
||||
float frustumAngle = frustumAngleInDegrees * Mathf.Deg2Rad;
|
||||
float halfFrustumAngle = frustumAngle / 2;
|
||||
float tanHalfFrustumAngle = Mathf.Tan(halfFrustumAngle);
|
||||
|
||||
float halfSliceResolution = sliceResolutionInTexels / 2;
|
||||
float halfGuardBand = guardBandSizeInTexels / 2;
|
||||
float factorBetweenAngleTangents = 1 + halfGuardBand / halfSliceResolution;
|
||||
|
||||
float tanHalfGuardAnglePlusHalfFrustumAngle = tanHalfFrustumAngle * factorBetweenAngleTangents;
|
||||
|
||||
float halfGuardAnglePlusHalfFrustumAngle = Mathf.Atan(tanHalfGuardAnglePlusHalfFrustumAngle);
|
||||
float halfGuardAngleInRadian = halfGuardAnglePlusHalfFrustumAngle - halfFrustumAngle;
|
||||
|
||||
float guardAngleInRadian = 2 * halfGuardAngleInRadian;
|
||||
float guardAngleInDegree = guardAngleInRadian * Mathf.Rad2Deg;
|
||||
|
||||
return guardAngleInDegree;
|
||||
}
|
||||
|
||||
private const int kMinimumPunctualLightHardShadowResolution = 8;
|
||||
private const int kMinimumPunctualLightSoftShadowResolution = 16;
|
||||
// Minimal shadow map resolution required to have meaningful shadows visible during lighting
|
||||
int MinimalPunctualLightShadowResolution(bool softShadow)
|
||||
{
|
||||
return softShadow ? kMinimumPunctualLightSoftShadowResolution : kMinimumPunctualLightHardShadowResolution;
|
||||
}
|
||||
|
||||
static bool m_IssuedMessageAboutPointLightHardShadowResolutionTooSmall = false;
|
||||
static bool m_IssuedMessageAboutPointLightSoftShadowResolutionTooSmall = false;
|
||||
|
||||
// Returns the guard angle that must be added to a point light shadow face frustum angle
|
||||
// in order to avoid shadows missing at the boundaries between cube faces.
|
||||
internal static float GetPointLightShadowFrustumFovBiasInDegrees(int shadowSliceResolution, bool shadowFiltering)
|
||||
{
|
||||
// Commented-out code below uses the theoretical formula to compute the required guard angle based on the number of additional
|
||||
// texels that the projection should cover. It is close to HDRP's HDShadowUtils.CalcGuardAnglePerspective method.
|
||||
// However, due to precision issues or other filterings performed at lighting for example, this formula also still requires a fudge factor.
|
||||
// Since we only handle a fixed number of resolutions, we use empirical values instead.
|
||||
#if false
|
||||
float fudgeFactor = 1.5f;
|
||||
return fudgeFactor * CalcGuardAngle(90, shadowFiltering ? 5 : 1, shadowSliceResolution);
|
||||
#endif
|
||||
|
||||
|
||||
float fovBias = 4.00f;
|
||||
|
||||
// Empirical value found to remove gaps between point light shadow faces in test scenes.
|
||||
// We can see that the guard angle is roughly proportional to the inverse of resolution https://docs.google.com/spreadsheets/d/1QrIZJn18LxVKq2-K1XS4EFRZcZdZOJTTKKhDN8Z1b_s
|
||||
if (shadowSliceResolution <= kMinimumPunctualLightHardShadowResolution)
|
||||
{
|
||||
if (!m_IssuedMessageAboutPointLightHardShadowResolutionTooSmall)
|
||||
{
|
||||
Debug.LogWarning("Too many additional punctual lights shadows, increase shadow atlas size or remove some shadowed lights");
|
||||
m_IssuedMessageAboutPointLightHardShadowResolutionTooSmall = true; // Only output this once per shadow requests configuration
|
||||
}
|
||||
}
|
||||
else if (shadowSliceResolution <= 16)
|
||||
fovBias = 43.0f;
|
||||
else if (shadowSliceResolution <= 32)
|
||||
fovBias = 18.55f;
|
||||
else if (shadowSliceResolution <= 64)
|
||||
fovBias = 8.63f;
|
||||
else if (shadowSliceResolution <= 128)
|
||||
fovBias = 4.13f;
|
||||
else if (shadowSliceResolution <= 256)
|
||||
fovBias = 2.03f;
|
||||
else if (shadowSliceResolution <= 512)
|
||||
fovBias = 1.00f;
|
||||
else if (shadowSliceResolution <= 1024)
|
||||
fovBias = 0.50f;
|
||||
|
||||
if (shadowFiltering)
|
||||
{
|
||||
if (shadowSliceResolution <= kMinimumPunctualLightSoftShadowResolution)
|
||||
{
|
||||
if (!m_IssuedMessageAboutPointLightSoftShadowResolutionTooSmall)
|
||||
{
|
||||
Debug.LogWarning("Too many additional punctual lights shadows to use Soft Shadows. Increase shadow atlas size, remove some shadowed lights or use Hard Shadows.");
|
||||
// With such small resolutions no fovBias can give good visual results
|
||||
m_IssuedMessageAboutPointLightSoftShadowResolutionTooSmall = true; // Only output this once per shadow requests configuration
|
||||
}
|
||||
}
|
||||
else if (shadowSliceResolution <= 32)
|
||||
fovBias += 9.35f;
|
||||
else if (shadowSliceResolution <= 64)
|
||||
fovBias += 4.07f;
|
||||
else if (shadowSliceResolution <= 128)
|
||||
fovBias += 1.77f;
|
||||
else if (shadowSliceResolution <= 256)
|
||||
fovBias += 0.85f;
|
||||
else if (shadowSliceResolution <= 512)
|
||||
fovBias += 0.39f;
|
||||
else if (shadowSliceResolution <= 1024)
|
||||
fovBias += 0.17f;
|
||||
|
||||
// These values were verified to work on platforms for which m_SupportsBoxFilterForShadows is true (Mobile, Switch).
|
||||
// TODO: Investigate finer-tuned values for those platforms. Soft shadows are implemented differently for them.
|
||||
}
|
||||
|
||||
return fovBias;
|
||||
}
|
||||
|
||||
bool m_IssuedMessageAboutShadowSlicesTooMany = false;
|
||||
|
||||
Vector4 m_MainLightShadowParams; // Shadow Fade parameters _MainLightShadowParams.zw are actually also used by AdditionalLights
|
||||
|
||||
// Adapted from InsertionSort() in com.unity.render-pipelines.high-definition/Runtime/Lighting/Shadow/HDDynamicShadowAtlas.cs
|
||||
// Sort array in decreasing requestedResolution order,
|
||||
// sub-sorting in "HardShadow > SoftShadow" and then "Spot > Point", 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)
|
||||
internal void InsertionSort(ShadowResolutionRequest[] array, int startIndex, int lastIndex)
|
||||
{
|
||||
int i = startIndex + 1;
|
||||
|
||||
while (i < lastIndex)
|
||||
{
|
||||
var curr = array[i];
|
||||
int j = i - 1;
|
||||
|
||||
// Sort in priority order
|
||||
while ((j >= 0) && ((curr.requestedResolution > array[j].requestedResolution)
|
||||
|| (curr.requestedResolution == array[j].requestedResolution && !curr.softShadow && array[j].softShadow)
|
||||
|| (curr.requestedResolution == array[j].requestedResolution && curr.softShadow == array[j].softShadow && !curr.pointLightShadow && array[j].pointLightShadow)
|
||||
|| (curr.requestedResolution == array[j].requestedResolution && curr.softShadow == array[j].softShadow && curr.pointLightShadow == array[j].pointLightShadow && m_VisibleLightIndexToCameraSquareDistance[curr.visibleLightIndex] < m_VisibleLightIndexToCameraSquareDistance[array[j].visibleLightIndex])
|
||||
|| (curr.requestedResolution == array[j].requestedResolution && curr.softShadow == array[j].softShadow && curr.pointLightShadow == array[j].pointLightShadow && m_VisibleLightIndexToCameraSquareDistance[curr.visibleLightIndex] == m_VisibleLightIndexToCameraSquareDistance[array[j].visibleLightIndex] && curr.visibleLightIndex < array[j].visibleLightIndex)
|
||||
|| (curr.requestedResolution == array[j].requestedResolution && curr.softShadow == array[j].softShadow && curr.pointLightShadow == array[j].pointLightShadow && m_VisibleLightIndexToCameraSquareDistance[curr.visibleLightIndex] == m_VisibleLightIndexToCameraSquareDistance[array[j].visibleLightIndex] && curr.visibleLightIndex == array[j].visibleLightIndex && curr.perLightShadowSliceIndex < array[j].perLightShadowSliceIndex)))
|
||||
{
|
||||
array[j + 1] = array[j];
|
||||
j--;
|
||||
}
|
||||
|
||||
array[j + 1] = curr;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
int EstimateScaleFactorNeededToFitAllShadowsInAtlas(in ShadowResolutionRequest[] shadowResolutionRequests, int endIndex, int atlasWidth)
|
||||
{
|
||||
long totalTexelsInShadowAtlas = atlasWidth * atlasWidth;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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.
|
||||
void AtlasLayout(int atlasSize, int totalShadowSlicesCount, int estimatedScaleFactor)
|
||||
{
|
||||
bool allShadowSlicesFitInAtlas = false;
|
||||
bool tooManyShadows = false;
|
||||
int shadowSlicesScaleFactor = estimatedScaleFactor;
|
||||
|
||||
while (!allShadowSlicesFitInAtlas && !tooManyShadows)
|
||||
{
|
||||
m_UnusedAtlasSquareAreas.Clear();
|
||||
m_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 < MinimalPunctualLightShadowResolution(m_SortedShadowResolutionRequests[shadowRequestIndex].softShadow))
|
||||
{
|
||||
tooManyShadows = true;
|
||||
break;
|
||||
}
|
||||
|
||||
bool foundSpaceInAtlas = false;
|
||||
|
||||
// Try to find free space in the atlas
|
||||
for (int unusedAtlasSquareAreaIndex = 0; unusedAtlasSquareAreaIndex < m_UnusedAtlasSquareAreas.Count; ++unusedAtlasSquareAreaIndex)
|
||||
{
|
||||
var atlasArea = m_UnusedAtlasSquareAreas[unusedAtlasSquareAreaIndex];
|
||||
var atlasAreaWidth = atlasArea.width;
|
||||
var atlasAreaHeight = atlasArea.height;
|
||||
var atlasAreaX = atlasArea.x;
|
||||
var atlasAreaY = atlasArea.y;
|
||||
if (atlasAreaWidth >= resolution)
|
||||
{
|
||||
// we can use this atlas area for the shadow request
|
||||
m_SortedShadowResolutionRequests[shadowRequestIndex].offsetX = atlasAreaX;
|
||||
m_SortedShadowResolutionRequests[shadowRequestIndex].offsetY = atlasAreaY;
|
||||
m_SortedShadowResolutionRequests[shadowRequestIndex].allocatedResolution = resolution;
|
||||
|
||||
// this atlas space is not available anymore, so remove it from the list
|
||||
m_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;
|
||||
var newSquareAreaX = atlasAreaX;
|
||||
var 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)
|
||||
m_UnusedAtlasSquareAreas.Insert(unusedAtlasSquareAreaIndex + newSquareAreasCount, new RectInt(newSquareAreaX, newSquareAreaY, newSquareAreaWidth, newSquareAreaHeight));
|
||||
++newSquareAreasCount;
|
||||
}
|
||||
|
||||
foundSpaceInAtlas = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundSpaceInAtlas)
|
||||
{
|
||||
allShadowSlicesFitInAtlas = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!allShadowSlicesFitInAtlas && !tooManyShadows)
|
||||
shadowSlicesScaleFactor *= 2;
|
||||
}
|
||||
|
||||
if (!m_IssuedMessageAboutShadowMapsTooBig && tooManyShadows)
|
||||
{
|
||||
Debug.LogWarning($"Too many additional punctual lights shadows. URP tried reducing shadow resolutions by {shadowSlicesScaleFactor} but it was still too much. Increase shadow atlas size, decrease big shadow resolutions, or reduce the number of shadow maps active in the same frame (currently was {totalShadowSlicesCount}).");
|
||||
m_IssuedMessageAboutShadowMapsTooBig = true; // Only output this once per shadow requests configuration
|
||||
}
|
||||
|
||||
if (!m_IssuedMessageAboutShadowMapsRescale && shadowSlicesScaleFactor > 1)
|
||||
{
|
||||
Debug.Log($"Reduced additional punctual light shadows resolution by {shadowSlicesScaleFactor} to make {totalShadowSlicesCount} shadow maps fit in the {atlasSize}x{atlasSize} shadow atlas. To avoid this, increase shadow atlas size, decrease big shadow resolutions, or reduce the number of shadow maps active in the same frame");
|
||||
m_IssuedMessageAboutShadowMapsRescale = true; // Only output this once per shadow requests configuration
|
||||
}
|
||||
}
|
||||
|
||||
bool m_IssuedMessageAboutShadowMapsRescale = false;
|
||||
bool m_IssuedMessageAboutShadowMapsTooBig = false;
|
||||
bool m_IssuedMessageAboutRemovedShadowSlices = false;
|
||||
|
||||
Dictionary<int, ulong> m_ShadowRequestsHashes = new Dictionary<int, ulong>(); // used to keep track of changes in the shadow requests and shadow atlas configuration (per camera)
|
||||
|
||||
ulong ResolutionLog2ForHash(int resolution)
|
||||
{
|
||||
switch (resolution)
|
||||
{
|
||||
case 4096: return 12;
|
||||
case 2048: return 11;
|
||||
case 1024: return 10;
|
||||
case 0512: return 09;
|
||||
}
|
||||
return 08;
|
||||
}
|
||||
|
||||
ulong ComputeShadowRequestHash(ref RenderingData renderingData)
|
||||
{
|
||||
ulong numberOfShadowedPointLights = 0;
|
||||
ulong numberOfSoftShadowedLights = 0;
|
||||
ulong numberOfShadowsWithResolution0128 = 0;
|
||||
ulong numberOfShadowsWithResolution0256 = 0;
|
||||
ulong numberOfShadowsWithResolution0512 = 0;
|
||||
ulong numberOfShadowsWithResolution1024 = 0;
|
||||
ulong numberOfShadowsWithResolution2048 = 0;
|
||||
ulong numberOfShadowsWithResolution4096 = 0;
|
||||
|
||||
var visibleLights = renderingData.lightData.visibleLights;
|
||||
for (int visibleLightIndex = 0; visibleLightIndex < visibleLights.Length; ++visibleLightIndex)
|
||||
{
|
||||
if (!IsValidShadowCastingLight(ref renderingData.lightData, visibleLightIndex))
|
||||
continue;
|
||||
if (visibleLights[visibleLightIndex].lightType == LightType.Point)
|
||||
++numberOfShadowedPointLights;
|
||||
if (visibleLights[visibleLightIndex].light.shadows == LightShadows.Soft)
|
||||
++numberOfSoftShadowedLights;
|
||||
if (renderingData.shadowData.resolution[visibleLightIndex] == 0128)
|
||||
++numberOfShadowsWithResolution0128;
|
||||
if (renderingData.shadowData.resolution[visibleLightIndex] == 0256)
|
||||
++numberOfShadowsWithResolution0256;
|
||||
if (renderingData.shadowData.resolution[visibleLightIndex] == 0512)
|
||||
++numberOfShadowsWithResolution0512;
|
||||
if (renderingData.shadowData.resolution[visibleLightIndex] == 1024)
|
||||
++numberOfShadowsWithResolution1024;
|
||||
if (renderingData.shadowData.resolution[visibleLightIndex] == 2048)
|
||||
++numberOfShadowsWithResolution2048;
|
||||
if (renderingData.shadowData.resolution[visibleLightIndex] == 4096)
|
||||
++numberOfShadowsWithResolution4096;
|
||||
}
|
||||
ulong shadowRequestsHash = ResolutionLog2ForHash(renderingData.shadowData.additionalLightsShadowmapWidth) - 8; // bits [00~02]
|
||||
shadowRequestsHash |= numberOfShadowedPointLights << 03; // bits [03~10]
|
||||
shadowRequestsHash |= numberOfSoftShadowedLights << 11; // bits [11~18]
|
||||
shadowRequestsHash |= numberOfShadowsWithResolution0128 << 19; // bits [19~26]
|
||||
shadowRequestsHash |= numberOfShadowsWithResolution0256 << 27; // bits [27~34]
|
||||
shadowRequestsHash |= numberOfShadowsWithResolution0512 << 35; // bits [35~42]
|
||||
shadowRequestsHash |= numberOfShadowsWithResolution1024 << 43; // bits [43~49]
|
||||
shadowRequestsHash |= numberOfShadowsWithResolution2048 << 50; // bits [50~56]
|
||||
shadowRequestsHash |= numberOfShadowsWithResolution4096 << 57; // bits [57~63]
|
||||
return shadowRequestsHash;
|
||||
}
|
||||
|
||||
public bool Setup(ref RenderingData renderingData)
|
||||
{
|
||||
using var profScope = new ProfilingScope(null, m_ProfilingSetupSampler);
|
||||
|
||||
Clear();
|
||||
|
||||
m_ShadowmapWidth = renderingData.shadowData.additionalLightsShadowmapWidth;
|
||||
m_ShadowmapHeight = renderingData.shadowData.additionalLightsShadowmapHeight;
|
||||
|
||||
// In order to apply shadow fade to AdditionalLights, we need to set constants _MainLightShadowParams.zw used by function GetShadowFade in Shadows.hlsl.
|
||||
// However, we also have to make sure not to override _MainLightShadowParams.xy constants, that are used by MainLight only. Therefore we need to store these values in m_MainLightShadowParams and set them again during SetupAdditionalLightsShadowReceiverConstants.
|
||||
m_MainLightShadowParams = ShadowUtils.GetMainLightShadowParams(ref renderingData);
|
||||
|
||||
var visibleLights = renderingData.lightData.visibleLights;
|
||||
int additionalLightsCount = renderingData.lightData.additionalLightsCount;
|
||||
|
||||
int atlasWidth = renderingData.shadowData.additionalLightsShadowmapWidth;
|
||||
|
||||
int 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.
|
||||
|
||||
m_ShadowResolutionRequests.Clear();
|
||||
|
||||
// Check changes in the shadow requests and shadow atlas configuration - compute shadow request/configuration hash
|
||||
if (!renderingData.cameraData.isPreviewCamera)
|
||||
{
|
||||
ulong newShadowRequestHash = ComputeShadowRequestHash(ref renderingData);
|
||||
ulong oldShadowRequestHash = 0;
|
||||
m_ShadowRequestsHashes.TryGetValue(renderingData.cameraData.camera.GetHashCode(), out oldShadowRequestHash);
|
||||
if (oldShadowRequestHash != newShadowRequestHash)
|
||||
{
|
||||
m_ShadowRequestsHashes[renderingData.cameraData.camera.GetHashCode()] = newShadowRequestHash;
|
||||
|
||||
// congif changed ; reset error message flags as we might need to issue those messages again
|
||||
m_IssuedMessageAboutPointLightHardShadowResolutionTooSmall = false;
|
||||
m_IssuedMessageAboutPointLightSoftShadowResolutionTooSmall = false;
|
||||
m_IssuedMessageAboutShadowMapsRescale = false;
|
||||
m_IssuedMessageAboutShadowMapsTooBig = false;
|
||||
m_IssuedMessageAboutShadowSlicesTooMany = false;
|
||||
m_IssuedMessageAboutRemovedShadowSlices = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_AdditionalLightIndexToVisibleLightIndex.Length < visibleLights.Length)
|
||||
{
|
||||
// Array "visibleLights" is returned by ScriptableRenderContext.Cull()
|
||||
// The maximum number of "visibleLights" that ScriptableRenderContext.Cull() should return, is defined by parameter ScriptableCullingParameters.maximumVisibleLights
|
||||
// Universal RP sets this "ScriptableCullingParameters.maximumVisibleLights" value during ScriptableRenderer.SetupCullingParameters.
|
||||
// When using Deferred rendering, it is possible to specify a very high number of visible lights.
|
||||
m_AdditionalLightIndexToVisibleLightIndex = new int[visibleLights.Length];
|
||||
m_VisibleLightIndexToAdditionalLightIndex = new int[visibleLights.Length];
|
||||
m_AdditionalLightIndexToShadowParams = new Vector4[visibleLights.Length];
|
||||
m_VisibleLightIndexToCameraSquareDistance = new float[visibleLights.Length];
|
||||
m_VisibleLightIndexToSortedShadowResolutionRequestsFirstSliceIndex = new int[visibleLights.Length];
|
||||
}
|
||||
|
||||
// reset m_VisibleLightIndexClosenessToCamera
|
||||
for (int visibleLightIndex = 0; visibleLightIndex < m_VisibleLightIndexToCameraSquareDistance.Length; ++visibleLightIndex)
|
||||
m_VisibleLightIndexToCameraSquareDistance[visibleLightIndex] = float.MaxValue;
|
||||
|
||||
for (int visibleLightIndex = 0; visibleLightIndex < visibleLights.Length; ++visibleLightIndex)
|
||||
{
|
||||
if (visibleLightIndex == renderingData.lightData.mainLightIndex)
|
||||
// Skip main directional light as it is not packed into the shadow atlas
|
||||
continue;
|
||||
|
||||
if (IsValidShadowCastingLight(ref renderingData.lightData, visibleLightIndex))
|
||||
{
|
||||
int shadowSlicesCountForThisLight = GetPunctualLightShadowSlicesCount(visibleLights[visibleLightIndex].lightType);
|
||||
totalShadowResolutionRequestsCount += shadowSlicesCountForThisLight;
|
||||
|
||||
for (int perLightShadowSliceIndex = 0; perLightShadowSliceIndex < shadowSlicesCountForThisLight; ++perLightShadowSliceIndex)
|
||||
{
|
||||
m_ShadowResolutionRequests.Add(new ShadowResolutionRequest(visibleLightIndex, perLightShadowSliceIndex, renderingData.shadowData.resolution[visibleLightIndex],
|
||||
(visibleLights[visibleLightIndex].light.shadows == LightShadows.Soft), (visibleLights[visibleLightIndex].lightType == LightType.Point)));
|
||||
}
|
||||
// mark this light as casting shadows
|
||||
m_VisibleLightIndexToCameraSquareDistance[visibleLightIndex] = (renderingData.cameraData.camera.transform.position - visibleLights[visibleLightIndex].light.transform.position).sqrMagnitude;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_SortedShadowResolutionRequests == null || m_SortedShadowResolutionRequests.Length < totalShadowResolutionRequestsCount)
|
||||
m_SortedShadowResolutionRequests = new ShadowResolutionRequest[totalShadowResolutionRequestsCount];
|
||||
|
||||
for (int shadowRequestIndex = 0; shadowRequestIndex < m_ShadowResolutionRequests.Count; ++shadowRequestIndex)
|
||||
m_SortedShadowResolutionRequests[shadowRequestIndex] = m_ShadowResolutionRequests[shadowRequestIndex];
|
||||
for (int sortedArrayIndex = totalShadowResolutionRequestsCount; sortedArrayIndex < m_SortedShadowResolutionRequests.Length; ++sortedArrayIndex)
|
||||
m_SortedShadowResolutionRequests[sortedArrayIndex].requestedResolution = 0; // reset unused entries
|
||||
InsertionSort(m_SortedShadowResolutionRequests, 0, totalShadowResolutionRequestsCount);
|
||||
|
||||
// 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 = m_UseStructuredBuffer ? totalShadowResolutionRequestsCount : Math.Min(totalShadowResolutionRequestsCount, MAX_PUNCTUAL_LIGHT_SHADOW_SLICES_IN_UBO); // Number of shadow slices that we will actually be able to fit in the shadow atlas without causing visual artifacts.
|
||||
|
||||
// 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)
|
||||
{
|
||||
estimatedScaleFactor = EstimateScaleFactorNeededToFitAllShadowsInAtlas(m_SortedShadowResolutionRequests, totalShadowSlicesCount, atlasWidth);
|
||||
|
||||
// check if resolution of the least priority shadow slice request would be acceptable
|
||||
if (m_SortedShadowResolutionRequests[totalShadowSlicesCount - 1].requestedResolution >= estimatedScaleFactor * MinimalPunctualLightShadowResolution(m_SortedShadowResolutionRequests[totalShadowSlicesCount - 1].softShadow))
|
||||
allShadowsAfterStartIndexHaveEnoughResolution = true;
|
||||
else // Skip shadow requests for this light ; their resolution is too small to look any good
|
||||
totalShadowSlicesCount -= GetPunctualLightShadowSlicesCount(m_SortedShadowResolutionRequests[totalShadowSlicesCount - 1].pointLightShadow ? LightType.Point : LightType.Spot);
|
||||
}
|
||||
if (totalShadowSlicesCount < totalShadowResolutionRequestsCount)
|
||||
{
|
||||
if (!m_IssuedMessageAboutRemovedShadowSlices)
|
||||
{
|
||||
Debug.LogWarning($"Too many additional punctual lights shadows to look good, URP removed {totalShadowResolutionRequestsCount - totalShadowSlicesCount } shadow maps to make the others fit in the shadow atlas. To avoid this, increase shadow atlas size, remove some shadowed lights, replace soft shadows by hard shadows ; or replace point lights by spot lights");
|
||||
m_IssuedMessageAboutRemovedShadowSlices = true; // Only output this once per shadow requests configuration
|
||||
}
|
||||
}
|
||||
for (int sortedArrayIndex = totalShadowSlicesCount; sortedArrayIndex < m_SortedShadowResolutionRequests.Length; ++sortedArrayIndex)
|
||||
m_SortedShadowResolutionRequests[sortedArrayIndex].requestedResolution = 0; // Reset entries that we cannot fit in the atlas
|
||||
|
||||
// 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)
|
||||
m_VisibleLightIndexToSortedShadowResolutionRequestsFirstSliceIndex[m_SortedShadowResolutionRequests[sortedArrayIndex].visibleLightIndex] = sortedArrayIndex;
|
||||
|
||||
AtlasLayout(atlasWidth, totalShadowSlicesCount, estimatedScaleFactor);
|
||||
|
||||
|
||||
if (m_AdditionalLightsShadowSlices == null || m_AdditionalLightsShadowSlices.Length < totalShadowSlicesCount)
|
||||
m_AdditionalLightsShadowSlices = new ShadowSliceData[totalShadowSlicesCount];
|
||||
|
||||
if (m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix == null ||
|
||||
(m_UseStructuredBuffer && (m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix.Length < totalShadowSlicesCount))) // m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix can be resized when using SSBO to pass shadow data (no size limitation)
|
||||
m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix = new Matrix4x4[totalShadowSlicesCount];
|
||||
|
||||
// initialize _AdditionalShadowParams
|
||||
Vector4 defaultShadowParams = new Vector4(0 /*shadowStrength*/, 0, 0, -1 /*perLightFirstShadowSliceIndex*/);
|
||||
// shadowParams.x is used in RenderAdditionalShadowMapAtlas to skip shadow map rendering for non-shadow-casting lights
|
||||
// shadowParams.w is used in Lighting shader to find if Additional light casts shadows
|
||||
for (int i = 0; i < visibleLights.Length; ++i)
|
||||
m_AdditionalLightIndexToShadowParams[i] = defaultShadowParams;
|
||||
|
||||
int validShadowCastingLightsCount = 0;
|
||||
bool supportsSoftShadows = renderingData.shadowData.supportsSoftShadows;
|
||||
int additionalLightIndex = -1;
|
||||
for (int visibleLightIndex = 0; visibleLightIndex < visibleLights.Length && m_ShadowSliceToAdditionalLightIndex.Count < totalShadowSlicesCount; ++visibleLightIndex)
|
||||
{
|
||||
VisibleLight shadowLight = visibleLights[visibleLightIndex];
|
||||
|
||||
// Skip main directional light as it is not packed into the shadow atlas
|
||||
if (visibleLightIndex == renderingData.lightData.mainLightIndex)
|
||||
{
|
||||
m_VisibleLightIndexToAdditionalLightIndex[visibleLightIndex] = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
++additionalLightIndex; // ForwardLights.SetupAdditionalLightConstants skips main Light and thus uses a different index for additional lights
|
||||
m_AdditionalLightIndexToVisibleLightIndex[additionalLightIndex] = visibleLightIndex;
|
||||
m_VisibleLightIndexToAdditionalLightIndex[visibleLightIndex] = additionalLightIndex;
|
||||
|
||||
LightType lightType = shadowLight.lightType;
|
||||
int perLightShadowSlicesCount = GetPunctualLightShadowSlicesCount(lightType);
|
||||
|
||||
if ((m_ShadowSliceToAdditionalLightIndex.Count + perLightShadowSlicesCount) > totalShadowSlicesCount && IsValidShadowCastingLight(ref renderingData.lightData, visibleLightIndex))
|
||||
{
|
||||
if (!m_IssuedMessageAboutShadowSlicesTooMany)
|
||||
{
|
||||
// This case can especially happen in Deferred, where there can be a high number of visibleLights
|
||||
Debug.Log($"There are too many shadowed additional punctual lights active at the same time, URP will not render all the shadows. To ensure all shadows are rendered, reduce the number of shadowed additional lights in the scene ; make sure they are not active at the same time ; or replace point lights by spot lights (spot lights use less shadow maps than point lights).");
|
||||
m_IssuedMessageAboutShadowSlicesTooMany = true; // Only output this once
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
int perLightFirstShadowSliceIndex = m_ShadowSliceToAdditionalLightIndex.Count; // shadowSliceIndex within the global array of all additional light shadow slices
|
||||
|
||||
bool isValidShadowCastingLight = false;
|
||||
for (int perLightShadowSlice = 0; perLightShadowSlice < perLightShadowSlicesCount; ++perLightShadowSlice)
|
||||
{
|
||||
int globalShadowSliceIndex = m_ShadowSliceToAdditionalLightIndex.Count; // shadowSliceIndex within the global array of all additional light shadow slices
|
||||
|
||||
bool lightRangeContainsShadowCasters = renderingData.cullResults.GetShadowCasterBounds(visibleLightIndex, out var shadowCastersBounds);
|
||||
if (lightRangeContainsShadowCasters)
|
||||
{
|
||||
// We need to iterate the lights even though additional lights are disabled because
|
||||
// cullResults.GetShadowCasterBounds() does the fence sync for the shadow culling jobs.
|
||||
if (!renderingData.shadowData.supportsAdditionalLightShadows)
|
||||
continue;
|
||||
|
||||
if (IsValidShadowCastingLight(ref renderingData.lightData, visibleLightIndex))
|
||||
{
|
||||
if (m_VisibleLightIndexToSortedShadowResolutionRequestsFirstSliceIndex[visibleLightIndex] == -1)
|
||||
{
|
||||
// We could not find place in the shadow atlas for shadow maps of this light.
|
||||
// Skip it.
|
||||
}
|
||||
else if (lightType == LightType.Spot)
|
||||
{
|
||||
bool success = ShadowUtils.ExtractSpotLightMatrix(ref renderingData.cullResults,
|
||||
ref renderingData.shadowData,
|
||||
visibleLightIndex,
|
||||
out var shadowTransform,
|
||||
out m_AdditionalLightsShadowSlices[globalShadowSliceIndex].viewMatrix,
|
||||
out m_AdditionalLightsShadowSlices[globalShadowSliceIndex].projectionMatrix,
|
||||
out m_AdditionalLightsShadowSlices[globalShadowSliceIndex].splitData);
|
||||
|
||||
if (success)
|
||||
{
|
||||
m_ShadowSliceToAdditionalLightIndex.Add(additionalLightIndex);
|
||||
m_GlobalShadowSliceIndexToPerLightShadowSliceIndex.Add(perLightShadowSlice);
|
||||
var light = shadowLight.light;
|
||||
float shadowStrength = light.shadowStrength;
|
||||
float softShadows = (supportsSoftShadows && light.shadows == LightShadows.Soft) ? 1.0f : 0.0f;
|
||||
Vector4 shadowParams = new Vector4(shadowStrength, softShadows, LightTypeIdentifierInShadowParams_Spot, perLightFirstShadowSliceIndex);
|
||||
m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix[globalShadowSliceIndex] = shadowTransform;
|
||||
m_AdditionalLightIndexToShadowParams[additionalLightIndex] = shadowParams;
|
||||
isValidShadowCastingLight = true;
|
||||
}
|
||||
}
|
||||
else if (lightType == LightType.Point)
|
||||
{
|
||||
var sliceResolution = m_SortedShadowResolutionRequests[m_VisibleLightIndexToSortedShadowResolutionRequestsFirstSliceIndex[visibleLightIndex]].allocatedResolution;
|
||||
float fovBias = GetPointLightShadowFrustumFovBiasInDegrees(sliceResolution, (shadowLight.light.shadows == LightShadows.Soft));
|
||||
// Note: the same fovBias will also be used to compute ShadowUtils.GetShadowBias
|
||||
|
||||
bool success = ShadowUtils.ExtractPointLightMatrix(ref renderingData.cullResults,
|
||||
ref renderingData.shadowData,
|
||||
visibleLightIndex,
|
||||
(CubemapFace)perLightShadowSlice,
|
||||
fovBias,
|
||||
out var shadowTransform,
|
||||
out m_AdditionalLightsShadowSlices[globalShadowSliceIndex].viewMatrix,
|
||||
out m_AdditionalLightsShadowSlices[globalShadowSliceIndex].projectionMatrix,
|
||||
out m_AdditionalLightsShadowSlices[globalShadowSliceIndex].splitData);
|
||||
|
||||
if (success)
|
||||
{
|
||||
m_ShadowSliceToAdditionalLightIndex.Add(additionalLightIndex);
|
||||
m_GlobalShadowSliceIndexToPerLightShadowSliceIndex.Add(perLightShadowSlice);
|
||||
var light = shadowLight.light;
|
||||
float shadowStrength = light.shadowStrength;
|
||||
float softShadows = (supportsSoftShadows && light.shadows == LightShadows.Soft) ? 1.0f : 0.0f;
|
||||
Vector4 shadowParams = new Vector4(shadowStrength, softShadows, LightTypeIdentifierInShadowParams_Point, perLightFirstShadowSliceIndex);
|
||||
m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix[globalShadowSliceIndex] = shadowTransform;
|
||||
m_AdditionalLightIndexToShadowParams[additionalLightIndex] = shadowParams;
|
||||
isValidShadowCastingLight = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isValidShadowCastingLight)
|
||||
validShadowCastingLightsCount++;
|
||||
}
|
||||
|
||||
// Lights that need to be rendered in the shadow map atlas
|
||||
if (validShadowCastingLightsCount == 0)
|
||||
return false;
|
||||
|
||||
int shadowCastingLightsBufferCount = m_ShadowSliceToAdditionalLightIndex.Count;
|
||||
|
||||
// Trim shadow atlas dimensions if possible (to avoid allocating texture space that will not be used)
|
||||
int atlasMaxX = 0;
|
||||
int atlasMaxY = 0;
|
||||
for (int sortedShadowResolutionRequestIndex = 0; sortedShadowResolutionRequestIndex < totalShadowSlicesCount; ++sortedShadowResolutionRequestIndex)
|
||||
{
|
||||
var shadowResolutionRequest = m_SortedShadowResolutionRequests[sortedShadowResolutionRequestIndex];
|
||||
atlasMaxX = Mathf.Max(atlasMaxX, shadowResolutionRequest.offsetX + shadowResolutionRequest.allocatedResolution);
|
||||
atlasMaxY = Mathf.Max(atlasMaxY, shadowResolutionRequest.offsetY + shadowResolutionRequest.allocatedResolution);
|
||||
}
|
||||
// ...but make sure we still use power-of-two dimensions (might perform better on some hardware)
|
||||
m_ShadowmapWidth = Mathf.NextPowerOfTwo(atlasMaxX);
|
||||
m_ShadowmapHeight = Mathf.NextPowerOfTwo(atlasMaxY);
|
||||
|
||||
float oneOverAtlasWidth = 1.0f / m_ShadowmapWidth;
|
||||
float oneOverAtlasHeight = 1.0f / m_ShadowmapHeight;
|
||||
|
||||
Matrix4x4 sliceTransform;
|
||||
for (int globalShadowSliceIndex = 0; globalShadowSliceIndex < shadowCastingLightsBufferCount; ++globalShadowSliceIndex)
|
||||
{
|
||||
additionalLightIndex = m_ShadowSliceToAdditionalLightIndex[globalShadowSliceIndex];
|
||||
|
||||
// We can skip the slice if strength is zero.
|
||||
if (Mathf.Approximately(m_AdditionalLightIndexToShadowParams[additionalLightIndex].x, 0.0f) || Mathf.Approximately(m_AdditionalLightIndexToShadowParams[additionalLightIndex].w, -1.0f))
|
||||
continue;
|
||||
|
||||
int visibleLightIndex = m_AdditionalLightIndexToVisibleLightIndex[additionalLightIndex];
|
||||
int sortedShadowResolutionRequestFirstSliceIndex = m_VisibleLightIndexToSortedShadowResolutionRequestsFirstSliceIndex[visibleLightIndex];
|
||||
int perLightSliceIndex = m_GlobalShadowSliceIndexToPerLightShadowSliceIndex[globalShadowSliceIndex];
|
||||
var shadowResolutionRequest = m_SortedShadowResolutionRequests[sortedShadowResolutionRequestFirstSliceIndex + perLightSliceIndex];
|
||||
int sliceResolution = shadowResolutionRequest.allocatedResolution;
|
||||
|
||||
sliceTransform = Matrix4x4.identity;
|
||||
sliceTransform.m00 = sliceResolution * oneOverAtlasWidth;
|
||||
sliceTransform.m11 = sliceResolution * oneOverAtlasHeight;
|
||||
|
||||
m_AdditionalLightsShadowSlices[globalShadowSliceIndex].offsetX = shadowResolutionRequest.offsetX;
|
||||
m_AdditionalLightsShadowSlices[globalShadowSliceIndex].offsetY = shadowResolutionRequest.offsetY;
|
||||
m_AdditionalLightsShadowSlices[globalShadowSliceIndex].resolution = sliceResolution;
|
||||
|
||||
sliceTransform.m03 = m_AdditionalLightsShadowSlices[globalShadowSliceIndex].offsetX * oneOverAtlasWidth;
|
||||
sliceTransform.m13 = m_AdditionalLightsShadowSlices[globalShadowSliceIndex].offsetY * oneOverAtlasHeight;
|
||||
|
||||
// We bake scale and bias to each shadow map in the atlas in the matrix.
|
||||
// saves some instructions in shader.
|
||||
m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix[globalShadowSliceIndex] = sliceTransform * m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix[globalShadowSliceIndex];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
|
||||
{
|
||||
m_AdditionalLightsShadowmapTexture = ShadowUtils.GetTemporaryShadowTexture(m_ShadowmapWidth, m_ShadowmapHeight, k_ShadowmapBufferBits);
|
||||
ConfigureTarget(new RenderTargetIdentifier(m_AdditionalLightsShadowmapTexture));
|
||||
ConfigureClear(ClearFlag.All, Color.black);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
if (renderingData.shadowData.supportsAdditionalLightShadows)
|
||||
RenderAdditionalShadowmapAtlas(ref context, ref renderingData.cullResults, ref renderingData.lightData, ref renderingData.shadowData);
|
||||
}
|
||||
|
||||
public override void OnCameraCleanup(CommandBuffer cmd)
|
||||
{
|
||||
if (cmd == null)
|
||||
throw new ArgumentNullException("cmd");
|
||||
|
||||
if (m_AdditionalLightsShadowmapTexture)
|
||||
{
|
||||
RenderTexture.ReleaseTemporary(m_AdditionalLightsShadowmapTexture);
|
||||
m_AdditionalLightsShadowmapTexture = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the "additional light index" (used to index arrays _AdditionalLightsPosition, _AdditionalShadowParams, ...) from the "global" visible light index
|
||||
// Function called by Deferred Renderer
|
||||
public int GetShadowLightIndexFromLightIndex(int visibleLightIndex)
|
||||
{
|
||||
if (visibleLightIndex < 0 || visibleLightIndex >= m_VisibleLightIndexToAdditionalLightIndex.Length)
|
||||
return -1;
|
||||
|
||||
return m_VisibleLightIndexToAdditionalLightIndex[visibleLightIndex];
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
m_ShadowSliceToAdditionalLightIndex.Clear();
|
||||
m_GlobalShadowSliceIndexToPerLightShadowSliceIndex.Clear();
|
||||
m_AdditionalLightsShadowmapTexture = null;
|
||||
}
|
||||
|
||||
void RenderAdditionalShadowmapAtlas(ref ScriptableRenderContext context, ref CullingResults cullResults, ref LightData lightData, ref ShadowData shadowData)
|
||||
{
|
||||
NativeArray<VisibleLight> visibleLights = lightData.visibleLights;
|
||||
|
||||
bool additionalLightHasSoftShadows = false;
|
||||
// NOTE: Do NOT mix ProfilingScope with named CommandBuffers i.e. CommandBufferPool.Get("name").
|
||||
// Currently there's an issue which results in mismatched markers.
|
||||
CommandBuffer cmd = CommandBufferPool.Get();
|
||||
using (new ProfilingScope(cmd, ProfilingSampler.Get(URPProfileId.AdditionalLightsShadow)))
|
||||
{
|
||||
bool anyShadowSliceRenderer = false;
|
||||
int shadowSlicesCount = m_ShadowSliceToAdditionalLightIndex.Count;
|
||||
for (int globalShadowSliceIndex = 0; globalShadowSliceIndex < shadowSlicesCount; ++globalShadowSliceIndex)
|
||||
{
|
||||
int additionalLightIndex = m_ShadowSliceToAdditionalLightIndex[globalShadowSliceIndex];
|
||||
|
||||
// we do the shadow strength check here again here because we might have zero strength for non-shadow-casting lights.
|
||||
// In that case we need the shadow data buffer but we can skip rendering them to shadowmap.
|
||||
if (Mathf.Approximately(m_AdditionalLightIndexToShadowParams[additionalLightIndex].x, 0.0f) || Mathf.Approximately(m_AdditionalLightIndexToShadowParams[additionalLightIndex].w, -1.0f))
|
||||
continue;
|
||||
|
||||
int visibleLightIndex = m_AdditionalLightIndexToVisibleLightIndex[additionalLightIndex];
|
||||
|
||||
VisibleLight shadowLight = visibleLights[visibleLightIndex];
|
||||
|
||||
ShadowSliceData shadowSliceData = m_AdditionalLightsShadowSlices[globalShadowSliceIndex];
|
||||
|
||||
var settings = new ShadowDrawingSettings(cullResults, visibleLightIndex);
|
||||
settings.splitData = shadowSliceData.splitData;
|
||||
Vector4 shadowBias = ShadowUtils.GetShadowBias(ref shadowLight, visibleLightIndex,
|
||||
ref shadowData, shadowSliceData.projectionMatrix, shadowSliceData.resolution);
|
||||
ShadowUtils.SetupShadowCasterConstantBuffer(cmd, ref shadowLight, shadowBias);
|
||||
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.CastingPunctualLightShadow, true);
|
||||
ShadowUtils.RenderShadowSlice(cmd, ref context, ref shadowSliceData, ref settings);
|
||||
additionalLightHasSoftShadows |= shadowLight.light.shadows == LightShadows.Soft;
|
||||
anyShadowSliceRenderer = true;
|
||||
}
|
||||
|
||||
// We share soft shadow settings for main light and additional lights to save keywords.
|
||||
// So we check here if pipeline supports soft shadows and either main light or any additional light has soft shadows
|
||||
// to enable the keyword.
|
||||
// TODO: In PC and Consoles we can upload shadow data per light and branch on shader. That will be more likely way faster.
|
||||
bool mainLightHasSoftShadows = shadowData.supportsMainLightShadows &&
|
||||
lightData.mainLightIndex != -1 &&
|
||||
visibleLights[lightData.mainLightIndex].light.shadows ==
|
||||
LightShadows.Soft;
|
||||
|
||||
bool softShadows = shadowData.supportsSoftShadows &&
|
||||
(mainLightHasSoftShadows || additionalLightHasSoftShadows);
|
||||
|
||||
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.AdditionalLightShadows, anyShadowSliceRenderer);
|
||||
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.SoftShadows, softShadows);
|
||||
|
||||
if (anyShadowSliceRenderer)
|
||||
SetupAdditionalLightsShadowReceiverConstants(cmd, ref shadowData, softShadows);
|
||||
}
|
||||
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
CommandBufferPool.Release(cmd);
|
||||
}
|
||||
|
||||
// Set constant buffer data that will be used during the lighting/shadowing pass
|
||||
void SetupAdditionalLightsShadowReceiverConstants(CommandBuffer cmd, ref ShadowData shadowData, bool softShadows)
|
||||
{
|
||||
float invShadowAtlasWidth = 1.0f / shadowData.additionalLightsShadowmapWidth;
|
||||
float invShadowAtlasHeight = 1.0f / shadowData.additionalLightsShadowmapHeight;
|
||||
float invHalfShadowAtlasWidth = 0.5f * invShadowAtlasWidth;
|
||||
float invHalfShadowAtlasHeight = 0.5f * invShadowAtlasHeight;
|
||||
|
||||
cmd.SetGlobalTexture(m_AdditionalLightsShadowmap.id, m_AdditionalLightsShadowmapTexture);
|
||||
|
||||
// set shadow fade (shadow distance) parameters
|
||||
ShadowUtils.SetupShadowReceiverConstantBuffer(cmd, m_MainLightShadowParams);
|
||||
|
||||
if (m_UseStructuredBuffer)
|
||||
{
|
||||
// per-light data
|
||||
var shadowParamsBuffer = ShaderData.instance.GetAdditionalLightShadowParamsStructuredBuffer(m_AdditionalLightIndexToShadowParams.Length);
|
||||
shadowParamsBuffer.SetData(m_AdditionalLightIndexToShadowParams);
|
||||
cmd.SetGlobalBuffer(m_AdditionalShadowParams_SSBO, shadowParamsBuffer);
|
||||
|
||||
// per-shadow-slice data
|
||||
var shadowSliceMatricesBuffer = ShaderData.instance.GetAdditionalLightShadowSliceMatricesStructuredBuffer(m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix.Length);
|
||||
shadowSliceMatricesBuffer.SetData(m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix);
|
||||
cmd.SetGlobalBuffer(m_AdditionalLightsWorldToShadow_SSBO, shadowSliceMatricesBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd.SetGlobalVectorArray(AdditionalShadowsConstantBuffer._AdditionalShadowParams, m_AdditionalLightIndexToShadowParams); // per-light data
|
||||
cmd.SetGlobalMatrixArray(AdditionalShadowsConstantBuffer._AdditionalLightsWorldToShadow, m_AdditionalLightShadowSliceIndexTo_WorldShadowMatrix); // per-shadow-slice data
|
||||
}
|
||||
|
||||
if (softShadows)
|
||||
{
|
||||
if (m_SupportsBoxFilterForShadows)
|
||||
{
|
||||
cmd.SetGlobalVector(AdditionalShadowsConstantBuffer._AdditionalShadowOffset0,
|
||||
new Vector4(-invHalfShadowAtlasWidth, -invHalfShadowAtlasHeight, 0.0f, 0.0f));
|
||||
cmd.SetGlobalVector(AdditionalShadowsConstantBuffer._AdditionalShadowOffset1,
|
||||
new Vector4(invHalfShadowAtlasWidth, -invHalfShadowAtlasHeight, 0.0f, 0.0f));
|
||||
cmd.SetGlobalVector(AdditionalShadowsConstantBuffer._AdditionalShadowOffset2,
|
||||
new Vector4(-invHalfShadowAtlasWidth, invHalfShadowAtlasHeight, 0.0f, 0.0f));
|
||||
cmd.SetGlobalVector(AdditionalShadowsConstantBuffer._AdditionalShadowOffset3,
|
||||
new Vector4(invHalfShadowAtlasWidth, invHalfShadowAtlasHeight, 0.0f, 0.0f));
|
||||
}
|
||||
|
||||
// Currently only used when !SHADER_API_MOBILE but risky to not set them as it's generic
|
||||
// enough so custom shaders might use it.
|
||||
cmd.SetGlobalVector(AdditionalShadowsConstantBuffer._AdditionalShadowmapSize, new Vector4(invShadowAtlasWidth, invShadowAtlasHeight,
|
||||
shadowData.additionalLightsShadowmapWidth, shadowData.additionalLightsShadowmapHeight));
|
||||
}
|
||||
}
|
||||
|
||||
bool IsValidShadowCastingLight(ref LightData lightData, int i)
|
||||
{
|
||||
if (i == lightData.mainLightIndex)
|
||||
return false;
|
||||
|
||||
VisibleLight shadowLight = lightData.visibleLights[i];
|
||||
|
||||
// Directional and light shadows are not supported in the shadow map atlas
|
||||
if (shadowLight.lightType == LightType.Directional)
|
||||
return false;
|
||||
|
||||
Light light = shadowLight.light;
|
||||
return light != null && light.shadows != LightShadows.None && !Mathf.Approximately(light.shadowStrength, 0.0f);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
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
|
||||
{
|
||||
RenderTargetHandle m_CameraColorHandle;
|
||||
const string m_ProfilerTag = "Capture Pass";
|
||||
private static readonly ProfilingSampler m_ProfilingSampler = new ProfilingSampler(m_ProfilerTag);
|
||||
public CapturePass(RenderPassEvent evt)
|
||||
{
|
||||
base.profilingSampler = new ProfilingSampler(nameof(CapturePass));
|
||||
renderPassEvent = evt;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configure the pass
|
||||
/// </summary>
|
||||
/// <param name="actions"></param>
|
||||
public void Setup(RenderTargetHandle colorHandle)
|
||||
{
|
||||
m_CameraColorHandle = colorHandle;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
CommandBuffer cmdBuf = CommandBufferPool.Get();
|
||||
using (new ProfilingScope(cmdBuf, m_ProfilingSampler))
|
||||
{
|
||||
var colorAttachmentIdentifier = m_CameraColorHandle.Identifier();
|
||||
var captureActions = renderingData.cameraData.captureActions;
|
||||
for (captureActions.Reset(); captureActions.MoveNext();)
|
||||
captureActions.Current(colorAttachmentIdentifier, cmdBuf);
|
||||
}
|
||||
|
||||
context.ExecuteCommandBuffer(cmdBuf);
|
||||
CommandBufferPool.Release(cmdBuf);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,220 @@
|
||||
using UnityEngine.Experimental.Rendering;
|
||||
|
||||
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 class ColorGradingLutPass : ScriptableRenderPass
|
||||
{
|
||||
readonly Material m_LutBuilderLdr;
|
||||
readonly Material m_LutBuilderHdr;
|
||||
readonly GraphicsFormat m_HdrLutFormat;
|
||||
readonly GraphicsFormat m_LdrLutFormat;
|
||||
|
||||
RenderTargetHandle m_InternalLut;
|
||||
|
||||
public ColorGradingLutPass(RenderPassEvent evt, PostProcessData data)
|
||||
{
|
||||
base.profilingSampler = new ProfilingSampler(nameof(ColorGradingLutPass));
|
||||
renderPassEvent = evt;
|
||||
overrideCameraTarget = true;
|
||||
|
||||
Material Load(Shader shader)
|
||||
{
|
||||
if (shader == null)
|
||||
{
|
||||
Debug.LogError($"Missing shader. {GetType().DeclaringType.Name} render pass will not execute. Check for missing reference in the renderer resources.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return CoreUtils.CreateEngineMaterial(shader);
|
||||
}
|
||||
|
||||
m_LutBuilderLdr = Load(data.shaders.lutBuilderLdrPS);
|
||||
m_LutBuilderHdr = Load(data.shaders.lutBuilderHdrPS);
|
||||
|
||||
// Warm up lut format as IsFormatSupported adds GC pressure...
|
||||
const FormatUsage kFlags = FormatUsage.Linear | FormatUsage.Render;
|
||||
if (SystemInfo.IsFormatSupported(GraphicsFormat.R16G16B16A16_SFloat, kFlags))
|
||||
m_HdrLutFormat = GraphicsFormat.R16G16B16A16_SFloat;
|
||||
else if (SystemInfo.IsFormatSupported(GraphicsFormat.B10G11R11_UFloatPack32, kFlags))
|
||||
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;
|
||||
}
|
||||
|
||||
public void Setup(in RenderTargetHandle internalLut)
|
||||
{
|
||||
m_InternalLut = internalLut;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
var cmd = CommandBufferPool.Get();
|
||||
using (new ProfilingScope(cmd, ProfilingSampler.Get(URPProfileId.ColorGradingLUT)))
|
||||
{
|
||||
// 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>();
|
||||
|
||||
ref var postProcessingData = ref renderingData.postProcessingData;
|
||||
bool hdr = postProcessingData.gradingMode == ColorGradingMode.HighDynamicRange;
|
||||
|
||||
// Prepare texture & material
|
||||
int lutHeight = postProcessingData.lutSize;
|
||||
int lutWidth = lutHeight * lutHeight;
|
||||
var format = hdr ? m_HdrLutFormat : m_LdrLutFormat;
|
||||
var material = hdr ? m_LutBuilderHdr : m_LutBuilderLdr;
|
||||
var desc = new RenderTextureDescriptor(lutWidth, lutHeight, format, 0);
|
||||
desc.vrUsage = VRTextureUsage.None; // We only need one for both eyes in VR
|
||||
cmd.GetTemporaryRT(m_InternalLut.id, desc, FilterMode.Bilinear);
|
||||
|
||||
// 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
|
||||
);
|
||||
|
||||
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(ShaderKeywordStrings.TonemapACES); break;
|
||||
default: break; // None
|
||||
}
|
||||
}
|
||||
|
||||
renderingData.cameraData.xr.StopSinglePass(cmd);
|
||||
|
||||
// Render the lut
|
||||
cmd.Blit(null, m_InternalLut.id, material);
|
||||
|
||||
renderingData.cameraData.xr.StartSinglePass(cmd);
|
||||
}
|
||||
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
CommandBufferPool.Release(cmd);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnFinishCameraStackRendering(CommandBuffer cmd)
|
||||
{
|
||||
cmd.ReleaseTemporaryRT(m_InternalLut.id);
|
||||
}
|
||||
|
||||
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,120 @@
|
||||
using System;
|
||||
|
||||
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 class CopyColorPass : ScriptableRenderPass
|
||||
{
|
||||
int m_SampleOffsetShaderHandle;
|
||||
Material m_SamplingMaterial;
|
||||
Downsampling m_DownsamplingMethod;
|
||||
Material m_CopyColorMaterial;
|
||||
|
||||
private RenderTargetIdentifier source { get; set; }
|
||||
private RenderTargetHandle destination { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create the CopyColorPass
|
||||
/// </summary>
|
||||
public CopyColorPass(RenderPassEvent evt, Material samplingMaterial, Material copyColorMaterial = null)
|
||||
{
|
||||
base.profilingSampler = new ProfilingSampler(nameof(CopyColorPass));
|
||||
|
||||
m_SamplingMaterial = samplingMaterial;
|
||||
m_CopyColorMaterial = copyColorMaterial;
|
||||
m_SampleOffsetShaderHandle = Shader.PropertyToID("_SampleOffset");
|
||||
renderPassEvent = evt;
|
||||
m_DownsamplingMethod = Downsampling.None;
|
||||
}
|
||||
|
||||
/// <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(RenderTargetIdentifier source, RenderTargetHandle destination, Downsampling downsampling)
|
||||
{
|
||||
this.source = source;
|
||||
this.destination = destination;
|
||||
m_DownsamplingMethod = downsampling;
|
||||
}
|
||||
|
||||
public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
|
||||
{
|
||||
RenderTextureDescriptor descriptor = renderingData.cameraData.cameraTargetDescriptor;
|
||||
descriptor.msaaSamples = 1;
|
||||
descriptor.depthBufferBits = 0;
|
||||
if (m_DownsamplingMethod == Downsampling._2xBilinear)
|
||||
{
|
||||
descriptor.width /= 2;
|
||||
descriptor.height /= 2;
|
||||
}
|
||||
else if (m_DownsamplingMethod == Downsampling._4xBox || m_DownsamplingMethod == Downsampling._4xBilinear)
|
||||
{
|
||||
descriptor.width /= 4;
|
||||
descriptor.height /= 4;
|
||||
}
|
||||
|
||||
cmd.GetTemporaryRT(destination.id, descriptor, m_DownsamplingMethod == Downsampling.None ? FilterMode.Point : FilterMode.Bilinear);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
if (m_SamplingMaterial == null)
|
||||
{
|
||||
Debug.LogErrorFormat("Missing {0}. {1} render pass will not execute. Check for missing reference in the renderer resources.", m_SamplingMaterial, GetType().Name);
|
||||
return;
|
||||
}
|
||||
|
||||
CommandBuffer cmd = CommandBufferPool.Get();
|
||||
using (new ProfilingScope(cmd, ProfilingSampler.Get(URPProfileId.CopyColor)))
|
||||
{
|
||||
RenderTargetIdentifier opaqueColorRT = destination.Identifier();
|
||||
|
||||
ScriptableRenderer.SetRenderTarget(cmd, opaqueColorRT, BuiltinRenderTextureType.CameraTarget, clearFlag,
|
||||
clearColor);
|
||||
|
||||
bool useDrawProceduleBlit = renderingData.cameraData.xr.enabled;
|
||||
switch (m_DownsamplingMethod)
|
||||
{
|
||||
case Downsampling.None:
|
||||
RenderingUtils.Blit(cmd, source, opaqueColorRT, m_CopyColorMaterial, 0, useDrawProceduleBlit);
|
||||
break;
|
||||
case Downsampling._2xBilinear:
|
||||
RenderingUtils.Blit(cmd, source, opaqueColorRT, m_CopyColorMaterial, 0, useDrawProceduleBlit);
|
||||
break;
|
||||
case Downsampling._4xBox:
|
||||
m_SamplingMaterial.SetFloat(m_SampleOffsetShaderHandle, 2);
|
||||
RenderingUtils.Blit(cmd, source, opaqueColorRT, m_SamplingMaterial, 0, useDrawProceduleBlit);
|
||||
break;
|
||||
case Downsampling._4xBilinear:
|
||||
RenderingUtils.Blit(cmd, source, opaqueColorRT, m_CopyColorMaterial, 0, useDrawProceduleBlit);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
CommandBufferPool.Release(cmd);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnCameraCleanup(CommandBuffer cmd)
|
||||
{
|
||||
if (cmd == null)
|
||||
throw new ArgumentNullException("cmd");
|
||||
|
||||
if (destination != RenderTargetHandle.CameraTarget)
|
||||
{
|
||||
cmd.ReleaseTemporaryRT(destination.id);
|
||||
destination = RenderTargetHandle.CameraTarget;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,157 @@
|
||||
using System;
|
||||
|
||||
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 class CopyDepthPass : ScriptableRenderPass
|
||||
{
|
||||
private RenderTargetHandle source { get; set; }
|
||||
private RenderTargetHandle destination { get; set; }
|
||||
internal bool AllocateRT { get; set; }
|
||||
Material m_CopyDepthMaterial;
|
||||
public CopyDepthPass(RenderPassEvent evt, Material copyDepthMaterial)
|
||||
{
|
||||
base.profilingSampler = new ProfilingSampler(nameof(CopyDepthPass));
|
||||
AllocateRT = true;
|
||||
m_CopyDepthMaterial = copyDepthMaterial;
|
||||
renderPassEvent = evt;
|
||||
}
|
||||
|
||||
/// <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 Targt</param>
|
||||
public void Setup(RenderTargetHandle source, RenderTargetHandle destination)
|
||||
{
|
||||
this.source = source;
|
||||
this.destination = destination;
|
||||
this.AllocateRT = AllocateRT && !destination.HasInternalRenderTargetId();
|
||||
}
|
||||
|
||||
public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
|
||||
{
|
||||
var descriptor = renderingData.cameraData.cameraTargetDescriptor;
|
||||
descriptor.colorFormat = RenderTextureFormat.Depth;
|
||||
descriptor.depthBufferBits = 32; //TODO: do we really need this. double check;
|
||||
descriptor.msaaSamples = 1;
|
||||
if (this.AllocateRT)
|
||||
cmd.GetTemporaryRT(destination.id, descriptor, FilterMode.Point);
|
||||
|
||||
// On Metal iOS, prevent camera attachments to be bound and cleared during this pass.
|
||||
ConfigureTarget(new RenderTargetIdentifier(destination.Identifier(), 0, CubemapFace.Unknown, -1));
|
||||
ConfigureClear(ClearFlag.None, Color.black);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
if (m_CopyDepthMaterial == null)
|
||||
{
|
||||
Debug.LogErrorFormat("Missing {0}. {1} render pass will not execute. Check for missing reference in the renderer resources.", m_CopyDepthMaterial, GetType().Name);
|
||||
return;
|
||||
}
|
||||
CommandBuffer cmd = CommandBufferPool.Get();
|
||||
using (new ProfilingScope(cmd, ProfilingSampler.Get(URPProfileId.CopyDepth)))
|
||||
{
|
||||
RenderTextureDescriptor descriptor = renderingData.cameraData.cameraTargetDescriptor;
|
||||
int cameraSamples = descriptor.msaaSamples;
|
||||
|
||||
CameraData cameraData = renderingData.cameraData;
|
||||
|
||||
switch (cameraSamples)
|
||||
{
|
||||
case 8:
|
||||
cmd.DisableShaderKeyword(ShaderKeywordStrings.DepthMsaa2);
|
||||
cmd.DisableShaderKeyword(ShaderKeywordStrings.DepthMsaa4);
|
||||
cmd.EnableShaderKeyword(ShaderKeywordStrings.DepthMsaa8);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
cmd.DisableShaderKeyword(ShaderKeywordStrings.DepthMsaa2);
|
||||
cmd.EnableShaderKeyword(ShaderKeywordStrings.DepthMsaa4);
|
||||
cmd.DisableShaderKeyword(ShaderKeywordStrings.DepthMsaa8);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
cmd.EnableShaderKeyword(ShaderKeywordStrings.DepthMsaa2);
|
||||
cmd.DisableShaderKeyword(ShaderKeywordStrings.DepthMsaa4);
|
||||
cmd.DisableShaderKeyword(ShaderKeywordStrings.DepthMsaa8);
|
||||
break;
|
||||
|
||||
// MSAA disabled
|
||||
default:
|
||||
cmd.DisableShaderKeyword(ShaderKeywordStrings.DepthMsaa2);
|
||||
cmd.DisableShaderKeyword(ShaderKeywordStrings.DepthMsaa4);
|
||||
cmd.DisableShaderKeyword(ShaderKeywordStrings.DepthMsaa8);
|
||||
break;
|
||||
}
|
||||
|
||||
cmd.SetGlobalTexture("_CameraDepthAttachment", source.Identifier());
|
||||
|
||||
|
||||
#if ENABLE_VR && ENABLE_XR_MODULE
|
||||
// XR uses procedural draw instead of cmd.blit or cmd.DrawFullScreenMesh
|
||||
if (renderingData.cameraData.xr.enabled)
|
||||
{
|
||||
// XR flip logic is not the same as non-XR case because XR uses draw procedure
|
||||
// and draw procedure does not need to take projection matrix yflip into account
|
||||
// We y-flip if
|
||||
// 1) we are bliting from render texture to back buffer and
|
||||
// 2) renderTexture starts UV at top
|
||||
// XRTODO: handle scalebias and scalebiasRt for src and dst separately
|
||||
bool isRenderToBackBufferTarget = destination.Identifier() == cameraData.xr.renderTarget && !cameraData.xr.renderTargetIsRenderTexture;
|
||||
bool yflip = isRenderToBackBufferTarget && SystemInfo.graphicsUVStartsAtTop;
|
||||
float flipSign = (yflip) ? -1.0f : 1.0f;
|
||||
Vector4 scaleBiasRt = (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, scaleBiasRt);
|
||||
|
||||
cmd.DrawProcedural(Matrix4x4.identity, m_CopyDepthMaterial, 0, MeshTopology.Quads, 4);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
// Blit has logic to flip projection matrix when rendering to render texture.
|
||||
// Currently the y-flip is handled in CopyDepthPass.hlsl by checking _ProjectionParams.x
|
||||
// If you replace this Blit with a Draw* that sets projection matrix double check
|
||||
// to also update shader.
|
||||
// scaleBias.x = flipSign
|
||||
// scaleBias.y = scale
|
||||
// scaleBias.z = bias
|
||||
// scaleBias.w = unused
|
||||
float flipSign = (cameraData.IsCameraProjectionMatrixFlipped()) ? -1.0f : 1.0f;
|
||||
Vector4 scaleBiasRt = (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, scaleBiasRt);
|
||||
|
||||
cmd.DrawMesh(RenderingUtils.fullscreenMesh, Matrix4x4.identity, m_CopyDepthMaterial);
|
||||
}
|
||||
}
|
||||
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
CommandBufferPool.Release(cmd);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnCameraCleanup(CommandBuffer cmd)
|
||||
{
|
||||
if (cmd == null)
|
||||
throw new ArgumentNullException("cmd");
|
||||
|
||||
if (this.AllocateRT)
|
||||
cmd.ReleaseTemporaryRT(destination.id);
|
||||
destination = RenderTargetHandle.CameraTarget;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
using UnityEngine.Experimental.GlobalIllumination;
|
||||
using UnityEngine.Profiling;
|
||||
using Unity.Collections;
|
||||
|
||||
// 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)
|
||||
{
|
||||
base.profilingSampler = new ProfilingSampler(nameof(DeferredPass));
|
||||
base.renderPassEvent = evt;
|
||||
m_DeferredLights = deferredLights;
|
||||
}
|
||||
|
||||
// ScriptableRenderPass
|
||||
public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescripor)
|
||||
{
|
||||
RenderTargetIdentifier lightingAttachmentId = m_DeferredLights.GbufferAttachmentIdentifiers[m_DeferredLights.GBufferLightingIndex];
|
||||
RenderTargetIdentifier depthAttachmentId = m_DeferredLights.DepthAttachmentIdentifier;
|
||||
|
||||
// TODO: Cannot currently bind depth texture as read-only!
|
||||
ConfigureTarget(lightingAttachmentId, depthAttachmentId);
|
||||
}
|
||||
|
||||
// ScriptableRenderPass
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
m_DeferredLights.ExecuteDeferredPass(context, ref renderingData);
|
||||
}
|
||||
|
||||
// ScriptableRenderPass
|
||||
public override void OnCameraCleanup(CommandBuffer cmd)
|
||||
{
|
||||
m_DeferredLights.OnCameraCleanup(cmd);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,99 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEngine.Rendering.Universal.Internal
|
||||
{
|
||||
public class DepthNormalOnlyPass : ScriptableRenderPass
|
||||
{
|
||||
internal RenderTextureDescriptor normalDescriptor { get; private set; }
|
||||
internal RenderTextureDescriptor depthDescriptor { get; private set; }
|
||||
|
||||
private RenderTargetHandle depthHandle { get; set; }
|
||||
private RenderTargetHandle normalHandle { get; set; }
|
||||
private ShaderTagId m_ShaderTagId = new ShaderTagId("DepthNormals");
|
||||
private FilteringSettings m_FilteringSettings;
|
||||
|
||||
// Constants
|
||||
private const int k_DepthBufferBits = 32;
|
||||
|
||||
/// <summary>
|
||||
/// Create the DepthNormalOnlyPass
|
||||
/// </summary>
|
||||
public DepthNormalOnlyPass(RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask)
|
||||
{
|
||||
base.profilingSampler = new ProfilingSampler(nameof(DepthNormalOnlyPass));
|
||||
m_FilteringSettings = new FilteringSettings(renderQueueRange, layerMask);
|
||||
renderPassEvent = evt;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configure the pass
|
||||
/// </summary>
|
||||
public void Setup(RenderTextureDescriptor baseDescriptor, RenderTargetHandle depthHandle, RenderTargetHandle normalHandle)
|
||||
{
|
||||
this.depthHandle = depthHandle;
|
||||
baseDescriptor.colorFormat = RenderTextureFormat.Depth;
|
||||
baseDescriptor.depthBufferBits = k_DepthBufferBits;
|
||||
baseDescriptor.msaaSamples = 1;// Depth-Only pass don't use MSAA
|
||||
depthDescriptor = baseDescriptor;
|
||||
|
||||
this.normalHandle = normalHandle;
|
||||
baseDescriptor.colorFormat = RenderTextureFormat.RGHalf;
|
||||
baseDescriptor.depthBufferBits = 0;
|
||||
baseDescriptor.msaaSamples = 1;
|
||||
normalDescriptor = baseDescriptor;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
|
||||
{
|
||||
cmd.GetTemporaryRT(normalHandle.id, normalDescriptor, FilterMode.Point);
|
||||
cmd.GetTemporaryRT(depthHandle.id, depthDescriptor, FilterMode.Point);
|
||||
ConfigureTarget(
|
||||
new RenderTargetIdentifier(normalHandle.Identifier(), 0, CubemapFace.Unknown, -1),
|
||||
new RenderTargetIdentifier(depthHandle.Identifier(), 0, CubemapFace.Unknown, -1)
|
||||
);
|
||||
ConfigureClear(ClearFlag.All, Color.black);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
// NOTE: Do NOT mix ProfilingScope with named CommandBuffers i.e. CommandBufferPool.Get("name").
|
||||
// Currently there's an issue which results in mismatched markers.
|
||||
CommandBuffer cmd = CommandBufferPool.Get();
|
||||
using (new ProfilingScope(cmd, ProfilingSampler.Get(URPProfileId.DepthNormalPrepass)))
|
||||
{
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
cmd.Clear();
|
||||
|
||||
var sortFlags = renderingData.cameraData.defaultOpaqueSortFlags;
|
||||
var drawSettings = CreateDrawingSettings(m_ShaderTagId, ref renderingData, sortFlags);
|
||||
drawSettings.perObjectData = PerObjectData.None;
|
||||
|
||||
ref CameraData cameraData = ref renderingData.cameraData;
|
||||
Camera camera = cameraData.camera;
|
||||
|
||||
context.DrawRenderers(renderingData.cullResults, ref drawSettings, ref m_FilteringSettings);
|
||||
}
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
CommandBufferPool.Release(cmd);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnCameraCleanup(CommandBuffer cmd)
|
||||
{
|
||||
if (cmd == null)
|
||||
{
|
||||
throw new ArgumentNullException("cmd");
|
||||
}
|
||||
|
||||
if (depthHandle != RenderTargetHandle.CameraTarget)
|
||||
{
|
||||
cmd.ReleaseTemporaryRT(normalHandle.id);
|
||||
cmd.ReleaseTemporaryRT(depthHandle.id);
|
||||
normalHandle = RenderTargetHandle.CameraTarget;
|
||||
depthHandle = RenderTargetHandle.CameraTarget;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,88 @@
|
||||
using System;
|
||||
|
||||
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 class DepthOnlyPass : ScriptableRenderPass
|
||||
{
|
||||
int kDepthBufferBits = 32;
|
||||
|
||||
private RenderTargetHandle depthAttachmentHandle { get; set; }
|
||||
internal RenderTextureDescriptor descriptor { get; private set; }
|
||||
|
||||
FilteringSettings m_FilteringSettings;
|
||||
ShaderTagId m_ShaderTagId = new ShaderTagId("DepthOnly");
|
||||
|
||||
/// <summary>
|
||||
/// Create the DepthOnlyPass
|
||||
/// </summary>
|
||||
public DepthOnlyPass(RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask)
|
||||
{
|
||||
base.profilingSampler = new ProfilingSampler(nameof(DepthOnlyPass));
|
||||
m_FilteringSettings = new FilteringSettings(renderQueueRange, layerMask);
|
||||
renderPassEvent = evt;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configure the pass
|
||||
/// </summary>
|
||||
public void Setup(
|
||||
RenderTextureDescriptor baseDescriptor,
|
||||
RenderTargetHandle depthAttachmentHandle)
|
||||
{
|
||||
this.depthAttachmentHandle = depthAttachmentHandle;
|
||||
baseDescriptor.colorFormat = RenderTextureFormat.Depth;
|
||||
baseDescriptor.depthBufferBits = kDepthBufferBits;
|
||||
|
||||
// Depth-Only pass don't use MSAA
|
||||
baseDescriptor.msaaSamples = 1;
|
||||
descriptor = baseDescriptor;
|
||||
}
|
||||
|
||||
public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
|
||||
{
|
||||
cmd.GetTemporaryRT(depthAttachmentHandle.id, descriptor, FilterMode.Point);
|
||||
ConfigureTarget(new RenderTargetIdentifier(depthAttachmentHandle.Identifier(), 0, CubemapFace.Unknown, -1));
|
||||
ConfigureClear(ClearFlag.All, Color.black);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
// NOTE: Do NOT mix ProfilingScope with named CommandBuffers i.e. CommandBufferPool.Get("name").
|
||||
// Currently there's an issue which results in mismatched markers.
|
||||
CommandBuffer cmd = CommandBufferPool.Get();
|
||||
using (new ProfilingScope(cmd, ProfilingSampler.Get(URPProfileId.DepthPrepass)))
|
||||
{
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
cmd.Clear();
|
||||
|
||||
var sortFlags = renderingData.cameraData.defaultOpaqueSortFlags;
|
||||
var drawSettings = CreateDrawingSettings(m_ShaderTagId, ref renderingData, sortFlags);
|
||||
drawSettings.perObjectData = PerObjectData.None;
|
||||
|
||||
context.DrawRenderers(renderingData.cullResults, ref drawSettings, ref m_FilteringSettings);
|
||||
}
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
CommandBufferPool.Release(cmd);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnCameraCleanup(CommandBuffer cmd)
|
||||
{
|
||||
if (cmd == null)
|
||||
throw new ArgumentNullException("cmd");
|
||||
|
||||
if (depthAttachmentHandle != RenderTargetHandle.CameraTarget)
|
||||
{
|
||||
cmd.ReleaseTemporaryRT(depthAttachmentHandle.id);
|
||||
depthAttachmentHandle = RenderTargetHandle.CameraTarget;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.Profiling;
|
||||
|
||||
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 class DrawObjectsPass : ScriptableRenderPass
|
||||
{
|
||||
FilteringSettings m_FilteringSettings;
|
||||
RenderStateBlock m_RenderStateBlock;
|
||||
List<ShaderTagId> m_ShaderTagIdList = new List<ShaderTagId>();
|
||||
string m_ProfilerTag;
|
||||
ProfilingSampler m_ProfilingSampler;
|
||||
bool m_IsOpaque;
|
||||
|
||||
static readonly int s_DrawObjectPassDataPropID = Shader.PropertyToID("_DrawObjectPassData");
|
||||
|
||||
public DrawObjectsPass(string profilerTag, ShaderTagId[] shaderTagIds, bool opaque, RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask, StencilState stencilState, int stencilReference)
|
||||
{
|
||||
base.profilingSampler = new ProfilingSampler(nameof(DrawObjectsPass));
|
||||
|
||||
m_ProfilerTag = profilerTag;
|
||||
m_ProfilingSampler = new ProfilingSampler(profilerTag);
|
||||
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;
|
||||
|
||||
if (stencilState.enabled)
|
||||
{
|
||||
m_RenderStateBlock.stencilReference = stencilReference;
|
||||
m_RenderStateBlock.mask = RenderStateMask.Stencil;
|
||||
m_RenderStateBlock.stencilState = stencilState;
|
||||
}
|
||||
}
|
||||
|
||||
public DrawObjectsPass(string profilerTag, bool opaque, RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask, StencilState stencilState, int stencilReference)
|
||||
: this(profilerTag,
|
||||
new ShaderTagId[] { new ShaderTagId("SRPDefaultUnlit"), new ShaderTagId("UniversalForward"), new ShaderTagId("UniversalForwardOnly"), new ShaderTagId("LightweightForward")},
|
||||
opaque, evt, renderQueueRange, layerMask, stencilState, stencilReference)
|
||||
{}
|
||||
|
||||
internal DrawObjectsPass(URPProfileId profileId, bool opaque, RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask, StencilState stencilState, int stencilReference)
|
||||
: this(profileId.GetType().Name, opaque, evt, renderQueueRange, layerMask, stencilState, stencilReference)
|
||||
{
|
||||
m_ProfilingSampler = ProfilingSampler.Get(profileId);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
// NOTE: Do NOT mix ProfilingScope with named CommandBuffers i.e. CommandBufferPool.Get("name").
|
||||
// Currently there's an issue which results in mismatched markers.
|
||||
CommandBuffer cmd = CommandBufferPool.Get();
|
||||
using (new ProfilingScope(cmd, m_ProfilingSampler))
|
||||
{
|
||||
// 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, (m_IsOpaque) ? 1.0f : 0.0f);
|
||||
cmd.SetGlobalVector(s_DrawObjectPassDataPropID, drawObjectPassData);
|
||||
|
||||
// scaleBias.x = flipSign
|
||||
// scaleBias.y = scale
|
||||
// scaleBias.z = bias
|
||||
// scaleBias.w = unused
|
||||
float flipSign = (renderingData.cameraData.IsCameraProjectionMatrixFlipped()) ? -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);
|
||||
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
cmd.Clear();
|
||||
|
||||
Camera camera = renderingData.cameraData.camera;
|
||||
var sortFlags = (m_IsOpaque) ? renderingData.cameraData.defaultOpaqueSortFlags : SortingCriteria.CommonTransparent;
|
||||
var drawSettings = CreateDrawingSettings(m_ShaderTagIdList, ref renderingData, sortFlags);
|
||||
var filterSettings = m_FilteringSettings;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
// When rendering the preview camera, we want the layer mask to be forced to Everything
|
||||
if (renderingData.cameraData.isPreviewCamera)
|
||||
{
|
||||
filterSettings.layerMask = -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
context.DrawRenderers(renderingData.cullResults, ref drawSettings, ref filterSettings, ref m_RenderStateBlock);
|
||||
|
||||
// Render objects that did not match any shader pass with error shader
|
||||
RenderingUtils.RenderObjectsWithError(context, ref renderingData.cullResults, camera, filterSettings, SortingCriteria.None);
|
||||
}
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
CommandBufferPool.Release(cmd);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,75 @@
|
||||
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 class DrawSkyboxPass : ScriptableRenderPass
|
||||
{
|
||||
public DrawSkyboxPass(RenderPassEvent evt)
|
||||
{
|
||||
base.profilingSampler = new ProfilingSampler(nameof(DrawSkyboxPass));
|
||||
|
||||
renderPassEvent = evt;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
#if ENABLE_VR && ENABLE_XR_MODULE
|
||||
// XRTODO: Remove this code once Skybox pass is moved to SRP land.
|
||||
if (renderingData.cameraData.xr.enabled)
|
||||
{
|
||||
// Setup Legacy XR buffer states
|
||||
if (renderingData.cameraData.xr.singlePassEnabled)
|
||||
{
|
||||
// Setup legacy skybox stereo buffer
|
||||
renderingData.cameraData.camera.SetStereoProjectionMatrix(Camera.StereoscopicEye.Left, renderingData.cameraData.GetProjectionMatrix(0));
|
||||
renderingData.cameraData.camera.SetStereoViewMatrix(Camera.StereoscopicEye.Left, renderingData.cameraData.GetViewMatrix(0));
|
||||
renderingData.cameraData.camera.SetStereoProjectionMatrix(Camera.StereoscopicEye.Right, renderingData.cameraData.GetProjectionMatrix(1));
|
||||
renderingData.cameraData.camera.SetStereoViewMatrix(Camera.StereoscopicEye.Right, renderingData.cameraData.GetViewMatrix(1));
|
||||
|
||||
CommandBuffer cmd = CommandBufferPool.Get();
|
||||
|
||||
// Use legacy stereo instancing mode to have legacy XR code path configured
|
||||
cmd.SetSinglePassStereo(SystemInfo.supportsMultiview ? SinglePassStereoMode.Multiview : SinglePassStereoMode.Instancing);
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
cmd.Clear();
|
||||
|
||||
// Calling into built-in skybox pass
|
||||
context.DrawSkybox(renderingData.cameraData.camera);
|
||||
|
||||
// Disable Legacy XR path
|
||||
cmd.SetSinglePassStereo(SinglePassStereoMode.None);
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
// We do not need to submit here due to special handling of stereo matricies in core.
|
||||
// context.Submit();
|
||||
CommandBufferPool.Release(cmd);
|
||||
|
||||
renderingData.cameraData.camera.ResetStereoProjectionMatrices();
|
||||
renderingData.cameraData.camera.ResetStereoViewMatrices();
|
||||
}
|
||||
else
|
||||
{
|
||||
renderingData.cameraData.camera.projectionMatrix = renderingData.cameraData.GetProjectionMatrix(0);
|
||||
renderingData.cameraData.camera.worldToCameraMatrix = renderingData.cameraData.GetViewMatrix(0);
|
||||
|
||||
context.DrawSkybox(renderingData.cameraData.camera);
|
||||
|
||||
// XRTODO: remove this call because it creates issues with nested profiling scopes
|
||||
// See examples in UniversalRenderPipeline.RenderSingleCamera() and in ScriptableRenderer.Execute()
|
||||
context.Submit(); // Submit and execute the skybox pass before resetting the matrices
|
||||
|
||||
renderingData.cameraData.camera.ResetProjectionMatrix();
|
||||
renderingData.cameraData.camera.ResetWorldToCameraMatrix();
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
context.DrawSkybox(renderingData.cameraData.camera);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,117 @@
|
||||
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 class FinalBlitPass : ScriptableRenderPass
|
||||
{
|
||||
RenderTargetHandle m_Source;
|
||||
Material m_BlitMaterial;
|
||||
|
||||
public FinalBlitPass(RenderPassEvent evt, Material blitMaterial)
|
||||
{
|
||||
base.profilingSampler = new ProfilingSampler(nameof(FinalBlitPass));
|
||||
|
||||
m_BlitMaterial = blitMaterial;
|
||||
renderPassEvent = evt;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configure the pass
|
||||
/// </summary>
|
||||
/// <param name="baseDescriptor"></param>
|
||||
/// <param name="colorHandle"></param>
|
||||
public void Setup(RenderTextureDescriptor baseDescriptor, RenderTargetHandle colorHandle)
|
||||
{
|
||||
m_Source = colorHandle;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
if (m_BlitMaterial == null)
|
||||
{
|
||||
Debug.LogErrorFormat("Missing {0}. {1} render pass will not execute. Check for missing reference in the renderer resources.", m_BlitMaterial, GetType().Name);
|
||||
return;
|
||||
}
|
||||
|
||||
// Note: We need to get the cameraData.targetTexture as this will get the targetTexture of the camera stack.
|
||||
// Overlay cameras need to output to the target described in the base camera while doing camera stack.
|
||||
ref CameraData cameraData = ref renderingData.cameraData;
|
||||
RenderTargetIdentifier cameraTarget = (cameraData.targetTexture != null) ? new RenderTargetIdentifier(cameraData.targetTexture) : BuiltinRenderTextureType.CameraTarget;
|
||||
|
||||
bool isSceneViewCamera = cameraData.isSceneViewCamera;
|
||||
CommandBuffer cmd = CommandBufferPool.Get();
|
||||
using (new ProfilingScope(cmd, ProfilingSampler.Get(URPProfileId.FinalBlit)))
|
||||
{
|
||||
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.LinearToSRGBConversion,
|
||||
cameraData.requireSrgbConversion);
|
||||
|
||||
cmd.SetGlobalTexture(ShaderPropertyId.sourceTex, m_Source.Identifier());
|
||||
|
||||
#if ENABLE_VR && ENABLE_XR_MODULE
|
||||
if (cameraData.xr.enabled)
|
||||
{
|
||||
int depthSlice = cameraData.xr.singlePassEnabled ? -1 : cameraData.xr.GetTextureArraySlice();
|
||||
cameraTarget =
|
||||
new RenderTargetIdentifier(cameraData.xr.renderTarget, 0, CubemapFace.Unknown, depthSlice);
|
||||
|
||||
CoreUtils.SetRenderTarget(
|
||||
cmd,
|
||||
cameraTarget,
|
||||
RenderBufferLoadAction.Load,
|
||||
RenderBufferStoreAction.Store,
|
||||
ClearFlag.None,
|
||||
Color.black);
|
||||
|
||||
cmd.SetViewport(cameraData.pixelRect);
|
||||
|
||||
// We y-flip if
|
||||
// 1) we are bliting from render texture to back buffer(UV starts at bottom) and
|
||||
// 2) renderTexture starts UV at top
|
||||
bool yflip = !cameraData.xr.renderTargetIsRenderTexture && SystemInfo.graphicsUVStartsAtTop;
|
||||
Vector4 scaleBias = yflip ? new Vector4(1, -1, 0, 1) : new Vector4(1, 1, 0, 0);
|
||||
cmd.SetGlobalVector(ShaderPropertyId.scaleBias, scaleBias);
|
||||
|
||||
cmd.DrawProcedural(Matrix4x4.identity, m_BlitMaterial, 0, MeshTopology.Quads, 4);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (isSceneViewCamera || cameraData.isDefaultViewport)
|
||||
{
|
||||
// This set render target is necessary so we change the LOAD state to DontCare.
|
||||
cmd.SetRenderTarget(BuiltinRenderTextureType.CameraTarget,
|
||||
RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store, // color
|
||||
RenderBufferLoadAction.DontCare, RenderBufferStoreAction.DontCare); // depth
|
||||
cmd.Blit(m_Source.Identifier(), cameraTarget, m_BlitMaterial);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Final blit pass should always blit to backbuffer. The first time we do we don't need to Load contents to tile.
|
||||
// We need to keep in the pipeline of first render pass to each render target to properly set load/store actions.
|
||||
// meanwhile we set to load so split screen case works.
|
||||
CoreUtils.SetRenderTarget(
|
||||
cmd,
|
||||
cameraTarget,
|
||||
RenderBufferLoadAction.Load,
|
||||
RenderBufferStoreAction.Store,
|
||||
ClearFlag.None,
|
||||
Color.black);
|
||||
|
||||
Camera camera = cameraData.camera;
|
||||
cmd.SetViewProjectionMatrices(Matrix4x4.identity, Matrix4x4.identity);
|
||||
cmd.SetViewport(cameraData.pixelRect);
|
||||
cmd.DrawMesh(RenderingUtils.fullscreenMesh, Matrix4x4.identity, m_BlitMaterial);
|
||||
cmd.SetViewProjectionMatrices(camera.worldToCameraMatrix, camera.projectionMatrix);
|
||||
}
|
||||
}
|
||||
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
CommandBufferPool.Release(cmd);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,119 @@
|
||||
using UnityEngine.Experimental.GlobalIllumination;
|
||||
using UnityEngine.Experimental.Rendering;
|
||||
using UnityEngine.Profiling;
|
||||
using Unity.Collections;
|
||||
|
||||
namespace UnityEngine.Rendering.Universal.Internal
|
||||
{
|
||||
// Render all tiled-based deferred lights.
|
||||
internal class GBufferPass : ScriptableRenderPass
|
||||
{
|
||||
static ShaderTagId s_ShaderTagLit = new ShaderTagId("Lit");
|
||||
static ShaderTagId s_ShaderTagSimpleLit = new ShaderTagId("SimpleLit");
|
||||
static ShaderTagId s_ShaderTagUnlit = new ShaderTagId("Unlit");
|
||||
static ShaderTagId s_ShaderTagUniversalGBuffer = new ShaderTagId("UniversalGBuffer");
|
||||
static ShaderTagId s_ShaderTagUniversalMaterialType = new ShaderTagId("UniversalMaterialType");
|
||||
|
||||
ProfilingSampler m_ProfilingSampler = new ProfilingSampler("Render GBuffer");
|
||||
|
||||
DeferredLights m_DeferredLights;
|
||||
|
||||
ShaderTagId[] m_ShaderTagValues;
|
||||
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(nameof(GBufferPass));
|
||||
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_ShaderTagValues = new ShaderTagId[4];
|
||||
m_ShaderTagValues[0] = s_ShaderTagLit;
|
||||
m_ShaderTagValues[1] = s_ShaderTagSimpleLit;
|
||||
m_ShaderTagValues[2] = s_ShaderTagUnlit;
|
||||
m_ShaderTagValues[3] = new ShaderTagId(); // Special catch all case for materials where UniversalMaterialType is not defined or the tag value doesn't match anything we know.
|
||||
|
||||
m_RenderStateBlocks = new RenderStateBlock[4];
|
||||
m_RenderStateBlocks[0] = DeferredLights.OverwriteStencil(m_RenderStateBlock, (int)StencilUsage.MaterialMask, (int)StencilUsage.MaterialLit);
|
||||
m_RenderStateBlocks[1] = DeferredLights.OverwriteStencil(m_RenderStateBlock, (int)StencilUsage.MaterialMask, (int)StencilUsage.MaterialSimpleLit);
|
||||
m_RenderStateBlocks[2] = DeferredLights.OverwriteStencil(m_RenderStateBlock, (int)StencilUsage.MaterialMask, (int)StencilUsage.MaterialUnlit);
|
||||
m_RenderStateBlocks[3] = m_RenderStateBlocks[0];
|
||||
}
|
||||
|
||||
public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
|
||||
{
|
||||
RenderTargetHandle[] gbufferAttachments = m_DeferredLights.GbufferAttachments;
|
||||
|
||||
// Create and declare the render targets used in the pass
|
||||
for (int i = 0; i < gbufferAttachments.Length; ++i)
|
||||
{
|
||||
// Lighting buffer has already been declared with line ConfigureCameraTarget(m_ActiveCameraColorAttachment.Identifier(), ...) in DeferredRenderer.Setup
|
||||
if (i != m_DeferredLights.GBufferLightingIndex)
|
||||
{
|
||||
RenderTextureDescriptor gbufferSlice = cameraTextureDescriptor;
|
||||
gbufferSlice.depthBufferBits = 0; // make sure no depth surface is actually created
|
||||
gbufferSlice.stencilFormat = GraphicsFormat.None;
|
||||
gbufferSlice.graphicsFormat = m_DeferredLights.GetGBufferFormat(i);
|
||||
cmd.GetTemporaryRT(m_DeferredLights.GbufferAttachments[i].id, gbufferSlice);
|
||||
}
|
||||
}
|
||||
|
||||
ConfigureTarget(m_DeferredLights.GbufferAttachmentIdentifiers, m_DeferredLights.DepthAttachmentIdentifier);
|
||||
// We must explicitely specify we don't want any clear to avoid unwanted side-effects.
|
||||
// ScriptableRenderer may still implicitely force a clear the first time the camera color/depth targets are bound.
|
||||
ConfigureClear(ClearFlag.None, Color.black);
|
||||
}
|
||||
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
CommandBuffer gbufferCommands = CommandBufferPool.Get();
|
||||
using (new ProfilingScope(gbufferCommands, m_ProfilingSampler))
|
||||
{
|
||||
// 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 isin 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.
|
||||
if (m_DeferredLights.IsOverlay)
|
||||
m_DeferredLights.ClearStencilPartial(gbufferCommands);
|
||||
|
||||
context.ExecuteCommandBuffer(gbufferCommands);
|
||||
gbufferCommands.Clear();
|
||||
|
||||
ref CameraData cameraData = ref renderingData.cameraData;
|
||||
Camera camera = cameraData.camera;
|
||||
ShaderTagId lightModeTag = s_ShaderTagUniversalGBuffer;
|
||||
DrawingSettings drawingSettings = CreateDrawingSettings(lightModeTag, ref renderingData, renderingData.cameraData.defaultOpaqueSortFlags);
|
||||
ShaderTagId universalMaterialTypeTag = s_ShaderTagUniversalMaterialType;
|
||||
|
||||
NativeArray<ShaderTagId> tagValues = new NativeArray<ShaderTagId>(m_ShaderTagValues, Allocator.Temp);
|
||||
NativeArray<RenderStateBlock> stateBlocks = new NativeArray<RenderStateBlock>(m_RenderStateBlocks, Allocator.Temp);
|
||||
context.DrawRenderers(renderingData.cullResults, ref drawingSettings, ref m_FilteringSettings, universalMaterialTypeTag, false, tagValues, stateBlocks);
|
||||
tagValues.Dispose();
|
||||
stateBlocks.Dispose();
|
||||
|
||||
// Render objects that did not match any shader pass with error shader
|
||||
RenderingUtils.RenderObjectsWithError(context, ref renderingData.cullResults, camera, m_FilteringSettings, SortingCriteria.None);
|
||||
}
|
||||
context.ExecuteCommandBuffer(gbufferCommands);
|
||||
CommandBufferPool.Release(gbufferCommands);
|
||||
}
|
||||
|
||||
public override void OnCameraCleanup(CommandBuffer cmd)
|
||||
{
|
||||
RenderTargetHandle[] gbufferAttachments = m_DeferredLights.GbufferAttachments;
|
||||
|
||||
for (int i = 0; i < gbufferAttachments.Length; ++i)
|
||||
if (i != m_DeferredLights.GBufferLightingIndex)
|
||||
cmd.ReleaseTemporaryRT(gbufferAttachments[i].id);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
namespace UnityEngine.Rendering.Universal
|
||||
{
|
||||
/// <summary>
|
||||
/// Invokes OnRenderObject callback
|
||||
/// </summary>
|
||||
|
||||
internal class InvokeOnRenderObjectCallbackPass : ScriptableRenderPass
|
||||
{
|
||||
public InvokeOnRenderObjectCallbackPass(RenderPassEvent evt)
|
||||
{
|
||||
base.profilingSampler = new ProfilingSampler(nameof(InvokeOnRenderObjectCallbackPass));
|
||||
renderPassEvent = evt;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
context.InvokeOnRenderObjectCallback();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,262 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEngine.Rendering.Universal.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// Renders a shadow map for the main Light.
|
||||
/// </summary>
|
||||
public class MainLightShadowCasterPass : ScriptableRenderPass
|
||||
{
|
||||
private static class MainLightShadowConstantBuffer
|
||||
{
|
||||
public static int _WorldToShadow;
|
||||
public static int _CascadeShadowSplitSpheres0;
|
||||
public static int _CascadeShadowSplitSpheres1;
|
||||
public static int _CascadeShadowSplitSpheres2;
|
||||
public static int _CascadeShadowSplitSpheres3;
|
||||
public static int _CascadeShadowSplitSphereRadii;
|
||||
public static int _ShadowOffset0;
|
||||
public static int _ShadowOffset1;
|
||||
public static int _ShadowOffset2;
|
||||
public static int _ShadowOffset3;
|
||||
public static int _ShadowmapSize;
|
||||
}
|
||||
|
||||
const int k_MaxCascades = 4;
|
||||
const int k_ShadowmapBufferBits = 16;
|
||||
Vector4 m_MainLightShadowParams;
|
||||
int m_ShadowmapWidth;
|
||||
int m_ShadowmapHeight;
|
||||
int m_ShadowCasterCascadesCount;
|
||||
bool m_SupportsBoxFilterForShadows;
|
||||
|
||||
RenderTargetHandle m_MainLightShadowmap;
|
||||
RenderTexture m_MainLightShadowmapTexture;
|
||||
|
||||
Matrix4x4[] m_MainLightShadowMatrices;
|
||||
ShadowSliceData[] m_CascadeSlices;
|
||||
Vector4[] m_CascadeSplitDistances;
|
||||
|
||||
ProfilingSampler m_ProfilingSetupSampler = new ProfilingSampler("Setup Main Shadowmap");
|
||||
|
||||
public MainLightShadowCasterPass(RenderPassEvent evt)
|
||||
{
|
||||
base.profilingSampler = new ProfilingSampler(nameof(MainLightShadowCasterPass));
|
||||
renderPassEvent = evt;
|
||||
|
||||
m_MainLightShadowMatrices = new Matrix4x4[k_MaxCascades + 1];
|
||||
m_CascadeSlices = new ShadowSliceData[k_MaxCascades];
|
||||
m_CascadeSplitDistances = new Vector4[k_MaxCascades];
|
||||
|
||||
MainLightShadowConstantBuffer._WorldToShadow = Shader.PropertyToID("_MainLightWorldToShadow");
|
||||
MainLightShadowConstantBuffer._CascadeShadowSplitSpheres0 = Shader.PropertyToID("_CascadeShadowSplitSpheres0");
|
||||
MainLightShadowConstantBuffer._CascadeShadowSplitSpheres1 = Shader.PropertyToID("_CascadeShadowSplitSpheres1");
|
||||
MainLightShadowConstantBuffer._CascadeShadowSplitSpheres2 = Shader.PropertyToID("_CascadeShadowSplitSpheres2");
|
||||
MainLightShadowConstantBuffer._CascadeShadowSplitSpheres3 = Shader.PropertyToID("_CascadeShadowSplitSpheres3");
|
||||
MainLightShadowConstantBuffer._CascadeShadowSplitSphereRadii = Shader.PropertyToID("_CascadeShadowSplitSphereRadii");
|
||||
MainLightShadowConstantBuffer._ShadowOffset0 = Shader.PropertyToID("_MainLightShadowOffset0");
|
||||
MainLightShadowConstantBuffer._ShadowOffset1 = Shader.PropertyToID("_MainLightShadowOffset1");
|
||||
MainLightShadowConstantBuffer._ShadowOffset2 = Shader.PropertyToID("_MainLightShadowOffset2");
|
||||
MainLightShadowConstantBuffer._ShadowOffset3 = Shader.PropertyToID("_MainLightShadowOffset3");
|
||||
MainLightShadowConstantBuffer._ShadowmapSize = Shader.PropertyToID("_MainLightShadowmapSize");
|
||||
|
||||
m_MainLightShadowmap.Init("_MainLightShadowmapTexture");
|
||||
m_SupportsBoxFilterForShadows = Application.isMobilePlatform || SystemInfo.graphicsDeviceType == GraphicsDeviceType.Switch;
|
||||
}
|
||||
|
||||
public bool Setup(ref RenderingData renderingData)
|
||||
{
|
||||
using var profScope = new ProfilingScope(null, m_ProfilingSetupSampler);
|
||||
|
||||
if (!renderingData.shadowData.supportsMainLightShadows)
|
||||
return false;
|
||||
|
||||
Clear();
|
||||
int shadowLightIndex = renderingData.lightData.mainLightIndex;
|
||||
if (shadowLightIndex == -1)
|
||||
return false;
|
||||
|
||||
VisibleLight shadowLight = renderingData.lightData.visibleLights[shadowLightIndex];
|
||||
Light light = shadowLight.light;
|
||||
if (light.shadows == LightShadows.None)
|
||||
return false;
|
||||
|
||||
if (shadowLight.lightType != LightType.Directional)
|
||||
{
|
||||
Debug.LogWarning("Only directional lights are supported as main light.");
|
||||
}
|
||||
|
||||
Bounds bounds;
|
||||
if (!renderingData.cullResults.GetShadowCasterBounds(shadowLightIndex, out bounds))
|
||||
return false;
|
||||
|
||||
m_ShadowCasterCascadesCount = renderingData.shadowData.mainLightShadowCascadesCount;
|
||||
|
||||
int shadowResolution = ShadowUtils.GetMaxTileResolutionInAtlas(renderingData.shadowData.mainLightShadowmapWidth,
|
||||
renderingData.shadowData.mainLightShadowmapHeight, m_ShadowCasterCascadesCount);
|
||||
m_ShadowmapWidth = renderingData.shadowData.mainLightShadowmapWidth;
|
||||
m_ShadowmapHeight = (m_ShadowCasterCascadesCount == 2) ?
|
||||
renderingData.shadowData.mainLightShadowmapHeight >> 1 :
|
||||
renderingData.shadowData.mainLightShadowmapHeight;
|
||||
|
||||
for (int cascadeIndex = 0; cascadeIndex < m_ShadowCasterCascadesCount; ++cascadeIndex)
|
||||
{
|
||||
bool success = ShadowUtils.ExtractDirectionalLightMatrix(ref renderingData.cullResults, ref renderingData.shadowData,
|
||||
shadowLightIndex, cascadeIndex, m_ShadowmapWidth, m_ShadowmapHeight, shadowResolution, light.shadowNearPlane,
|
||||
out m_CascadeSplitDistances[cascadeIndex], out m_CascadeSlices[cascadeIndex]);
|
||||
|
||||
if (!success)
|
||||
return false;
|
||||
}
|
||||
|
||||
m_MainLightShadowParams = ShadowUtils.GetMainLightShadowParams(ref renderingData);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
|
||||
{
|
||||
m_MainLightShadowmapTexture = ShadowUtils.GetTemporaryShadowTexture(m_ShadowmapWidth,
|
||||
m_ShadowmapHeight, k_ShadowmapBufferBits);
|
||||
ConfigureTarget(new RenderTargetIdentifier(m_MainLightShadowmapTexture));
|
||||
ConfigureClear(ClearFlag.All, Color.black);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
RenderMainLightCascadeShadowmap(ref context, ref renderingData.cullResults, ref renderingData.lightData, ref renderingData.shadowData);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnCameraCleanup(CommandBuffer cmd)
|
||||
{
|
||||
if (cmd == null)
|
||||
throw new ArgumentNullException("cmd");
|
||||
|
||||
if (m_MainLightShadowmapTexture)
|
||||
{
|
||||
RenderTexture.ReleaseTemporary(m_MainLightShadowmapTexture);
|
||||
m_MainLightShadowmapTexture = null;
|
||||
}
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
m_MainLightShadowmapTexture = null;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
void RenderMainLightCascadeShadowmap(ref ScriptableRenderContext context, ref CullingResults cullResults, ref LightData lightData, ref ShadowData shadowData)
|
||||
{
|
||||
int shadowLightIndex = lightData.mainLightIndex;
|
||||
if (shadowLightIndex == -1)
|
||||
return;
|
||||
|
||||
VisibleLight shadowLight = lightData.visibleLights[shadowLightIndex];
|
||||
|
||||
// NOTE: Do NOT mix ProfilingScope with named CommandBuffers i.e. CommandBufferPool.Get("name").
|
||||
// Currently there's an issue which results in mismatched markers.
|
||||
CommandBuffer cmd = CommandBufferPool.Get();
|
||||
using (new ProfilingScope(cmd, ProfilingSampler.Get(URPProfileId.MainLightShadow)))
|
||||
{
|
||||
var settings = new ShadowDrawingSettings(cullResults, shadowLightIndex);
|
||||
|
||||
for (int cascadeIndex = 0; cascadeIndex < m_ShadowCasterCascadesCount; ++cascadeIndex)
|
||||
{
|
||||
settings.splitData = m_CascadeSlices[cascadeIndex].splitData;
|
||||
|
||||
Vector4 shadowBias = ShadowUtils.GetShadowBias(ref shadowLight, shadowLightIndex, ref shadowData, m_CascadeSlices[cascadeIndex].projectionMatrix, m_CascadeSlices[cascadeIndex].resolution);
|
||||
ShadowUtils.SetupShadowCasterConstantBuffer(cmd, ref shadowLight, shadowBias);
|
||||
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.CastingPunctualLightShadow, false);
|
||||
ShadowUtils.RenderShadowSlice(cmd, ref context, ref m_CascadeSlices[cascadeIndex],
|
||||
ref settings, m_CascadeSlices[cascadeIndex].projectionMatrix, m_CascadeSlices[cascadeIndex].viewMatrix);
|
||||
}
|
||||
|
||||
bool softShadows = shadowLight.light.shadows == LightShadows.Soft && shadowData.supportsSoftShadows;
|
||||
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.MainLightShadows, shadowData.mainLightShadowCascadesCount == 1);
|
||||
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.MainLightShadowCascades, shadowData.mainLightShadowCascadesCount > 1);
|
||||
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.SoftShadows, softShadows);
|
||||
|
||||
SetupMainLightShadowReceiverConstants(cmd, shadowLight, shadowData.supportsSoftShadows);
|
||||
}
|
||||
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
CommandBufferPool.Release(cmd);
|
||||
}
|
||||
|
||||
void SetupMainLightShadowReceiverConstants(CommandBuffer cmd, VisibleLight shadowLight, bool 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_ShadowmapWidth;
|
||||
float invShadowAtlasHeight = 1.0f / m_ShadowmapHeight;
|
||||
float invHalfShadowAtlasWidth = 0.5f * invShadowAtlasWidth;
|
||||
float invHalfShadowAtlasHeight = 0.5f * invShadowAtlasHeight;
|
||||
|
||||
cmd.SetGlobalTexture(m_MainLightShadowmap.id, m_MainLightShadowmapTexture);
|
||||
cmd.SetGlobalMatrixArray(MainLightShadowConstantBuffer._WorldToShadow, m_MainLightShadowMatrices);
|
||||
ShadowUtils.SetupShadowReceiverConstantBuffer(cmd, m_MainLightShadowParams);
|
||||
|
||||
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 (supportsSoftShadows)
|
||||
{
|
||||
if (m_SupportsBoxFilterForShadows)
|
||||
{
|
||||
cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowOffset0,
|
||||
new Vector4(-invHalfShadowAtlasWidth, -invHalfShadowAtlasHeight, 0.0f, 0.0f));
|
||||
cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowOffset1,
|
||||
new Vector4(invHalfShadowAtlasWidth, -invHalfShadowAtlasHeight, 0.0f, 0.0f));
|
||||
cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowOffset2,
|
||||
new Vector4(-invHalfShadowAtlasWidth, invHalfShadowAtlasHeight, 0.0f, 0.0f));
|
||||
cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowOffset3,
|
||||
new Vector4(invHalfShadowAtlasWidth, invHalfShadowAtlasHeight, 0.0f, 0.0f));
|
||||
}
|
||||
|
||||
// Currently only used when !SHADER_API_MOBILE but risky to not set them as it's generic
|
||||
// enough so custom shaders might use it.
|
||||
cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowmapSize, new Vector4(invShadowAtlasWidth,
|
||||
invShadowAtlasHeight,
|
||||
m_ShadowmapWidth, m_ShadowmapHeight));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,138 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Scripting.APIUpdating;
|
||||
|
||||
namespace UnityEngine.Experimental.Rendering.Universal
|
||||
{
|
||||
[MovedFrom("UnityEngine.Experimental.Rendering.LWRP")] public class RenderObjectsPass : ScriptableRenderPass
|
||||
{
|
||||
RenderQueueType renderQueueType;
|
||||
FilteringSettings m_FilteringSettings;
|
||||
RenderObjects.CustomCameraSettings m_CameraSettings;
|
||||
string m_ProfilerTag;
|
||||
ProfilingSampler m_ProfilingSampler;
|
||||
|
||||
public Material overrideMaterial { get; set; }
|
||||
public int overrideMaterialPassIndex { get; set; }
|
||||
|
||||
List<ShaderTagId> m_ShaderTagIdList = new List<ShaderTagId>();
|
||||
|
||||
public void SetDetphState(bool writeEnabled, CompareFunction function = CompareFunction.Less)
|
||||
{
|
||||
m_RenderStateBlock.mask |= RenderStateMask.Depth;
|
||||
m_RenderStateBlock.depthState = new DepthState(writeEnabled, function);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
public RenderObjectsPass(string profilerTag, RenderPassEvent renderPassEvent, string[] shaderTags, RenderQueueType renderQueueType, int layerMask, RenderObjects.CustomCameraSettings cameraSettings)
|
||||
{
|
||||
base.profilingSampler = new ProfilingSampler(nameof(RenderObjectsPass));
|
||||
|
||||
m_ProfilerTag = profilerTag;
|
||||
m_ProfilingSampler = new ProfilingSampler(profilerTag);
|
||||
this.renderPassEvent = renderPassEvent;
|
||||
this.renderQueueType = renderQueueType;
|
||||
this.overrideMaterial = null;
|
||||
this.overrideMaterialPassIndex = 0;
|
||||
RenderQueueRange renderQueueRange = (renderQueueType == RenderQueueType.Transparent)
|
||||
? RenderQueueRange.transparent
|
||||
: RenderQueueRange.opaque;
|
||||
m_FilteringSettings = new FilteringSettings(renderQueueRange, layerMask);
|
||||
|
||||
if (shaderTags != null && shaderTags.Length > 0)
|
||||
{
|
||||
foreach (var passName in shaderTags)
|
||||
m_ShaderTagIdList.Add(new ShaderTagId(passName));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ShaderTagIdList.Add(new ShaderTagId("SRPDefaultUnlit"));
|
||||
m_ShaderTagIdList.Add(new ShaderTagId("UniversalForward"));
|
||||
m_ShaderTagIdList.Add(new ShaderTagId("UniversalForwardOnly"));
|
||||
m_ShaderTagIdList.Add(new ShaderTagId("LightweightForward"));
|
||||
}
|
||||
|
||||
m_RenderStateBlock = new RenderStateBlock(RenderStateMask.Nothing);
|
||||
m_CameraSettings = cameraSettings;
|
||||
}
|
||||
|
||||
internal RenderObjectsPass(URPProfileId profileId, RenderPassEvent renderPassEvent, string[] shaderTags, RenderQueueType renderQueueType, int layerMask, RenderObjects.CustomCameraSettings cameraSettings)
|
||||
: this(profileId.GetType().Name, renderPassEvent, shaderTags, renderQueueType, layerMask, cameraSettings)
|
||||
{
|
||||
m_ProfilingSampler = ProfilingSampler.Get(profileId);
|
||||
}
|
||||
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
SortingCriteria sortingCriteria = (renderQueueType == RenderQueueType.Transparent)
|
||||
? SortingCriteria.CommonTransparent
|
||||
: renderingData.cameraData.defaultOpaqueSortFlags;
|
||||
|
||||
DrawingSettings drawingSettings = CreateDrawingSettings(m_ShaderTagIdList, ref renderingData, sortingCriteria);
|
||||
drawingSettings.overrideMaterial = overrideMaterial;
|
||||
drawingSettings.overrideMaterialPassIndex = overrideMaterialPassIndex;
|
||||
|
||||
ref CameraData cameraData = ref renderingData.cameraData;
|
||||
Camera camera = cameraData.camera;
|
||||
|
||||
// In case of camera stacking we need to take the viewport rect from base camera
|
||||
Rect pixelRect = renderingData.cameraData.pixelRect;
|
||||
float cameraAspect = (float)pixelRect.width / (float)pixelRect.height;
|
||||
|
||||
// NOTE: Do NOT mix ProfilingScope with named CommandBuffers i.e. CommandBufferPool.Get("name").
|
||||
// Currently there's an issue which results in mismatched markers.
|
||||
CommandBuffer cmd = CommandBufferPool.Get();
|
||||
using (new ProfilingScope(cmd, m_ProfilingSampler))
|
||||
{
|
||||
if (m_CameraSettings.overrideCamera)
|
||||
{
|
||||
if (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(m_CameraSettings.cameraFieldOfView, cameraAspect,
|
||||
camera.nearClipPlane, camera.farClipPlane);
|
||||
projectionMatrix = GL.GetGPUProjectionMatrix(projectionMatrix, cameraData.IsCameraProjectionMatrixFlipped());
|
||||
|
||||
Matrix4x4 viewMatrix = cameraData.GetViewMatrix();
|
||||
Vector4 cameraTranslation = viewMatrix.GetColumn(3);
|
||||
viewMatrix.SetColumn(3, cameraTranslation + m_CameraSettings.offset);
|
||||
|
||||
RenderingUtils.SetViewAndProjectionMatrices(cmd, viewMatrix, projectionMatrix, false);
|
||||
}
|
||||
}
|
||||
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
cmd.Clear();
|
||||
|
||||
context.DrawRenderers(renderingData.cullResults, ref drawingSettings, ref m_FilteringSettings,
|
||||
ref m_RenderStateBlock);
|
||||
|
||||
if (m_CameraSettings.overrideCamera && m_CameraSettings.restoreCamera && !cameraData.xr.enabled)
|
||||
{
|
||||
RenderingUtils.SetViewAndProjectionMatrices(cmd, cameraData.GetViewMatrix(), cameraData.GetGPUProjectionMatrix(), false);
|
||||
}
|
||||
}
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
CommandBufferPool.Release(cmd);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
namespace UnityEngine.Rendering.Universal
|
||||
{
|
||||
internal class SceneViewDepthCopyPass : ScriptableRenderPass
|
||||
{
|
||||
private RenderTargetHandle source { get; set; }
|
||||
|
||||
Material m_CopyDepthMaterial;
|
||||
const string m_ProfilerTag = "Copy Depth for Scene View";
|
||||
private static readonly ProfilingSampler m_ProfilingSampler = new ProfilingSampler(m_ProfilerTag);
|
||||
|
||||
public SceneViewDepthCopyPass(RenderPassEvent evt, Material copyDepthMaterial)
|
||||
{
|
||||
base.profilingSampler = new ProfilingSampler(nameof(SceneViewDepthCopyPass));
|
||||
m_CopyDepthMaterial = copyDepthMaterial;
|
||||
renderPassEvent = evt;
|
||||
}
|
||||
|
||||
public void Setup(RenderTargetHandle source)
|
||||
{
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
if (m_CopyDepthMaterial == null)
|
||||
{
|
||||
Debug.LogErrorFormat("Missing {0}. {1} render pass will not execute. Check for missing reference in the renderer resources.", m_CopyDepthMaterial, GetType().Name);
|
||||
return;
|
||||
}
|
||||
|
||||
// Restore Render target for additional editor rendering.
|
||||
// Note: Scene view camera always perform depth prepass
|
||||
CommandBuffer cmd = CommandBufferPool.Get();
|
||||
using (new ProfilingScope(cmd, m_ProfilingSampler))
|
||||
{
|
||||
CoreUtils.SetRenderTarget(cmd, BuiltinRenderTextureType.CameraTarget);
|
||||
cmd.SetGlobalTexture("_CameraDepthAttachment", source.Identifier());
|
||||
cmd.EnableShaderKeyword(ShaderKeywordStrings.DepthNoMsaa);
|
||||
cmd.DisableShaderKeyword(ShaderKeywordStrings.DepthMsaa2);
|
||||
cmd.DisableShaderKeyword(ShaderKeywordStrings.DepthMsaa4);
|
||||
cmd.DisableShaderKeyword(ShaderKeywordStrings.DepthMsaa8);
|
||||
// Blit has logic to flip projection matrix when rendering to render texture.
|
||||
// Currently the y-flip is handled in CopyDepthPass.hlsl by checking _ProjectionParams.x
|
||||
// If you replace this Blit with a Draw* that sets projection matrix double check
|
||||
// to also update shader.
|
||||
// scaleBias.x = flipSign
|
||||
// scaleBias.y = scale
|
||||
// scaleBias.z = bias
|
||||
// scaleBias.w = unused
|
||||
ref CameraData cameraData = ref renderingData.cameraData;
|
||||
float flipSign = (cameraData.IsCameraProjectionMatrixFlipped()) ? -1.0f : 1.0f;
|
||||
Vector4 scaleBiasRt = (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, scaleBiasRt);
|
||||
|
||||
cmd.DrawMesh(RenderingUtils.fullscreenMesh, Matrix4x4.identity, m_CopyDepthMaterial);
|
||||
}
|
||||
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
CommandBufferPool.Release(cmd);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,87 @@
|
||||
using System;
|
||||
using UnityEngine.Experimental.Rendering;
|
||||
|
||||
namespace UnityEngine.Rendering.Universal.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// Resolves shadows in a screen space texture.
|
||||
/// </summary>
|
||||
public class ScreenSpaceShadowResolvePass : ScriptableRenderPass
|
||||
{
|
||||
Material m_ScreenSpaceShadowsMaterial;
|
||||
RenderTargetHandle m_ScreenSpaceShadowmap;
|
||||
RenderTextureDescriptor m_RenderTextureDescriptor;
|
||||
|
||||
public ScreenSpaceShadowResolvePass(RenderPassEvent evt, Material screenspaceShadowsMaterial)
|
||||
{
|
||||
base.profilingSampler = new ProfilingSampler(nameof(ScreenSpaceShadowResolvePass));
|
||||
|
||||
m_ScreenSpaceShadowsMaterial = screenspaceShadowsMaterial;
|
||||
m_ScreenSpaceShadowmap.Init("_ScreenSpaceShadowmapTexture");
|
||||
renderPassEvent = evt;
|
||||
}
|
||||
|
||||
public void Setup(RenderTextureDescriptor baseDescriptor)
|
||||
{
|
||||
m_RenderTextureDescriptor = baseDescriptor;
|
||||
m_RenderTextureDescriptor.depthBufferBits = 0;
|
||||
m_RenderTextureDescriptor.msaaSamples = 1;
|
||||
m_RenderTextureDescriptor.graphicsFormat = RenderingUtils.SupportsGraphicsFormat(GraphicsFormat.R8_UNorm, FormatUsage.Linear | FormatUsage.Render)
|
||||
? GraphicsFormat.R8_UNorm
|
||||
: GraphicsFormat.B8G8R8A8_UNorm;
|
||||
}
|
||||
|
||||
public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
|
||||
{
|
||||
cmd.GetTemporaryRT(m_ScreenSpaceShadowmap.id, m_RenderTextureDescriptor, FilterMode.Bilinear);
|
||||
|
||||
RenderTargetIdentifier screenSpaceOcclusionTexture = m_ScreenSpaceShadowmap.Identifier();
|
||||
ConfigureTarget(screenSpaceOcclusionTexture);
|
||||
ConfigureClear(ClearFlag.All, Color.white);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
if (m_ScreenSpaceShadowsMaterial == null)
|
||||
{
|
||||
Debug.LogErrorFormat("Missing {0}. {1} render pass will not execute. Check for missing reference in the renderer resources.", m_ScreenSpaceShadowsMaterial, GetType().Name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (renderingData.lightData.mainLightIndex == -1)
|
||||
return;
|
||||
|
||||
Camera camera = renderingData.cameraData.camera;
|
||||
|
||||
CommandBuffer cmd = CommandBufferPool.Get();
|
||||
using (new ProfilingScope(cmd, ProfilingSampler.Get(URPProfileId.ResolveShadows)))
|
||||
{
|
||||
if (!renderingData.cameraData.xr.enabled)
|
||||
{
|
||||
cmd.SetViewProjectionMatrices(Matrix4x4.identity, Matrix4x4.identity);
|
||||
cmd.DrawMesh(RenderingUtils.fullscreenMesh, Matrix4x4.identity, m_ScreenSpaceShadowsMaterial);
|
||||
cmd.SetViewProjectionMatrices(camera.worldToCameraMatrix, camera.projectionMatrix);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Avoid setting and restoring camera view and projection matrices when in stereo.
|
||||
RenderTargetIdentifier screenSpaceOcclusionTexture = m_ScreenSpaceShadowmap.Identifier();
|
||||
Blit(cmd, screenSpaceOcclusionTexture, screenSpaceOcclusionTexture, m_ScreenSpaceShadowsMaterial);
|
||||
}
|
||||
}
|
||||
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
CommandBufferPool.Release(cmd);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnCameraCleanup(CommandBuffer cmd)
|
||||
{
|
||||
if (cmd == null)
|
||||
throw new ArgumentNullException("cmd");
|
||||
|
||||
cmd.ReleaseTemporaryRT(m_ScreenSpaceShadowmap.id);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,384 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using UnityEngine.Scripting.APIUpdating;
|
||||
|
||||
namespace UnityEngine.Rendering.Universal
|
||||
{
|
||||
/// <summary>
|
||||
/// Input requirements for <c>ScriptableRenderPass</c>.
|
||||
/// </summary>
|
||||
/// <seealso cref="ConfigureInput"/>
|
||||
[Flags]
|
||||
public enum ScriptableRenderPassInput
|
||||
{
|
||||
None = 0,
|
||||
Depth = 1 << 0,
|
||||
Normal = 1 << 1,
|
||||
Color = 1 << 2,
|
||||
}
|
||||
|
||||
// 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>
|
||||
[MovedFrom("UnityEngine.Rendering.LWRP")] 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,
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("Obsolete, to match the capital from 'Prepass' to 'PrePass' (UnityUpgradable) -> BeforeRenderingPrePasses")]
|
||||
BeforeRenderingPrepasses = 151,
|
||||
|
||||
/// <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 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>
|
||||
/// <c>ScriptableRenderPass</c> implements a logical rendering pass that can be used to extend Universal RP renderer.
|
||||
/// </summary>
|
||||
[MovedFrom("UnityEngine.Rendering.LWRP")] public abstract partial class ScriptableRenderPass
|
||||
{
|
||||
public RenderPassEvent renderPassEvent { get; set; }
|
||||
|
||||
public RenderTargetIdentifier[] colorAttachments
|
||||
{
|
||||
get => m_ColorAttachments;
|
||||
}
|
||||
|
||||
public RenderTargetIdentifier colorAttachment
|
||||
{
|
||||
get => m_ColorAttachments[0];
|
||||
}
|
||||
|
||||
public RenderTargetIdentifier depthAttachment
|
||||
{
|
||||
get => m_DepthAttachment;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The input requirements for the <c>ScriptableRenderPass</c>, which has been set using <c>ConfigureInput</c>
|
||||
/// </summary>
|
||||
/// <seealso cref="ConfigureInput"/>
|
||||
public ScriptableRenderPassInput input
|
||||
{
|
||||
get => m_Input;
|
||||
}
|
||||
|
||||
public ClearFlag clearFlag
|
||||
{
|
||||
get => m_ClearFlag;
|
||||
}
|
||||
|
||||
public Color clearColor
|
||||
{
|
||||
get => m_ClearColor;
|
||||
}
|
||||
|
||||
/// A ProfilingSampler for the entire pass. Used by higher level objects such as ScriptableRenderer etc.
|
||||
protected internal ProfilingSampler profilingSampler { get; set; }
|
||||
internal bool overrideCameraTarget { get; set; }
|
||||
internal bool isBlitRenderPass { get; set; }
|
||||
|
||||
RenderTargetIdentifier[] m_ColorAttachments = new RenderTargetIdentifier[] {BuiltinRenderTextureType.CameraTarget};
|
||||
RenderTargetIdentifier m_DepthAttachment = BuiltinRenderTextureType.CameraTarget;
|
||||
ScriptableRenderPassInput m_Input = ScriptableRenderPassInput.None;
|
||||
ClearFlag m_ClearFlag = ClearFlag.None;
|
||||
Color m_ClearColor = Color.black;
|
||||
|
||||
public ScriptableRenderPass()
|
||||
{
|
||||
renderPassEvent = RenderPassEvent.AfterRenderingOpaques;
|
||||
m_ColorAttachments = new RenderTargetIdentifier[] {BuiltinRenderTextureType.CameraTarget, 0, 0, 0, 0, 0, 0, 0};
|
||||
m_DepthAttachment = BuiltinRenderTextureType.CameraTarget;
|
||||
m_ClearFlag = ClearFlag.None;
|
||||
m_ClearColor = Color.black;
|
||||
overrideCameraTarget = false;
|
||||
isBlitRenderPass = false;
|
||||
profilingSampler = new ProfilingSampler(nameof(ScriptableRenderPass));
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// Configures render targets for this render pass. Call this instead of CommandBuffer.SetRenderTarget.
|
||||
/// This method should be called inside Configure.
|
||||
/// </summary>
|
||||
/// <param name="colorAttachment">Color attachment identifier.</param>
|
||||
/// <param name="depthAttachment">Depth attachment identifier.</param>
|
||||
/// <seealso cref="Configure"/>
|
||||
public void ConfigureTarget(RenderTargetIdentifier colorAttachment, RenderTargetIdentifier depthAttachment)
|
||||
{
|
||||
m_DepthAttachment = depthAttachment;
|
||||
ConfigureTarget(colorAttachment);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures render targets for this render pass. Call this instead of CommandBuffer.SetRenderTarget.
|
||||
/// This method should be called inside Configure.
|
||||
/// </summary>
|
||||
/// <param name="colorAttachment">Color attachment identifier.</param>
|
||||
/// <param name="depthAttachment">Depth attachment identifier.</param>
|
||||
/// <seealso cref="Configure"/>
|
||||
public void ConfigureTarget(RenderTargetIdentifier[] colorAttachments, RenderTargetIdentifier depthAttachment)
|
||||
{
|
||||
overrideCameraTarget = true;
|
||||
|
||||
uint nonNullColorBuffers = RenderingUtils.GetValidColorBufferCount(colorAttachments);
|
||||
if (nonNullColorBuffers > SystemInfo.supportedRenderTargetCount)
|
||||
Debug.LogError("Trying to set " + nonNullColorBuffers + " renderTargets, which is more than the maximum supported:" + SystemInfo.supportedRenderTargetCount);
|
||||
|
||||
m_ColorAttachments = colorAttachments;
|
||||
m_DepthAttachment = depthAttachment;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures render targets for this render pass. Call this instead of CommandBuffer.SetRenderTarget.
|
||||
/// This method should be called inside Configure.
|
||||
/// </summary>
|
||||
/// <param name="colorAttachment">Color attachment identifier.</param>
|
||||
/// <seealso cref="Configure"/>
|
||||
public void ConfigureTarget(RenderTargetIdentifier colorAttachment)
|
||||
{
|
||||
overrideCameraTarget = true;
|
||||
|
||||
m_ColorAttachments[0] = colorAttachment;
|
||||
for (int i = 1; i < m_ColorAttachments.Length; ++i)
|
||||
m_ColorAttachments[i] = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures render targets for this render pass. Call this instead of CommandBuffer.SetRenderTarget.
|
||||
/// This method should be called inside Configure.
|
||||
/// </summary>
|
||||
/// <param name="colorAttachment">Color attachment identifier.</param>
|
||||
/// <seealso cref="Configure"/>
|
||||
public void ConfigureTarget(RenderTargetIdentifier[] colorAttachments)
|
||||
{
|
||||
ConfigureTarget(colorAttachments, BuiltinRenderTextureType.CameraTarget);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures clearing for the render targets for this render pass. Call this inside Configure.
|
||||
/// </summary>
|
||||
/// <param name="clearFlag">ClearFlag containing information about what targets to clear.</param>
|
||||
/// <param name="clearColor">Clear color.</param>
|
||||
/// <seealso cref="Configure"/>
|
||||
public void ConfigureClear(ClearFlag clearFlag, Color clearColor)
|
||||
{
|
||||
m_ClearFlag = clearFlag;
|
||||
m_ClearColor = clearColor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is called by the renderer before rendering a camera
|
||||
/// Override this method if you need to to configure render targets and their clear state, and to create temporary render target textures.
|
||||
/// If a render pass doesn't override this method, this render pass renders to the active Camera's render target.
|
||||
/// You should never call CommandBuffer.SetRenderTarget. Instead call <c>ConfigureTarget</c> and <c>ConfigureClear</c>.
|
||||
/// </summary>
|
||||
/// <param name="cmd">CommandBuffer to enqueue rendering commands. This will be executed by the pipeline.</param>
|
||||
/// <param name="renderingData">Current rendering state information</param>
|
||||
/// <seealso cref="ConfigureTarget"/>
|
||||
/// <seealso cref="ConfigureClear"/>
|
||||
public virtual void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
|
||||
{}
|
||||
|
||||
/// <summary>
|
||||
/// This method is called by the renderer before executing the render pass.
|
||||
/// Override this method if you need to to configure render targets and their clear state, and to create temporary render target textures.
|
||||
/// If a render pass doesn't override this method, this render pass renders to the active Camera's render target.
|
||||
/// You should never call CommandBuffer.SetRenderTarget. Instead call <c>ConfigureTarget</c> and <c>ConfigureClear</c>.
|
||||
/// </summary>
|
||||
/// <param name="cmd">CommandBuffer to enqueue rendering commands. This will be executed by the pipeline.</param>
|
||||
/// <param name="cameraTextureDescriptor">Render texture descriptor of the camera render target.</param>
|
||||
/// <seealso cref="ConfigureTarget"/>
|
||||
/// <seealso cref="ConfigureClear"/>
|
||||
public virtual void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
|
||||
{}
|
||||
|
||||
|
||||
/// <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 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)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called upon finish rendering a camera stack. You can use this callback to release any resources created
|
||||
/// by this render pass that need to be cleanup once all cameras in the stack have finished rendering.
|
||||
/// This method will be called once after rendering the last camera in the camera stack.
|
||||
/// Cameras that don't have an explicit camera stack are also considered stacked rendering.
|
||||
/// In that case the Base camera is the first and last camera in the stack.
|
||||
/// </summary>
|
||||
/// <param name="cmd">Use this CommandBuffer to cleanup any generated data</param>
|
||||
public virtual void OnFinishCameraStackRendering(CommandBuffer cmd)
|
||||
{}
|
||||
|
||||
/// <summary>
|
||||
/// Execute the pass. This is where custom rendering occurs. Specific details are left to the implementation
|
||||
/// </summary>
|
||||
/// <param name="context">Use this render context to issue any draw commands during execution</param>
|
||||
/// <param name="renderingData">Current rendering state information</param>
|
||||
public abstract void Execute(ScriptableRenderContext context, ref RenderingData renderingData);
|
||||
|
||||
/// <summary>
|
||||
/// Add a blit command to the context for execution. This changes the active render target in the ScriptableRenderer to
|
||||
/// destination.
|
||||
/// </summary>
|
||||
/// <param name="cmd">Command buffer to record command for execution.</param>
|
||||
/// <param name="source">Source texture or target identifier to blit from.</param>
|
||||
/// <param name="destination">Destination texture or target identifier to blit into. This becomes the renderer active render target.</param>
|
||||
/// <param name="material">Material to use.</param>
|
||||
/// <param name="passIndex">Shader pass to use. Default is 0.</param>
|
||||
/// <seealso cref="ScriptableRenderer"/>
|
||||
public void Blit(CommandBuffer cmd, RenderTargetIdentifier source, RenderTargetIdentifier destination, Material material = null, int passIndex = 0)
|
||||
{
|
||||
ScriptableRenderer.SetRenderTarget(cmd, destination, BuiltinRenderTextureType.CameraTarget, clearFlag, clearColor);
|
||||
cmd.Blit(source, destination, material, passIndex);
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// <seealso cref="DrawingSettings"/>
|
||||
public DrawingSettings CreateDrawingSettings(ShaderTagId shaderTagId, ref RenderingData renderingData, SortingCriteria sortingCriteria)
|
||||
{
|
||||
Camera camera = renderingData.cameraData.camera;
|
||||
SortingSettings sortingSettings = new SortingSettings(camera) { criteria = sortingCriteria };
|
||||
DrawingSettings settings = new DrawingSettings(shaderTagId, sortingSettings)
|
||||
{
|
||||
perObjectData = renderingData.perObjectData,
|
||||
mainLightIndex = renderingData.lightData.mainLightIndex,
|
||||
enableDynamicBatching = renderingData.supportsDynamicBatching,
|
||||
|
||||
// Disable instancing for preview cameras. This is consistent with the built-in forward renderer. Also fixes case 1127324.
|
||||
enableInstancing = camera.cameraType == CameraType.Preview ? false : true,
|
||||
};
|
||||
return settings;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// <seealso cref="DrawingSettings"/>
|
||||
public DrawingSettings CreateDrawingSettings(List<ShaderTagId> shaderTagIdList,
|
||||
ref RenderingData renderingData, SortingCriteria sortingCriteria)
|
||||
{
|
||||
if (shaderTagIdList == null || shaderTagIdList.Count == 0)
|
||||
{
|
||||
Debug.LogWarning("ShaderTagId list is invalid. DrawingSettings is created with default pipeline ShaderTagId");
|
||||
return CreateDrawingSettings(new ShaderTagId("UniversalPipeline"), ref renderingData, sortingCriteria);
|
||||
}
|
||||
|
||||
DrawingSettings settings = CreateDrawingSettings(shaderTagIdList[0], ref renderingData, sortingCriteria);
|
||||
for (int i = 1; i < shaderTagIdList.Count; ++i)
|
||||
settings.SetShaderPassName(i, shaderTagIdList[i]);
|
||||
return settings;
|
||||
}
|
||||
|
||||
public static bool operator<(ScriptableRenderPass lhs, ScriptableRenderPass rhs)
|
||||
{
|
||||
return lhs.renderPassEvent < rhs.renderPassEvent;
|
||||
}
|
||||
|
||||
public static bool operator>(ScriptableRenderPass lhs, ScriptableRenderPass rhs)
|
||||
{
|
||||
return lhs.renderPassEvent > rhs.renderPassEvent;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEngine.Rendering.Universal.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// Calculate min and max depth per screen tile for tiled-based deferred shading.
|
||||
/// </summary>
|
||||
internal class TileDepthRangePass : ScriptableRenderPass
|
||||
{
|
||||
DeferredLights m_DeferredLights;
|
||||
int m_PassIndex = 0;
|
||||
|
||||
public TileDepthRangePass(RenderPassEvent evt, DeferredLights deferredLights, int passIndex)
|
||||
{
|
||||
base.profilingSampler = new ProfilingSampler(nameof(TileDepthRangePass));
|
||||
base.renderPassEvent = evt;
|
||||
m_DeferredLights = deferredLights;
|
||||
m_PassIndex = passIndex;
|
||||
}
|
||||
|
||||
public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
|
||||
{
|
||||
RenderTargetHandle outputTex;
|
||||
RenderTextureDescriptor desc;
|
||||
|
||||
if (m_PassIndex == 0 && m_DeferredLights.HasTileDepthRangeExtraPass())
|
||||
{
|
||||
int alignment = 1 << DeferredConfig.kTileDepthInfoIntermediateLevel;
|
||||
int depthInfoWidth = (m_DeferredLights.RenderWidth + alignment - 1) >> DeferredConfig.kTileDepthInfoIntermediateLevel;
|
||||
int depthInfoHeight = (m_DeferredLights.RenderHeight + alignment - 1) >> DeferredConfig.kTileDepthInfoIntermediateLevel;
|
||||
|
||||
outputTex = m_DeferredLights.DepthInfoTexture;
|
||||
desc = new RenderTextureDescriptor(depthInfoWidth, depthInfoHeight, UnityEngine.Experimental.Rendering.GraphicsFormat.R32_UInt, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
int tileDepthRangeWidth = m_DeferredLights.GetTiler(0).TileXCount;
|
||||
int tileDepthRangeHeight = m_DeferredLights.GetTiler(0).TileYCount;
|
||||
|
||||
outputTex = m_DeferredLights.TileDepthInfoTexture;
|
||||
desc = new RenderTextureDescriptor(tileDepthRangeWidth, tileDepthRangeHeight, UnityEngine.Experimental.Rendering.GraphicsFormat.R32_UInt, 0);
|
||||
}
|
||||
cmd.GetTemporaryRT(outputTex.id, desc, FilterMode.Point);
|
||||
base.ConfigureTarget(outputTex.Identifier());
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
if (m_PassIndex == 0)
|
||||
m_DeferredLights.ExecuteTileDepthInfoPass(context, ref renderingData);
|
||||
else
|
||||
m_DeferredLights.ExecuteDownsampleBitmaskPass(context, ref renderingData);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnCameraCleanup(CommandBuffer cmd)
|
||||
{
|
||||
if (cmd == null)
|
||||
throw new ArgumentNullException("cmd");
|
||||
|
||||
cmd.ReleaseTemporaryRT(m_DeferredLights.TileDepthInfoTexture.id);
|
||||
m_DeferredLights.TileDepthInfoTexture = RenderTargetHandle.CameraTarget;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
namespace UnityEngine.Rendering.Universal
|
||||
{
|
||||
/// <summary>
|
||||
/// Applies relevant settings before rendering transparent objects
|
||||
/// </summary>
|
||||
|
||||
internal class TransparentSettingsPass : ScriptableRenderPass
|
||||
{
|
||||
bool m_shouldReceiveShadows;
|
||||
|
||||
const string m_ProfilerTag = "Transparent Settings Pass";
|
||||
private static readonly ProfilingSampler m_ProfilingSampler = new ProfilingSampler(m_ProfilerTag);
|
||||
|
||||
public TransparentSettingsPass(RenderPassEvent evt, bool shadowReceiveSupported)
|
||||
{
|
||||
base.profilingSampler = new ProfilingSampler(nameof(TransparentSettingsPass));
|
||||
renderPassEvent = evt;
|
||||
m_shouldReceiveShadows = shadowReceiveSupported;
|
||||
}
|
||||
|
||||
public bool Setup(ref RenderingData renderingData)
|
||||
{
|
||||
// Currently we only need to enqueue this pass when the user
|
||||
// doesn't want transparent objects to receive shadows
|
||||
return !m_shouldReceiveShadows;
|
||||
}
|
||||
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
// Get a command buffer...
|
||||
CommandBuffer cmd = CommandBufferPool.Get();
|
||||
using (new ProfilingScope(cmd, m_ProfilingSampler))
|
||||
{
|
||||
// Toggle light shadows enabled based on the renderer setting set in the constructor
|
||||
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.MainLightShadows, m_shouldReceiveShadows);
|
||||
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.MainLightShadowCascades, m_shouldReceiveShadows);
|
||||
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.AdditionalLightShadows, m_shouldReceiveShadows);
|
||||
}
|
||||
|
||||
// Execute and release the command buffer...
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
CommandBufferPool.Release(cmd);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
#if ENABLE_VR && ENABLE_XR_MODULE
|
||||
|
||||
namespace UnityEngine.Rendering.Universal
|
||||
{
|
||||
/// <summary>
|
||||
/// Draw the XR occlusion mesh into the current depth buffer when XR is enabled.
|
||||
/// </summary>
|
||||
public class XROcclusionMeshPass : ScriptableRenderPass
|
||||
{
|
||||
public XROcclusionMeshPass(RenderPassEvent evt)
|
||||
{
|
||||
base.profilingSampler = new ProfilingSampler(nameof(XROcclusionMeshPass));
|
||||
renderPassEvent = evt;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
if (!renderingData.cameraData.xr.enabled)
|
||||
return;
|
||||
|
||||
CommandBuffer cmd = CommandBufferPool.Get();
|
||||
|
||||
renderingData.cameraData.xr.RenderOcclusionMesh(cmd);
|
||||
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
CommandBufferPool.Release(cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user