2021-09-09 20:42:29 -04:00

249 lines
15 KiB
C#

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<HDAdditionalLightData>();
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<HDAdditionalReflectionData>();
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;
}
}
}
}