|
|
|
|
|
|
public const int k_MaxEnvLightsOnScreen = 64; |
|
|
|
public const int k_MaxShadowOnScreen = 16; |
|
|
|
public const int k_MaxCascadeCount = 4; //Should be not less than m_Settings.directionalLightCascadeCount;
|
|
|
|
public const int k_MaxStereoEyes = 2; |
|
|
|
public static readonly Vector3 k_BoxCullingExtentThreshold = Vector3.one * 0.01f; |
|
|
|
|
|
|
|
// Static keyword is required here else we get a "DestroyBuffer can only be called from the main thread"
|
|
|
|
|
|
|
|
|
|
|
public List<SFiniteLightBound> bounds; |
|
|
|
public List<LightVolumeData> lightVolumes; |
|
|
|
|
|
|
|
public List<SFiniteLightBound> rightEyeBounds; |
|
|
|
public List<LightVolumeData> rightEyeLightVolumes; |
|
|
|
|
|
|
|
public void Clear() |
|
|
|
{ |
|
|
|
|
|
|
|
|
|
|
bounds.Clear(); |
|
|
|
lightVolumes.Clear(); |
|
|
|
rightEyeBounds.Clear(); |
|
|
|
rightEyeLightVolumes.Clear(); |
|
|
|
} |
|
|
|
|
|
|
|
public void Allocate() |
|
|
|
|
|
|
|
|
|
|
bounds = new List<SFiniteLightBound>(); |
|
|
|
lightVolumes = new List<LightVolumeData>(); |
|
|
|
|
|
|
|
rightEyeBounds = new List<SFiniteLightBound>(); |
|
|
|
rightEyeLightVolumes = new List<LightVolumeData>(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int GetNumTileFtplX(HDCamera hdCamera) |
|
|
|
{ |
|
|
|
return (hdCamera.actualWidth + (LightDefinitions.s_TileSizeFptl - 1)) / LightDefinitions.s_TileSizeFptl; |
|
|
|
return ((int)hdCamera.screenSize.x + (LightDefinitions.s_TileSizeFptl - 1)) / LightDefinitions.s_TileSizeFptl; |
|
|
|
return (hdCamera.actualHeight + (LightDefinitions.s_TileSizeFptl - 1)) / LightDefinitions.s_TileSizeFptl; |
|
|
|
return ((int)hdCamera.screenSize.y + (LightDefinitions.s_TileSizeFptl - 1)) / LightDefinitions.s_TileSizeFptl; |
|
|
|
return (hdCamera.actualWidth + (LightDefinitions.s_TileSizeClustered - 1)) / LightDefinitions.s_TileSizeClustered; |
|
|
|
return ((int)hdCamera.screenSize.x + (LightDefinitions.s_TileSizeClustered - 1)) / LightDefinitions.s_TileSizeClustered; |
|
|
|
return (hdCamera.actualHeight + (LightDefinitions.s_TileSizeClustered - 1)) / LightDefinitions.s_TileSizeClustered; |
|
|
|
return ((int)hdCamera.screenSize.y + (LightDefinitions.s_TileSizeClustered - 1)) / LightDefinitions.s_TileSizeClustered; |
|
|
|
} |
|
|
|
|
|
|
|
public bool GetFeatureVariantsEnabled() |
|
|
|
|
|
|
|
|
|
|
s_GenAABBKernel = buildScreenAABBShader.FindKernel("ScreenBoundsAABB"); |
|
|
|
|
|
|
|
s_AABBBoundsBuffer = new ComputeBuffer(2 * k_MaxLightsOnScreen, 3 * sizeof(float)); |
|
|
|
s_ConvexBoundsBuffer = new ComputeBuffer(k_MaxLightsOnScreen, System.Runtime.InteropServices.Marshal.SizeOf(typeof(SFiniteLightBound))); |
|
|
|
s_LightVolumeDataBuffer = new ComputeBuffer(k_MaxLightsOnScreen, System.Runtime.InteropServices.Marshal.SizeOf(typeof(LightVolumeData))); |
|
|
|
// The bounds and light volumes are view-dependent, and AABB is additionally projection dependent.
|
|
|
|
// The view and proj matrices are per eye in stereo. This means we have to double the size of these buffers.
|
|
|
|
// TODO: Maybe in stereo, we will only support half as many lights total, in order to minimize buffer size waste.
|
|
|
|
// Alternatively, we could re-size these buffers if any stereo camera is active, instead of unilaterally increasing buffer size.
|
|
|
|
// TODO: I don't think k_MaxLightsOnScreen corresponds to the actual correct light count for cullable light types (punctual, area, env, decal)
|
|
|
|
s_AABBBoundsBuffer = new ComputeBuffer(k_MaxStereoEyes * 2 * k_MaxLightsOnScreen, 3 * sizeof(float)); |
|
|
|
s_ConvexBoundsBuffer = new ComputeBuffer(k_MaxStereoEyes* k_MaxLightsOnScreen, System.Runtime.InteropServices.Marshal.SizeOf(typeof(SFiniteLightBound))); |
|
|
|
s_LightVolumeDataBuffer = new ComputeBuffer(k_MaxStereoEyes* k_MaxLightsOnScreen, System.Runtime.InteropServices.Marshal.SizeOf(typeof(LightVolumeData))); |
|
|
|
s_DispatchIndirectBuffer = new ComputeBuffer(LightDefinitions.s_NumFeatureVariants * 3, sizeof(uint), ComputeBufferType.IndirectArguments); |
|
|
|
|
|
|
|
// Cluster
|
|
|
|
|
|
|
return 8 * (1 << k_Log2NumClusters); // total footprint for all layers of the tile (measured in light index entries)
|
|
|
|
} |
|
|
|
|
|
|
|
// TODO: Add proper stereo support
|
|
|
|
public void AllocResolutionDependentBuffers(int width, int height) |
|
|
|
public void AllocResolutionDependentBuffers(int width, int height, bool stereoEnabled) |
|
|
|
var nrStereoLayers = stereoEnabled ? 2 : 1; |
|
|
|
|
|
|
|
var nrTiles = nrTilesX * nrTilesY; |
|
|
|
var nrTiles = nrTilesX * nrTilesY * nrStereoLayers; |
|
|
|
s_TileFeatureFlags = new ComputeBuffer(nrTilesX * nrTilesY, sizeof(uint)); |
|
|
|
s_TileFeatureFlags = new ComputeBuffer(nrTiles, sizeof(uint)); |
|
|
|
var nrClusterTiles = nrClustersX * nrClustersY; |
|
|
|
var nrClusterTiles = nrClustersX * nrClustersY * nrStereoLayers; |
|
|
|
|
|
|
|
s_PerVoxelOffset = new ComputeBuffer((int)LightCategory.Count * (1 << k_Log2NumClusters) * nrClusterTiles, sizeof(uint)); |
|
|
|
s_PerVoxelLightLists = new ComputeBuffer(NumLightIndicesPerClusteredTile() * nrClusterTiles, sizeof(uint)); |
|
|
|
|
|
|
{ |
|
|
|
var nrBigTilesX = (width + 63) / 64; |
|
|
|
var nrBigTilesY = (height + 63) / 64; |
|
|
|
var nrBigTiles = nrBigTilesX * nrBigTilesY; |
|
|
|
var nrBigTiles = nrBigTilesX * nrBigTilesY * nrStereoLayers; |
|
|
|
s_BigTileLightList = new ComputeBuffer(LightDefinitions.s_MaxNrBigTileLightsPlusOne * nrBigTiles, sizeof(uint)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
return Matrix4x4.Scale(new Vector3(1, 1, -1)) * camera.worldToCameraMatrix; |
|
|
|
} |
|
|
|
|
|
|
|
static Matrix4x4 WorldToViewStereo(Camera camera, Camera.StereoscopicEye eyeIndex) |
|
|
|
{ |
|
|
|
return Matrix4x4.Scale(new Vector3(1, 1, -1)) * camera.GetStereoViewMatrix(eyeIndex); |
|
|
|
} |
|
|
|
|
|
|
|
// For light culling system, we need non oblique projection matrices
|
|
|
|
static Matrix4x4 CameraProjectionNonObliqueLHS(HDCamera camera) |
|
|
|
{ |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
static Matrix4x4 CameraProjectionStereoLHS(Camera camera, Camera.StereoscopicEye eyeIndex) |
|
|
|
{ |
|
|
|
return camera.GetStereoProjectionMatrix(eyeIndex) * Matrix4x4.Scale(new Vector3(1, 1, -1)); |
|
|
|
} |
|
|
|
|
|
|
|
public Vector3 GetLightColor(VisibleLight light) |
|
|
|
{ |
|
|
|
return new Vector3(light.finalColor.r, light.finalColor.g, light.finalColor.b); |
|
|
|
|
|
|
|
|
|
|
// TODO: we should be able to do this calculation only with LightData without VisibleLight light, but for now pass both
|
|
|
|
public void GetLightVolumeDataAndBound(LightCategory lightCategory, GPULightType gpuLightType, LightVolumeType lightVolumeType, |
|
|
|
VisibleLight light, LightData lightData, Vector3 lightDimensions, Matrix4x4 worldToView) |
|
|
|
VisibleLight light, LightData lightData, Vector3 lightDimensions, Matrix4x4 worldToView, |
|
|
|
Camera.StereoscopicEye eyeIndex = Camera.StereoscopicEye.Left) |
|
|
|
{ |
|
|
|
// Then Culling side
|
|
|
|
var range = lightDimensions.z; |
|
|
|
|
|
|
Debug.Assert(false, "TODO: encountered an unknown GPULightType."); |
|
|
|
} |
|
|
|
|
|
|
|
m_lightList.bounds.Add(bound); |
|
|
|
m_lightList.lightVolumes.Add(lightVolumeData); |
|
|
|
if (eyeIndex == Camera.StereoscopicEye.Left) |
|
|
|
{ |
|
|
|
m_lightList.bounds.Add(bound); |
|
|
|
m_lightList.lightVolumes.Add(lightVolumeData); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
m_lightList.rightEyeBounds.Add(bound); |
|
|
|
m_lightList.rightEyeLightVolumes.Add(lightVolumeData); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
m_lightList.envLights.Add(envLightData); |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public void GetEnvLightVolumeDataAndBound(ProbeWrapper probe, LightVolumeType lightVolumeType, Matrix4x4 worldToView) |
|
|
|
|
|
|
|
public void GetEnvLightVolumeDataAndBound(ProbeWrapper probe, LightVolumeType lightVolumeType, Matrix4x4 worldToView, Camera.StereoscopicEye eyeIndex = Camera.StereoscopicEye.Left) |
|
|
|
{ |
|
|
|
var bound = new SFiniteLightBound(); |
|
|
|
var lightVolumeData = new LightVolumeData(); |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
m_lightList.bounds.Add(bound); |
|
|
|
m_lightList.lightVolumes.Add(lightVolumeData); |
|
|
|
if (eyeIndex == Camera.StereoscopicEye.Left) |
|
|
|
{ |
|
|
|
m_lightList.bounds.Add(bound); |
|
|
|
m_lightList.lightVolumes.Add(lightVolumeData); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
m_lightList.rightEyeBounds.Add(bound); |
|
|
|
m_lightList.rightEyeLightVolumes.Add(lightVolumeData); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public int GetCurrentShadowCount() |
|
|
|
|
|
|
m_enableBakeShadowMask = false; |
|
|
|
|
|
|
|
m_lightList.Clear(); |
|
|
|
|
|
|
|
Vector3 camPosWS = camera.transform.position; |
|
|
|
var stereoEnabled = m_FrameSettings.enableStereo; |
|
|
|
|
|
|
|
// Note: Light with null intensity/Color are culled by the C++, no need to test it here
|
|
|
|
if (cullResults.visibleLights.Count != 0 || cullResults.visibleReflectionProbes.Count != 0) |
|
|
|
{ |
|
|
|
|
|
|
//TODO: Do not call ToArray here to avoid GC, refactor API
|
|
|
|
int[] shadowRequests = m_ShadowRequests.ToArray(); |
|
|
|
int[] shadowDataIndices; |
|
|
|
|
|
|
|
m_ShadowMgr.ProcessShadowRequests(m_FrameId, cullResults, camera, ShaderConfig.s_CameraRelativeRendering != 0, cullResults.visibleLights, |
|
|
|
ref shadowRequestCount, shadowRequests, out shadowDataIndices); |
|
|
|
|
|
|
|
|
|
|
// For now we will still apply the maximum of shadow here but we don't apply the sorting by priority + slot allocation yet
|
|
|
|
|
|
|
|
// 2. Go through all lights, convert them to GPU format.
|
|
|
|
// Create simultaneously data for culling (LigthVolumeData and rendering)
|
|
|
|
// Simultaneously create data for culling (LightVolumeData and SFiniteLightBound)
|
|
|
|
Vector3 camPosWS = camera.transform.position; |
|
|
|
|
|
|
|
var rightEyeWorldToView = Matrix4x4.identity; |
|
|
|
if (stereoEnabled) |
|
|
|
{ |
|
|
|
worldToView = WorldToViewStereo(camera, Camera.StereoscopicEye.Left); |
|
|
|
rightEyeWorldToView = WorldToViewStereo(camera, Camera.StereoscopicEye.Right); |
|
|
|
} |
|
|
|
|
|
|
|
for (int sortIndex = 0; sortIndex < sortCount; ++sortIndex) |
|
|
|
{ |
|
|
|
|
|
|
|
|
|
|
// Then culling side. Must be call in this order as we pass the created Light data to the function
|
|
|
|
GetLightVolumeDataAndBound(lightCategory, gpuLightType, lightVolumeType, light, m_lightList.lights[m_lightList.lights.Count - 1], lightDimensions, worldToView); |
|
|
|
if (stereoEnabled) |
|
|
|
GetLightVolumeDataAndBound(lightCategory, gpuLightType, lightVolumeType, light, m_lightList.lights[m_lightList.lights.Count - 1], lightDimensions, rightEyeWorldToView, Camera.StereoscopicEye.Right); |
|
|
|
|
|
|
|
// We make the light position camera-relative as late as possible in order
|
|
|
|
// to allow the preceding code to work with the absolute world space coordinates.
|
|
|
|
|
|
|
if (GetEnvLightData(cmd, camera, probeWrapper)) |
|
|
|
{ |
|
|
|
GetEnvLightVolumeDataAndBound(probeWrapper, lightVolumeType, worldToView); |
|
|
|
if (stereoEnabled) |
|
|
|
GetEnvLightVolumeDataAndBound(probeWrapper, lightVolumeType, rightEyeWorldToView, Camera.StereoscopicEye.Right); |
|
|
|
|
|
|
|
// We make the light position camera-relative as late as possible in order
|
|
|
|
// to allow the preceding code to work with the absolute world space coordinates.
|
|
|
|
|
|
|
Debug.Assert(m_lightList.bounds.Count == m_lightCount); |
|
|
|
Debug.Assert(m_lightList.lightVolumes.Count == m_lightCount); |
|
|
|
|
|
|
|
m_lightList.bounds.AddRange(DecalSystem.m_Bounds); |
|
|
|
m_lightList.lightVolumes.AddRange(DecalSystem.m_LightVolumes); |
|
|
|
m_lightCount += DecalSystem.m_DecalDatasCount; |
|
|
|
if (DecalSystem.m_DecalDatasCount > 0) |
|
|
|
{ |
|
|
|
// TODO: This adds the entire array to the buffer, likely introducing
|
|
|
|
// empty entries into the buffer. Currently, stereo assumes this buffer to
|
|
|
|
// be tightly packed. Decals are currently disabled for stereo anyway, so we
|
|
|
|
// will coordinate on a proper solution for this.
|
|
|
|
m_lightList.bounds.AddRange(DecalSystem.m_Bounds); |
|
|
|
m_lightList.lightVolumes.AddRange(DecalSystem.m_LightVolumes); |
|
|
|
m_lightCount += DecalSystem.m_DecalDatasCount; |
|
|
|
} |
|
|
|
|
|
|
|
if (stereoEnabled) |
|
|
|
{ |
|
|
|
// TODO: Proper decal + stereo cull management
|
|
|
|
|
|
|
|
Debug.Assert(m_lightList.rightEyeBounds.Count == m_lightCount); |
|
|
|
Debug.Assert(m_lightList.rightEyeLightVolumes.Count == m_lightCount); |
|
|
|
// TODO: GC considerations?
|
|
|
|
m_lightList.bounds.AddRange(m_lightList.rightEyeBounds); |
|
|
|
m_lightList.lightVolumes.AddRange(m_lightList.rightEyeLightVolumes); |
|
|
|
} |
|
|
|
|
|
|
|
UpdateDataBuffers(); |
|
|
|
|
|
|
|
m_maxShadowDistance = shadowSettings.maxShadowDistance; |
|
|
|
|
|
|
var camera = hdCamera.camera; |
|
|
|
cmd.BeginSample("Build Light List"); |
|
|
|
|
|
|
|
var w = camera.pixelWidth; |
|
|
|
var h = camera.pixelHeight; |
|
|
|
var w = (int)hdCamera.screenSize.x; |
|
|
|
var h = (int)hdCamera.screenSize.y; |
|
|
|
// camera to screen matrix (and it's inverse)
|
|
|
|
var proj = CameraProjectionNonObliqueLHS(hdCamera); |
|
|
|
var projscr = temp * proj; |
|
|
|
var invProjscr = projscr.inverse; |
|
|
|
// camera to screen matrix (and it's inverse)
|
|
|
|
var projArr = new Matrix4x4[2]; |
|
|
|
var projscrArr = new Matrix4x4[2]; |
|
|
|
var invProjscrArr = new Matrix4x4[2]; |
|
|
|
if (m_FrameSettings.enableStereo) |
|
|
|
{ |
|
|
|
for (int eyeIndex = 0; eyeIndex < 2; eyeIndex++) |
|
|
|
{ |
|
|
|
projArr[eyeIndex] = CameraProjectionStereoLHS(hdCamera.camera, (Camera.StereoscopicEye)eyeIndex); |
|
|
|
projscrArr[eyeIndex] = temp * projArr[eyeIndex]; |
|
|
|
invProjscrArr[eyeIndex] = projscrArr[eyeIndex].inverse; |
|
|
|
} |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
projArr[0] = CameraProjectionNonObliqueLHS(hdCamera); |
|
|
|
projscrArr[0] = temp * projArr[0]; |
|
|
|
invProjscrArr[0] = projscrArr[0].inverse; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// generate screen-space AABBs (used for both fptl and clustered).
|
|
|
|
if (m_lightCount != 0) |
|
|
|
{ |
|
|
|
|
|
|
temp.SetRow(3, new Vector4(0.0f, 0.0f, 0.0f, 1.0f)); |
|
|
|
var projh = temp * proj; |
|
|
|
var invProjh = projh.inverse; |
|
|
|
|
|
|
|
var projhArr = new Matrix4x4[2]; |
|
|
|
var invProjhArr = new Matrix4x4[2]; |
|
|
|
if (m_FrameSettings.enableStereo) |
|
|
|
{ |
|
|
|
for (int eyeIndex = 0; eyeIndex < 2; eyeIndex++) |
|
|
|
{ |
|
|
|
projhArr[eyeIndex] = temp * projArr[eyeIndex]; |
|
|
|
invProjhArr[eyeIndex] = projhArr[eyeIndex].inverse; |
|
|
|
} |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
projhArr[0] = temp * projArr[0]; |
|
|
|
invProjhArr[0] = projhArr[0].inverse; |
|
|
|
} |
|
|
|
|
|
|
|
// In the stereo case, we have two sets of light bounds to iterate over (bounds are in per-eye view space)
|
|
|
|
cmd.SetComputeMatrixParam(buildScreenAABBShader, HDShaderIDs.g_mProjection, projh); |
|
|
|
cmd.SetComputeMatrixParam(buildScreenAABBShader, HDShaderIDs.g_mInvProjection, invProjh); |
|
|
|
cmd.SetComputeMatrixArrayParam(buildScreenAABBShader, HDShaderIDs.g_mProjectionArr, projhArr); |
|
|
|
cmd.SetComputeMatrixArrayParam(buildScreenAABBShader, HDShaderIDs.g_mInvProjectionArr, invProjhArr); |
|
|
|
|
|
|
|
// In stereo, we output two sets of AABB bounds
|
|
|
|
cmd.DispatchCompute(buildScreenAABBShader, s_GenAABBKernel, (m_lightCount + 7) / 8, 1, 1); |
|
|
|
|
|
|
|
int tgY = m_FrameSettings.enableStereo ? 2 : 1; |
|
|
|
cmd.DispatchCompute(buildScreenAABBShader, s_GenAABBKernel, (m_lightCount + 7) / 8, tgY, 1); |
|
|
|
} |
|
|
|
|
|
|
|
// enable coarse 2D pass on 64x64 tiles (used for both fptl and clustered).
|
|
|
|
|
|
|
cmd.SetComputeIntParam(buildPerBigTileLightListShader, HDShaderIDs._EnvLightIndexShift, m_lightList.lights.Count); |
|
|
|
cmd.SetComputeIntParam(buildPerBigTileLightListShader, HDShaderIDs._DecalIndexShift, m_lightList.lights.Count + m_lightList.envLights.Count); |
|
|
|
cmd.SetComputeIntParam(buildPerBigTileLightListShader, HDShaderIDs.g_iNrVisibLights, m_lightCount); |
|
|
|
cmd.SetComputeMatrixParam(buildPerBigTileLightListShader, HDShaderIDs.g_mScrProjection, projscr); |
|
|
|
cmd.SetComputeMatrixParam(buildPerBigTileLightListShader, HDShaderIDs.g_mInvScrProjection, invProjscr); |
|
|
|
|
|
|
|
cmd.SetComputeMatrixParam(buildPerBigTileLightListShader, HDShaderIDs.g_mScrProjection, projscrArr[0]); |
|
|
|
cmd.SetComputeMatrixParam(buildPerBigTileLightListShader, HDShaderIDs.g_mInvScrProjection, invProjscrArr[0]); |
|
|
|
|
|
|
|
cmd.SetComputeFloatParam(buildPerBigTileLightListShader, HDShaderIDs.g_fNearPlane, camera.nearClipPlane); |
|
|
|
cmd.SetComputeFloatParam(buildPerBigTileLightListShader, HDShaderIDs.g_fFarPlane, camera.farClipPlane); |
|
|
|
cmd.SetComputeBufferParam(buildPerBigTileLightListShader, s_GenListPerBigTileKernel, HDShaderIDs.g_vLightList, s_BigTileLightList); |
|
|
|
|
|
|
cmd.SetComputeBufferParam(buildPerTileLightListShader, s_GenListPerTileKernel, HDShaderIDs._LightVolumeData, s_LightVolumeDataBuffer); |
|
|
|
cmd.SetComputeBufferParam(buildPerTileLightListShader, s_GenListPerTileKernel, HDShaderIDs.g_data, s_ConvexBoundsBuffer); |
|
|
|
|
|
|
|
cmd.SetComputeMatrixParam(buildPerTileLightListShader, HDShaderIDs.g_mScrProjection, projscr); |
|
|
|
cmd.SetComputeMatrixParam(buildPerTileLightListShader, HDShaderIDs.g_mInvScrProjection, invProjscr); |
|
|
|
cmd.SetComputeMatrixParam(buildPerTileLightListShader, HDShaderIDs.g_mScrProjection, projscrArr[0]); |
|
|
|
cmd.SetComputeMatrixParam(buildPerTileLightListShader, HDShaderIDs.g_mInvScrProjection, invProjscrArr[0]); |
|
|
|
|
|
|
|
cmd.SetComputeTextureParam(buildPerTileLightListShader, s_GenListPerTileKernel, HDShaderIDs.g_depth_tex, cameraDepthBufferRT); |
|
|
|
cmd.SetComputeBufferParam(buildPerTileLightListShader, s_GenListPerTileKernel, HDShaderIDs.g_vLightList, s_LightList); |
|
|
|
if (m_FrameSettings.lightLoopSettings.enableBigTilePrepass) |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// Cluster
|
|
|
|
VoxelLightListGeneration(cmd, hdCamera, projscr, invProjscr, cameraDepthBufferRT); |
|
|
|
VoxelLightListGeneration(cmd, hdCamera, projscrArr[0], invProjscrArr[0], cameraDepthBufferRT); |
|
|
|
|
|
|
|
if (enableFeatureVariants) |
|
|
|
{ |
|
|
|