using System; using System.Collections.Generic; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Profiling; namespace UnityEngine.Rendering.HighDefinition { /// /// World light manager handles all lights in a scene and makes them accessible on the GPU. This includes /// information about their shape. /// /// 1. First create WorldLights object and populate it using CollectWorldLights(). /// 2. Pass the WorldLights object to BuildWorldLightDatas() to create the GPU representation of the lights. /// 3. (optionally) Pass the WorldLights object to BuildWorldLightVolumes() to create the GPU representation of the light bounds. /// 4. Bind the buffers using Bind /// /// /// WorldLights life time loop /// - Construct / Update /// - Reset /// /// WorldLightsGpu and WorldLightsVolumes life time loop /// - Construct / Update /// - PushToGpu /// - Bind /// - Release /// /// TODO-WL: Replace data structures with nativearrays to be able to do burst jobs /// TODO-WL: CollectWorldLights() should be replaced with updates using the object dispatcher and kept persistent on the GPU /// TODO-WL: Remove the relative camera light positions, they need to be global. /// /// /// Flags: /// Active = 1 /// Rest user defined [GenerateHLSL(PackingRules.Exact, false)] struct WorldLightVolume { public Vector3 position; public uint flags; public Vector3 range; public uint shape; public uint lightType; public uint lightIndex; } class WorldLightsSettings { public bool enabled = false; } class WorldLights { public struct VisibleLight { public HDLightRenderEntity light; public Bounds bounds; public int type; } public NativeList culledLights = new NativeList(Allocator.Persistent); public NativeList hdLightEntityArray = new NativeList(Allocator.Persistent); // The list of reflection probes public List reflectionProbeArray = new List(); // Counter of the total number of lights public int totalLightCount => normalLightCount + envLightCount + decalCount; public int normalLightCount => pointLightCount + lineLightCount + rectLightCount + discLightCount; public int envLightCount => reflectionProbeArray.Count; public int pointLightCount = 0; public int lineLightCount = 0; public int rectLightCount = 0; public int discLightCount = 0; public int decalCount = 0; internal void Reset() { pointLightCount = 0; lineLightCount = 0; rectLightCount = 0; discLightCount = 0; decalCount = 0; culledLights.Clear(); hdLightEntityArray.Clear(); reflectionProbeArray.Clear(); } internal void Release() { culledLights.Dispose(); hdLightEntityArray.Dispose(); } } class WorldLightsGpu { // Light runtime data NativeArray m_LightDataCPUArray = new NativeArray(WorldLightManager.SizeAlignment, Allocator.Persistent); GraphicsBuffer m_LightDataGPUArray = new GraphicsBuffer(GraphicsBuffer.Target.Structured, 1, System.Runtime.InteropServices.Marshal.SizeOf(typeof(LightData))); // Env Light data NativeArray m_EnvLightDataCPUArray = new NativeArray(WorldLightManager.SizeAlignment, Allocator.Persistent); GraphicsBuffer m_EnvLightDataGPUArray = new GraphicsBuffer(GraphicsBuffer.Target.Structured, 1, System.Runtime.InteropServices.Marshal.SizeOf(typeof(EnvLightData))); // Env Light data support data WorldEnvLightReflectionData m_EnvLightReflectionDataRT = new WorldEnvLightReflectionData(); int m_numLights = 0; int m_numEnvLights = 0; internal ref LightData GetRef(int i) { unsafe { LightData* data = (LightData*)m_LightDataCPUArray.GetUnsafePtr() + i; return ref UnsafeUtility.AsRef(data); } } internal ref EnvLightData GetEnvRef(int i) { unsafe { EnvLightData* data = (EnvLightData*)m_EnvLightDataCPUArray.GetUnsafePtr() + i; return ref UnsafeUtility.AsRef(data); } } internal void ResizeLightDataGraphicsBuffer(int numLights) { int numLightsGpu = Math.Max(numLights, 1); if (numLights > m_LightDataCPUArray.Length) { m_LightDataCPUArray.ResizeArray(numLights); } m_numLights = numLights; // If it is not null and it has already the right size, we are pretty much done if (m_LightDataGPUArray.count == numLightsGpu) return; // It is not the right size, free it to be reallocated CoreUtils.SafeRelease(m_LightDataGPUArray); // Allocate the next buffer buffer m_LightDataGPUArray = new GraphicsBuffer(GraphicsBuffer.Target.Structured, numLightsGpu, System.Runtime.InteropServices.Marshal.SizeOf(typeof(LightData))); } internal unsafe void SetPlanarReflectionDataRT(int index, ref Matrix4x4 vp, ref Vector4 scaleOffset) { Debug.Assert(index < HDRenderPipeline.k_MaxPlanarReflectionsOnScreen); for (int j = 0; j < 16; ++j) m_EnvLightReflectionDataRT._PlanarCaptureVPWL[index * 16 + j] = vp[j]; for (int j = 0; j < 4; ++j) m_EnvLightReflectionDataRT._PlanarScaleOffsetWL[index * 4 + j] = scaleOffset[j]; } internal unsafe void SetCubeReflectionDataRT(int index, ref Vector4 scaleOffset) { Debug.Assert(index < HDRenderPipeline.k_MaxCubeReflectionsOnScreen); for (int j = 0; j < 4; ++j) m_EnvLightReflectionDataRT._CubeScaleOffsetWL[index * 4 + j] = scaleOffset[j]; } internal void ResizeEnvLightDataGraphicsBuffer(int numEnvLights) { int numEnvLightsGpu = Math.Max(numEnvLights, 1); if (numEnvLights > m_EnvLightDataCPUArray.Length) { int newSize = HDUtils.DivRoundUp(numEnvLights, WorldLightManager.SizeAlignment) * WorldLightManager.SizeAlignment; m_EnvLightDataCPUArray.ResizeArray(newSize); } m_numEnvLights = numEnvLights; if (m_EnvLightDataGPUArray.count == numEnvLightsGpu) return; CoreUtils.SafeRelease(m_EnvLightDataGPUArray); // Allocate the next buffer buffer m_EnvLightDataGPUArray = new GraphicsBuffer(GraphicsBuffer.Target.Structured, numEnvLightsGpu, System.Runtime.InteropServices.Marshal.SizeOf(typeof(EnvLightData))); } internal void PushToGpu(CommandBuffer cmd) { if (m_numLights > 0) m_LightDataGPUArray.SetData(m_LightDataCPUArray, 0, 0, m_numLights); if (m_numEnvLights > 0) m_EnvLightDataGPUArray.SetData(m_EnvLightDataCPUArray, 0, 0, m_numEnvLights); ConstantBuffer.PushGlobal(cmd, m_EnvLightReflectionDataRT, HDShaderIDs._WorldEnvLightReflectionData); } public void Bind(CommandBuffer cmd, int lightDataShaderID, int envLightDataShaderID) { if (lightDataShaderID > 0) cmd.SetGlobalBuffer(lightDataShaderID, m_LightDataGPUArray); if (envLightDataShaderID > 0) cmd.SetGlobalBuffer(envLightDataShaderID, m_EnvLightDataGPUArray); } internal void Release() { CoreUtils.SafeRelease(m_LightDataGPUArray); m_LightDataGPUArray = null; CoreUtils.SafeRelease(m_EnvLightDataGPUArray); m_EnvLightDataGPUArray = null; m_LightDataCPUArray.Dispose(); m_EnvLightDataCPUArray.Dispose(); } } class WorldLightsVolumes { // Light Culling data NativeArray m_LightVolumesCPUArray = new NativeArray(WorldLightManager.SizeAlignment, Allocator.Persistent); GraphicsBuffer m_LightVolumeGPUArray = new GraphicsBuffer(GraphicsBuffer.Target.Structured, 1, System.Runtime.InteropServices.Marshal.SizeOf(typeof(WorldLightVolume))); NativeArray m_LightFlagsCPUArray = new NativeArray(WorldLightManager.SizeAlignment, Allocator.Persistent); GraphicsBuffer m_LightFlagsGPUArray = new GraphicsBuffer(GraphicsBuffer.Target.Structured, 1, System.Runtime.InteropServices.Marshal.SizeOf(typeof(uint))); int m_numLights = 0; internal ref WorldLightVolume GetRef(int i) { unsafe { WorldLightVolume* data = (WorldLightVolume*)m_LightVolumesCPUArray.GetUnsafePtr() + i; return ref UnsafeUtility.AsRef(data); } } internal ref uint GetFlagsRef(int i) { unsafe { uint* data = (uint*)m_LightFlagsCPUArray.GetUnsafePtr() + i; return ref UnsafeUtility.AsRef(data); } } internal void ResizeVolumeBuffer(int numLights) { int numLightsGpu = Math.Max(numLights, 1); // Always reset the bounds bounds.SetMinMax(WorldLightManager.minBounds, WorldLightManager.maxBounds); if (numLights > m_LightVolumesCPUArray.Length) { int newSize = HDUtils.DivRoundUp(numLights, WorldLightManager.SizeAlignment) * WorldLightManager.SizeAlignment; m_LightVolumesCPUArray.ResizeArray(newSize); m_LightFlagsCPUArray.ResizeArray(newSize); } m_numLights = numLights; // If it is not null and it has already the right size, we are pretty much done if (m_LightVolumeGPUArray.count == numLightsGpu) return; CoreUtils.SafeRelease(m_LightVolumeGPUArray); CoreUtils.SafeRelease(m_LightFlagsGPUArray); m_LightVolumeGPUArray = new GraphicsBuffer(GraphicsBuffer.Target.Structured, numLightsGpu, System.Runtime.InteropServices.Marshal.SizeOf(typeof(WorldLightVolume))); m_LightFlagsGPUArray = new GraphicsBuffer(GraphicsBuffer.Target.Structured, numLightsGpu, System.Runtime.InteropServices.Marshal.SizeOf(typeof(uint))); } internal void PushToGpu() { if (m_numLights > 0) { m_LightVolumeGPUArray.SetData(m_LightVolumesCPUArray, 0, 0, m_numLights); m_LightFlagsGPUArray.SetData(m_LightFlagsCPUArray, 0, 0, m_numLights); } } public void Bind(CommandBuffer cmd, int lightVolumeShaderID, int lightFlagsShaderID) { if (lightVolumeShaderID > 0) cmd.SetGlobalBuffer(lightVolumeShaderID, m_LightVolumeGPUArray); if (lightFlagsShaderID > 0) cmd.SetGlobalBuffer(lightFlagsShaderID, m_LightFlagsGPUArray); } internal void Release() { m_LightVolumesCPUArray.Dispose(); m_LightFlagsCPUArray.Dispose(); CoreUtils.SafeRelease(m_LightVolumeGPUArray); m_LightVolumeGPUArray = null; CoreUtils.SafeRelease(m_LightFlagsGPUArray); m_LightFlagsGPUArray = null; } public GraphicsBuffer GetBuffer() { return m_LightVolumeGPUArray; } public int GetCount() { return m_numLights; } public Bounds bounds = new Bounds(Vector3.zero, Vector3.zero); } class WorldLightManager { public static void CollectWorldLights(in HDCamera hdCamera, in WorldLightsSettings settings, in Func flagFunc, in Bounds bounds, WorldLights worldLights) { // Refresh the entire structure every frame for now worldLights.Reset(); if (!settings.enabled) return; using var profilerScope = k_ProfilerMarkerCollect.Auto(); Vector3 camPosWS = hdCamera.mainViewConstants.worldSpaceCameraPos; // Fetch all visible lights in the scene HDLightRenderDatabase lightEntities = HDLightRenderDatabase.instance; for (int lightIdx = 0; lightIdx < lightEntities.lightCount; ++lightIdx) { HDLightRenderEntity lightRenderEntity = lightEntities.lightEntities[lightIdx]; HDAdditionalLightData hdLight = lightEntities.hdAdditionalLightData[lightIdx]; if (hdLight != null && hdLight.enabled && hdLight != HDUtils.s_DefaultHDAdditionalLightData) { Light light = hdLight.gameObject.GetComponent(); // If the light is null or disabled, skip it if (light == null || !light.enabled) continue; // TODO-WL: Here we filter out all light that doesn't have a flag set // this to ensure that we don't process more lights than before if ((flagFunc(hdCamera, hdLight, light) & 0xfffffffe) == 0) continue; // TODO-WL: Directional lights if (hdLight.legacyLight.type == LightType.Directional) continue; // Compute the camera relative position Vector3 lightPositionRWS = hdLight.gameObject.transform.position; if (ShaderConfig.s_CameraRelativeRendering != 0) lightPositionRWS -= camPosWS; var lightType = hdLight.legacyLight.type; float lightRange = light.range; bool isAreaLight = lightType.IsArea(); bool isBoxLight = lightType == LightType.Box; var visibleLight = new WorldLights.VisibleLight(); visibleLight.light = lightRenderEntity; // Compute light bounds and cull if (!isAreaLight && !isBoxLight) { if (!WorldLightCulling.IntersectSphereAABB(lightPositionRWS, lightRange, bounds.min, bounds.max)) continue; visibleLight.bounds = new Bounds(lightPositionRWS, new Vector3(2.0f * lightRange, 2.0f * lightRange, 2.0f * lightRange)); } else { // let's compute the oobb of the light influence volume first Vector3 oobbDimensions = new Vector3(hdLight.shapeWidth + 2 * lightRange, hdLight.shapeHeight + 2 * lightRange, lightRange); // One-sided Vector3 extents = 0.5f * oobbDimensions; Vector3 oobbCenter = lightPositionRWS; // Tube lights don't have forward / backward facing and have full extents on all directions as a consequence, since their OOBB is centered if (lightType == LightType.Tube) { oobbDimensions.z *= 2; extents.z *= 2; } else { oobbCenter += extents.z * hdLight.gameObject.transform.forward; } // Let's now compute an AABB that matches the previously defined OOBB Bounds lightBounds = new Bounds(); OOBBToAABBBounds(oobbCenter, extents, hdLight.gameObject.transform.up, hdLight.gameObject.transform.right, hdLight.gameObject.transform.forward, ref lightBounds); if (!bounds.Intersects(lightBounds)) continue; visibleLight.bounds = lightBounds; } switch (hdLight.legacyLight.type) { case LightType.Point: case LightType.Spot: case LightType.Pyramid: case LightType.Box: worldLights.pointLightCount++; visibleLight.type = 0; break; case LightType.Tube: worldLights.lineLightCount++; visibleLight.type = 1; break; case LightType.Rectangle: worldLights.rectLightCount++; visibleLight.type = 2; break; case LightType.Disc: worldLights.discLightCount++; visibleLight.type = 3; break; } worldLights.culledLights.Add(visibleLight); } } // We now have to sort the lights by type Span indices = stackalloc int[4]; indices[0] = 0; indices[1] = indices[0] + worldLights.pointLightCount; indices[2] = indices[1] + worldLights.lineLightCount; indices[3] = indices[2] + worldLights.rectLightCount; worldLights.hdLightEntityArray.Resize(worldLights.normalLightCount, NativeArrayOptions.UninitializedMemory); for (int i = 0; i < worldLights.hdLightEntityArray.Length; i++) { int type = worldLights.culledLights[i].type; worldLights.hdLightEntityArray[indices[type]] = worldLights.culledLights[i]; indices[type]++; } // Process the reflection probes; skip when pathtracer is on to avoid filling the reflection probe atlas if (!hdCamera.IsPathTracingEnabled()) { HDAdditionalReflectionData[] reflectionProbeArray = HDAdditionalReflectionData.GetAllInstances(); for (int reflIdx = 0; reflIdx < reflectionProbeArray.Length; ++reflIdx) { HDAdditionalReflectionData reflectionProbe = reflectionProbeArray[reflIdx]; // Add it to the list if enabled // Skip the probe if the probe has never rendered (in real time cases) or if texture is null if (!(reflectionProbe != null && reflectionProbe.enabled && reflectionProbe.ReflectionProbeIsEnabled() && reflectionProbe.gameObject.activeSelf && reflectionProbe.HasValidRenderedData())) continue; // Compute the camera relative position Vector3 probePositionRWS = reflectionProbe.influenceToWorld.GetColumn(3); if (ShaderConfig.s_CameraRelativeRendering != 0) probePositionRWS -= camPosWS; // Cull the probe if (reflectionProbe.influenceVolume.shape == InfluenceShape.Sphere) { var range = reflectionProbe.influenceVolume.sphereRadius; if (!WorldLightCulling.IntersectSphereAABB(probePositionRWS, range, bounds.min, bounds.max)) continue; } else { if (!bounds.Intersects(new Bounds(probePositionRWS, reflectionProbe.influenceVolume.boxSize))) continue; } worldLights.reflectionProbeArray.Add(reflectionProbe); } } // Decals // We don't do any CPU culling, they are all uploaded to GPU and culled during clustering worldLights.decalCount = DecalSystem.GetDecalCount(hdCamera); } public static void BuildWorldLightDatas(CommandBuffer cmd, in HDCamera hdCamera, in HDRenderPipeline renderPipeline, in WorldLights worldLights, WorldLightsGpu worldLightsGpu) { using var profilerScope = k_ProfilerMarkerBuild.Auto(); // Clear gpu data worldLightsGpu.ResizeLightDataGraphicsBuffer(worldLights.normalLightCount); worldLightsGpu.ResizeEnvLightDataGraphicsBuffer(worldLights.envLightCount); #region Normal lights if (worldLights.normalLightCount > 0) { // Grab the shadow settings var hdShadowSettings = hdCamera.volumeStack.GetComponent(); // Build the data for every light HDLightRenderDatabase lightEntities = HDLightRenderDatabase.instance; var processedLightEntity = new HDProcessedVisibleLight() { shadowMapFlags = HDProcessedVisibleLightsBuilder.ShadowMapFlags.None }; var globalConfig = HDGpuLightsBuilder.CreateGpuLightDataJobGlobalConfig.Create(hdCamera, hdShadowSettings); var shadowInitParams = renderPipeline.currentPlatformRenderPipelineSettings.hdShadowInitParams; for (int lightIdx = 0; lightIdx < worldLights.hdLightEntityArray.Length; ++lightIdx) { // Grab the additinal light data to process int dataIndex = lightEntities.GetEntityDataIndex(worldLights.hdLightEntityArray[lightIdx].light); HDAdditionalLightData additionalLightData = lightEntities.hdAdditionalLightData[dataIndex]; ref LightData lightData = ref worldLightsGpu.GetRef(lightIdx); lightData = s_defaultLightData; // When the user deletes a light source in the editor, there is a single frame where the light is null before the collection of light in the scene is triggered // the workaround for this is simply to add an invalid light for that frame if (additionalLightData == null) { continue; } // Evaluate all the light type data that we need LightCategory lightCategory = LightCategory.Count; GPULightType gpuLightType = GPULightType.Point; LightVolumeType lightVolumeType = LightVolumeType.Count; LightType lightType = additionalLightData.legacyLight.type; HDRenderPipeline.EvaluateGPULightType(lightType, ref lightCategory, ref gpuLightType, ref lightVolumeType); // Fetch the light component for this light additionalLightData.gameObject.TryGetComponent(out Light lightComponent); ref HDLightRenderData lightRenderData = ref lightEntities.GetLightDataAsRef(dataIndex); // Build the processed light data that we need processedLightEntity.dataIndex = dataIndex; processedLightEntity.gpuLightType = gpuLightType; processedLightEntity.lightType = lightType; processedLightEntity.distanceToCamera = (additionalLightData.transform.position - hdCamera.camera.transform.position).magnitude; processedLightEntity.lightDistanceFade = HDUtils.ComputeLinearDistanceFade(processedLightEntity.distanceToCamera, lightRenderData.fadeDistance); processedLightEntity.lightVolumetricDistanceFade = HDUtils.ComputeLinearDistanceFade(processedLightEntity.distanceToCamera, lightRenderData.volumetricFadeDistance); processedLightEntity.isBakedShadowMask = HDRenderPipeline.IsBakedShadowMaskLight(lightComponent); // Build a visible light VisibleLight visibleLight = new VisibleLight(); visibleLight.finalColor = additionalLightData.EvaluateLightColor(); visibleLight.range = lightComponent.range; // This should be done explicitly, localToWorld matrix doesn't work here Matrix4x4 localToWorldMatrix = new Matrix4x4(); localToWorldMatrix.SetColumn(3, lightComponent.gameObject.transform.position); localToWorldMatrix.SetColumn(2, lightComponent.transform.forward); localToWorldMatrix.SetColumn(1, lightComponent.transform.up); localToWorldMatrix.SetColumn(0, lightComponent.transform.right); visibleLight.localToWorldMatrix = localToWorldMatrix; visibleLight.spotAngle = lightComponent.spotAngle; int shadowIndex = additionalLightData.shadowIndex; // Use the shared code to build the light data HDGpuLightsBuilder.CreateGpuLightDataJob.ConvertLightToGPUFormat( lightCategory, gpuLightType, globalConfig, lightComponent.lightShadowCasterMode, lightComponent.bakingOutput, visibleLight, processedLightEntity, lightRenderData, out var _, ref lightData); renderPipeline.gpuLightList.ProcessLightDataShadowIndex(cmd, shadowInitParams, lightType, lightComponent, additionalLightData, shadowIndex, ref lightData); // 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. Vector3 camPosWS = hdCamera.mainViewConstants.worldSpaceCameraPos; HDRenderPipeline.UpdateLightCameraRelativetData(ref lightData, camPosWS); } } #endregion #region Env lights if (worldLights.envLightCount > 0) { ProcessedProbeData processedProbe = new ProcessedProbeData(); int fetchIndex; Vector4 scaleOffset; Matrix4x4 vp; // Build the data for every light for (int lightIdx = 0; lightIdx < worldLights.reflectionProbeArray.Count; ++lightIdx) { ref EnvLightData envLightData = ref worldLightsGpu.GetEnvRef(lightIdx); envLightData = s_defaultEnvLightData; HDProbe probeData = worldLights.reflectionProbeArray[lightIdx]; HDRenderPipeline.PreprocessProbeData(ref processedProbe, probeData, hdCamera); renderPipeline.GetEnvLightData(cmd, hdCamera, processedProbe, ref envLightData, out fetchIndex, out scaleOffset, out vp); switch (processedProbe.hdProbe) { case PlanarReflectionProbe planarProbe: worldLightsGpu.SetPlanarReflectionDataRT(fetchIndex, ref vp, ref scaleOffset); break; case HDAdditionalReflectionData reflectionData: worldLightsGpu.SetCubeReflectionDataRT(fetchIndex, ref scaleOffset); break; }; // 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. Vector3 camPosWS = hdCamera.mainViewConstants.worldSpaceCameraPos; HDRenderPipeline.UpdateEnvLighCameraRelativetData(ref envLightData, camPosWS); } } #endregion worldLightsGpu.PushToGpu(cmd); } public static void BuildWorldLightVolumes(in HDCamera hdCamera, in HDRenderPipeline renderPipeline, in WorldLights worldLights, in Func flagFunc, WorldLightsVolumes worldLightsVolumes) { int totalNumLights = worldLights.totalLightCount; worldLightsVolumes.ResizeVolumeBuffer(totalNumLights); if (totalNumLights == 0) return; using var profilerScope = k_ProfilerMarkerVolume.Auto(); Vector3 camPosWS = hdCamera.mainViewConstants.worldSpaceCameraPos; int realIndex = 0; HDLightRenderDatabase lightEntities = HDLightRenderDatabase.instance; for (int lightIdx = 0; lightIdx < worldLights.hdLightEntityArray.Length; ++lightIdx) { var visibleLight = worldLights.hdLightEntityArray[lightIdx]; int dataIndex = lightEntities.GetEntityDataIndex(visibleLight.light); HDAdditionalLightData currentLight = lightEntities.hdAdditionalLightData[dataIndex]; // When the user deletes a light source in the editor, there is a single frame where the light is null before the collection of light in the scene is triggered // the workaround for this is simply to not add it if it is null for that invalid frame if (currentLight != null) { // The light is guaranteed to be there since it was checked when gathering the light list Light light = currentLight.gameObject.GetComponent(); var lightType = currentLight.legacyLight.type; // Reserve space in the cookie atlas renderPipeline.ReserveCookieAtlasTexture(currentLight, light, lightType); // Grab the light range float lightRange = light.range; // User defined flags ref uint lightFlags = ref worldLightsVolumes.GetFlagsRef(realIndex); lightFlags = flagFunc(hdCamera, currentLight, light) | (currentLight.gameObject.activeInHierarchy ? 1u : 0u); ref WorldLightVolume lightVolume = ref worldLightsVolumes.GetRef(realIndex); bool isAreaLight = lightType.IsArea(); bool isBoxLight = lightType == LightType.Box; // Common volume data lightVolume.flags = lightFlags; lightVolume.lightIndex = (uint)lightIdx; lightVolume.range = visibleLight.bounds.extents; lightVolume.position = visibleLight.bounds.center; lightVolume.lightType = isAreaLight ? 1u : 0u; lightVolume.shape = isAreaLight || isBoxLight ? 1u : 0u; worldLightsVolumes.bounds.Encapsulate(visibleLight.bounds); realIndex++; } } // Set Env Light volume data to the CPU buffer for (int lightIdx = 0; lightIdx < worldLights.reflectionProbeArray.Count; ++lightIdx) { HDProbe currentEnvLight = worldLights.reflectionProbeArray[lightIdx]; // Compute the camera relative position Vector3 probePositionRWS = currentEnvLight.influenceToWorld.GetColumn(3); if (ShaderConfig.s_CameraRelativeRendering != 0) probePositionRWS -= camPosWS; ref WorldLightVolume lightVolume = ref worldLightsVolumes.GetRef(realIndex); if (currentEnvLight.influenceVolume.shape == InfluenceShape.Sphere) { lightVolume.shape = 0; lightVolume.range = new Vector3(currentEnvLight.influenceVolume.sphereRadius, currentEnvLight.influenceVolume.sphereRadius, currentEnvLight.influenceVolume.sphereRadius); lightVolume.position = probePositionRWS; } else { lightVolume.shape = 1; lightVolume.range = currentEnvLight.influenceVolume.boxSize * 0.5f; lightVolume.position = probePositionRWS; } // User defined flags // TODO-WL: Hard coded flags for Env lights ref uint lightFlags = ref worldLightsVolumes.GetFlagsRef(realIndex); lightFlags = lightVolume.flags = (uint)WorldLightFlags.Raytracing | (currentEnvLight.gameObject.activeInHierarchy ? 1u : 0u); lightVolume.lightIndex = (uint)lightIdx; lightVolume.lightType = 2; worldLightsVolumes.bounds.Encapsulate(new Bounds(probePositionRWS, lightVolume.range)); realIndex++; } // Add Decal data to m_lightVolumesCPUArray for (int decalIdx = 0; decalIdx < worldLights.decalCount; ++decalIdx) { ref WorldLightVolume lightVolume = ref worldLightsVolumes.GetRef(realIndex); // Decal projectors are box shaped lightVolume.shape = 1; // Compute the camera relative position Vector3 decalPositionRWS = DecalSystem.instance.GetClusteredDecalPosition(decalIdx); if (ShaderConfig.s_CameraRelativeRendering != 0) decalPositionRWS -= camPosWS; lightVolume.position = decalPositionRWS; lightVolume.range = DecalSystem.instance.GetClusteredDecalRange(decalIdx); lightVolume.lightIndex = (uint)decalIdx; lightVolume.lightType = 3; // TODO-WL: Hard coded flags for Decals ref uint lightFlags = ref worldLightsVolumes.GetFlagsRef(realIndex); lightFlags = lightVolume.flags = (uint)WorldLightFlags.Raytracing |(uint)WorldLightFlags.Pathtracing | (uint)WorldLightFlags.Active; realIndex++; } worldLightsVolumes.PushToGpu(); } static readonly ProfilerMarker k_ProfilerMarkerCollect = new ("WorldLightManager.Collect"); static readonly ProfilerMarker k_ProfilerMarkerBuild = new ("WorldLightManager.Build"); static readonly ProfilerMarker k_ProfilerMarkerVolume = new ("WorldLightManager.Volumes"); internal const int SizeAlignment = 32; internal static Vector3 minBounds = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue); internal static Vector3 maxBounds = new Vector3(-float.MaxValue, -float.MaxValue, -float.MaxValue); internal static LightData s_defaultLightData = new LightData(); internal static EnvLightData s_defaultEnvLightData = new EnvLightData(); private static void OOBBToAABBBounds(Vector3 centerWS, Vector3 extents, Vector3 up, Vector3 right, Vector3 forward, ref Bounds bounds) { // Reset the bounds of the AABB bounds.min = minBounds; bounds.max = maxBounds; // Push the 8 corners of the oobb into the AABB bounds.Encapsulate(centerWS + right * extents.x + up * extents.y + forward * extents.z); bounds.Encapsulate(centerWS + right * extents.x + up * extents.y - forward * extents.z); bounds.Encapsulate(centerWS + right * extents.x - up * extents.y + forward * extents.z); bounds.Encapsulate(centerWS + right * extents.x - up * extents.y - forward * extents.z); bounds.Encapsulate(centerWS - right * extents.x + up * extents.y + forward * extents.z); bounds.Encapsulate(centerWS - right * extents.x + up * extents.y - forward * extents.z); bounds.Encapsulate(centerWS - right * extents.x - up * extents.y + forward * extents.z); bounds.Encapsulate(centerWS - right * extents.x - up * extents.y - forward * extents.z); } } } // namespace UnityEngine.Rendering.HighDefinition