using UnityEngine.Experimental.Rendering; namespace UnityEngine.Rendering.HighDefinition { class DebugLightVolumes { // Material used to blit the output texture into the camera render target Material m_Blit; // Material used to render the light volumes Material m_DebugLightVolumeMaterial; // Material to resolve the light volume textures ComputeShader m_DebugLightVolumeCompute; int m_DebugLightVolumeGradientKernel; int m_DebugLightVolumeColorsKernel; // Texture used to display the gradient Texture2D m_ColorGradientTexture = null; // Shader property ids public static readonly int _ColorShaderID = Shader.PropertyToID("_Color"); public static readonly int _OffsetShaderID = Shader.PropertyToID("_Offset"); public static readonly int _RangeShaderID = Shader.PropertyToID("_Range"); public static readonly int _DebugLightCountBufferShaderID = Shader.PropertyToID("_DebugLightCountBuffer"); public static readonly int _DebugColorAccumulationBufferShaderID = Shader.PropertyToID("_DebugColorAccumulationBuffer"); public static readonly int _DebugLightVolumesTextureShaderID = Shader.PropertyToID("_DebugLightVolumesTexture"); public static readonly int _ColorGradientTextureShaderID = Shader.PropertyToID("_ColorGradientTexture"); public static readonly int _MaxDebugLightCountShaderID = Shader.PropertyToID("_MaxDebugLightCount"); public static readonly int _BorderRadiusShaderID = Shader.PropertyToID("_BorderRadius"); MaterialPropertyBlock m_MaterialProperty = new MaterialPropertyBlock(); public DebugLightVolumes() { } public void InitData(RenderPipelineResources renderPipelineResources) { m_DebugLightVolumeMaterial = CoreUtils.CreateEngineMaterial(renderPipelineResources.shaders.debugLightVolumePS); m_DebugLightVolumeCompute = renderPipelineResources.shaders.debugLightVolumeCS; m_DebugLightVolumeGradientKernel = m_DebugLightVolumeCompute.FindKernel("LightVolumeGradient"); m_DebugLightVolumeColorsKernel = m_DebugLightVolumeCompute.FindKernel("LightVolumeColors"); m_ColorGradientTexture = renderPipelineResources.textures.colorGradient; m_Blit = CoreUtils.CreateEngineMaterial(renderPipelineResources.shaders.blitPS); } public void ReleaseData() { CoreUtils.Destroy(m_Blit); CoreUtils.Destroy(m_DebugLightVolumeMaterial); } public struct RenderLightVolumesParameters { public HDCamera hdCamera; public CullingResults cullResults; public Material debugLightVolumeMaterial; public ComputeShader debugLightVolumeCS; public int debugLightVolumeKernel; public int maxDebugLightCount; public float borderRadius; public Texture2D colorGradientTexture; public bool lightOverlapEnabled; } public RenderLightVolumesParameters PrepareLightVolumeParameters(HDCamera hdCamera, LightingDebugSettings lightDebugSettings, CullingResults cullResults) { var parameters = new RenderLightVolumesParameters(); bool lightOverlapEnabled = CoreUtils.IsLightOverlapDebugEnabled(hdCamera.camera); bool useColorAndEdge = lightDebugSettings.lightVolumeDebugByCategory == LightVolumeDebug.ColorAndEdge || lightOverlapEnabled; parameters.hdCamera = hdCamera; parameters.cullResults = cullResults; parameters.debugLightVolumeMaterial = m_DebugLightVolumeMaterial; parameters.debugLightVolumeCS = m_DebugLightVolumeCompute; parameters.debugLightVolumeKernel = useColorAndEdge ? m_DebugLightVolumeColorsKernel : m_DebugLightVolumeGradientKernel; parameters.maxDebugLightCount = (int)lightDebugSettings.maxDebugLightCount; parameters.borderRadius = lightOverlapEnabled ? 0.5f : 1f; parameters.colorGradientTexture = m_ColorGradientTexture; parameters.lightOverlapEnabled = lightOverlapEnabled; return parameters; } public static void RenderLightVolumes(CommandBuffer cmd, in RenderLightVolumesParameters parameters, RenderTargetIdentifier[] accumulationMRT, // [0] = m_LightCountBuffer, [1] m_ColorAccumulationBuffer RTHandle lightCountBuffer, RTHandle colorAccumulationBuffer, RTHandle debugLightVolumesTexture, RTHandle depthBuffer, RTHandle destination, MaterialPropertyBlock mpb) { if (parameters.lightOverlapEnabled) { // We only need the accumulation buffer, not the color (we only disply the outline of the light shape in this mode). CoreUtils.SetRenderTarget(cmd, accumulationMRT[0], depthBuffer); // The cullresult doesn't contains overlapping lights so we use a custom list foreach (var overlappingHDLight in HDAdditionalLightData.s_overlappingHDLights) { RenderLightVolume(cmd, parameters, overlappingHDLight, overlappingHDLight.legacyLight, mpb); } } else { // Set the render target array CoreUtils.SetRenderTarget(cmd, accumulationMRT, depthBuffer); // First of all let's do the regions for the light sources (we only support Punctual and Area) int numLights = parameters.cullResults.visibleLights.Length; for (int lightIdx = 0; lightIdx < numLights; ++lightIdx) { // Let's build the light's bounding sphere matrix Light currentLegacyLight = parameters.cullResults.visibleLights[lightIdx].light; if (currentLegacyLight == null) continue; HDAdditionalLightData currentHDRLight = currentLegacyLight.GetComponent(); if (currentHDRLight == null) continue; RenderLightVolume(cmd, parameters, currentHDRLight, currentLegacyLight, mpb); } // When we enable the light overlap mode we hide probes as they can't be baked in shadow masks if (!parameters.lightOverlapEnabled) { // Now let's do the same but for reflection probes int numProbes = parameters.cullResults.visibleReflectionProbes.Length; for (int probeIdx = 0; probeIdx < numProbes; ++probeIdx) { // Let's build the light's bounding sphere matrix ReflectionProbe currentLegacyProbe = parameters.cullResults.visibleReflectionProbes[probeIdx].reflectionProbe; HDAdditionalReflectionData currentHDProbe = currentLegacyProbe.GetComponent(); if (!currentHDProbe) continue; MaterialPropertyBlock m_MaterialProperty = new MaterialPropertyBlock(); Mesh targetMesh = null; if (currentHDProbe.influenceVolume.shape == InfluenceShape.Sphere) { m_MaterialProperty.SetVector(_RangeShaderID, new Vector3(currentHDProbe.influenceVolume.sphereRadius, currentHDProbe.influenceVolume.sphereRadius, currentHDProbe.influenceVolume.sphereRadius)); targetMesh = DebugShapes.instance.RequestSphereMesh(); } else { m_MaterialProperty.SetVector(_RangeShaderID, new Vector3(currentHDProbe.influenceVolume.boxSize.x, currentHDProbe.influenceVolume.boxSize.y, currentHDProbe.influenceVolume.boxSize.z)); targetMesh = DebugShapes.instance.RequestBoxMesh(); } m_MaterialProperty.SetColor(_ColorShaderID, new Color(1.0f, 1.0f, 0.0f, 1.0f)); m_MaterialProperty.SetVector(_OffsetShaderID, new Vector3(0, 0, 0)); Matrix4x4 positionMat = Matrix4x4.Translate(currentLegacyProbe.transform.position); cmd.DrawMesh(targetMesh, positionMat, parameters.debugLightVolumeMaterial, 0, 0, m_MaterialProperty); } } } // Set the input params for the compute cmd.SetComputeTextureParam(parameters.debugLightVolumeCS, parameters.debugLightVolumeKernel, _DebugLightCountBufferShaderID, lightCountBuffer); cmd.SetComputeTextureParam(parameters.debugLightVolumeCS, parameters.debugLightVolumeKernel, _DebugColorAccumulationBufferShaderID, colorAccumulationBuffer); cmd.SetComputeTextureParam(parameters.debugLightVolumeCS, parameters.debugLightVolumeKernel, _DebugLightVolumesTextureShaderID, debugLightVolumesTexture); cmd.SetComputeTextureParam(parameters.debugLightVolumeCS, parameters.debugLightVolumeKernel, _ColorGradientTextureShaderID, parameters.colorGradientTexture); cmd.SetComputeIntParam(parameters.debugLightVolumeCS, _MaxDebugLightCountShaderID, parameters.maxDebugLightCount); cmd.SetComputeFloatParam(parameters.debugLightVolumeCS, _BorderRadiusShaderID, parameters.borderRadius); // Texture dimensions int texWidth = parameters.hdCamera.actualWidth; // m_ColorAccumulationBuffer.rt.width; int texHeight = parameters.hdCamera.actualHeight; // m_ColorAccumulationBuffer.rt.width; // Dispatch the compute int lightVolumesTileSize = 8; int numTilesX = (texWidth + (lightVolumesTileSize - 1)) / lightVolumesTileSize; int numTilesY = (texHeight + (lightVolumesTileSize - 1)) / lightVolumesTileSize; cmd.DispatchCompute(parameters.debugLightVolumeCS, parameters.debugLightVolumeKernel, numTilesX, numTilesY, parameters.hdCamera.viewCount); // Blit this into the camera target CoreUtils.SetRenderTarget(cmd, destination); mpb.SetTexture(HDShaderIDs._BlitTexture, debugLightVolumesTexture); cmd.DrawProcedural(Matrix4x4.identity, parameters.debugLightVolumeMaterial, 1, MeshTopology.Triangles, 3, 1, mpb); } static void RenderLightVolume( CommandBuffer cmd, in RenderLightVolumesParameters parameters, HDAdditionalLightData currentHDRLight, Light currentLegacyLight, MaterialPropertyBlock mpb) { Matrix4x4 positionMat = Matrix4x4.Translate(currentLegacyLight.transform.position); switch (currentHDRLight.ComputeLightType(currentLegacyLight)) { case HDLightType.Point: mpb.SetColor(_ColorShaderID, new Color(0.0f, 0.5f, 0.0f, 1.0f)); mpb.SetVector(_OffsetShaderID, new Vector3(0, 0, 0)); mpb.SetVector(_RangeShaderID, new Vector3(currentLegacyLight.range, currentLegacyLight.range, currentLegacyLight.range)); cmd.DrawMesh(DebugShapes.instance.RequestSphereMesh(), positionMat, parameters.debugLightVolumeMaterial, 0, 0, mpb); break; case HDLightType.Spot: switch (currentHDRLight.spotLightShape) { case SpotLightShape.Cone: float bottomRadius = Mathf.Tan(currentLegacyLight.spotAngle * Mathf.PI / 360.0f) * currentLegacyLight.range; mpb.SetColor(_ColorShaderID, new Color(1.0f, 0.5f, 0.0f, 1.0f)); mpb.SetVector(_RangeShaderID, new Vector3(bottomRadius, bottomRadius, currentLegacyLight.range)); mpb.SetVector(_OffsetShaderID, new Vector3(0, 0, 0)); cmd.DrawMesh(DebugShapes.instance.RequestConeMesh(), currentLegacyLight.gameObject.transform.localToWorldMatrix, parameters.debugLightVolumeMaterial, 0, 0, mpb); break; case SpotLightShape.Box: mpb.SetColor(_ColorShaderID, new Color(1.0f, 0.5f, 0.0f, 1.0f)); mpb.SetVector(_RangeShaderID, new Vector3(currentHDRLight.shapeWidth, currentHDRLight.shapeHeight, currentLegacyLight.range)); mpb.SetVector(_OffsetShaderID, new Vector3(0, 0, currentLegacyLight.range / 2.0f)); cmd.DrawMesh(DebugShapes.instance.RequestBoxMesh(), currentLegacyLight.gameObject.transform.localToWorldMatrix, parameters.debugLightVolumeMaterial, 0, 0, mpb); break; case SpotLightShape.Pyramid: float bottomWidth = Mathf.Tan(currentLegacyLight.spotAngle * Mathf.PI / 360.0f) * currentLegacyLight.range; mpb.SetColor(_ColorShaderID, new Color(1.0f, 0.5f, 0.0f, 1.0f)); mpb.SetVector(_RangeShaderID, new Vector3(currentHDRLight.aspectRatio * bottomWidth * 2, bottomWidth * 2, currentLegacyLight.range)); mpb.SetVector(_OffsetShaderID, new Vector3(0, 0, 0)); cmd.DrawMesh(DebugShapes.instance.RequestPyramidMesh(), currentLegacyLight.gameObject.transform.localToWorldMatrix, parameters.debugLightVolumeMaterial, 0, 0, mpb); break; } break; case HDLightType.Area: switch (currentHDRLight.areaLightShape) { case AreaLightShape.Rectangle: mpb.SetColor(_ColorShaderID, new Color(0.0f, 1.0f, 1.0f, 1.0f)); mpb.SetVector(_OffsetShaderID, new Vector3(0, 0, 0)); mpb.SetVector(_RangeShaderID, new Vector3(currentLegacyLight.range, currentLegacyLight.range, currentLegacyLight.range)); cmd.DrawMesh(DebugShapes.instance.RequestSphereMesh(), positionMat, parameters.debugLightVolumeMaterial, 0, 0, mpb); break; case AreaLightShape.Tube: mpb.SetColor(_ColorShaderID, new Color(1.0f, 0.0f, 0.5f, 1.0f)); mpb.SetVector(_OffsetShaderID, new Vector3(0, 0, 0)); mpb.SetVector(_RangeShaderID, new Vector3(currentLegacyLight.range, currentLegacyLight.range, currentLegacyLight.range)); cmd.DrawMesh(DebugShapes.instance.RequestSphereMesh(), positionMat, parameters.debugLightVolumeMaterial, 0, 0, mpb); break; default: break; } break; } } } }