You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
253 lines
17 KiB
253 lines
17 KiB
using UnityEngine.Experimental.Rendering;
|
|
using UnityEngine.Rendering.RenderGraphModule;
|
|
|
|
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(HDRenderPipeline renderPipeline)
|
|
{
|
|
m_DebugLightVolumeMaterial = CoreUtils.CreateEngineMaterial(renderPipeline.runtimeShaders.debugLightVolumePS);
|
|
m_DebugLightVolumeCompute = renderPipeline.runtimeShaders.debugLightVolumeCS;
|
|
m_DebugLightVolumeGradientKernel = m_DebugLightVolumeCompute.FindKernel("LightVolumeGradient");
|
|
m_DebugLightVolumeColorsKernel = m_DebugLightVolumeCompute.FindKernel("LightVolumeColors");
|
|
m_ColorGradientTexture = renderPipeline.runtimeTextures.colorGradient;
|
|
m_Blit = Blitter.GetBlitMaterial(TextureDimension.Tex2D);
|
|
}
|
|
|
|
public void ReleaseData()
|
|
{
|
|
CoreUtils.Destroy(m_DebugLightVolumeMaterial);
|
|
}
|
|
|
|
class RenderLightVolumesPassData
|
|
{
|
|
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;
|
|
|
|
// Render target that holds the light count in floating points
|
|
public TextureHandle lightCountBuffer;
|
|
// Render target that holds the color accumulated value
|
|
public TextureHandle colorAccumulationBuffer;
|
|
// The output texture of the debug
|
|
public TextureHandle debugLightVolumesTexture;
|
|
// Required depth texture given that we render multiple render targets
|
|
public TextureHandle depthBuffer;
|
|
public TextureHandle destination;
|
|
}
|
|
|
|
public void RenderLightVolumes(RenderGraph renderGraph, LightingDebugSettings lightingDebugSettings, TextureHandle destination, TextureHandle depthBuffer, CullingResults cullResults, HDCamera hdCamera)
|
|
{
|
|
using (var builder = renderGraph.AddUnsafePass<RenderLightVolumesPassData>("LightVolumes", out var passData))
|
|
{
|
|
bool lightOverlapEnabled = CoreUtils.IsLightOverlapDebugEnabled(hdCamera.camera);
|
|
bool useColorAndEdge = lightingDebugSettings.lightVolumeDebugByCategory == LightVolumeDebug.ColorAndEdge || lightOverlapEnabled;
|
|
|
|
passData.hdCamera = hdCamera;
|
|
passData.cullResults = cullResults;
|
|
passData.debugLightVolumeMaterial = m_DebugLightVolumeMaterial;
|
|
passData.debugLightVolumeCS = m_DebugLightVolumeCompute;
|
|
passData.debugLightVolumeKernel = useColorAndEdge ? m_DebugLightVolumeColorsKernel : m_DebugLightVolumeGradientKernel;
|
|
passData.maxDebugLightCount = (int)lightingDebugSettings.maxDebugLightCount;
|
|
passData.borderRadius = lightOverlapEnabled ? 0.5f : 1f;
|
|
passData.colorGradientTexture = m_ColorGradientTexture;
|
|
passData.lightOverlapEnabled = lightOverlapEnabled;
|
|
passData.lightCountBuffer = builder.CreateTransientTexture(new TextureDesc(Vector2.one, true, true)
|
|
{ format = GraphicsFormat.R32_SFloat, clearBuffer = true, clearColor = Color.black, name = "LightVolumeCount" });
|
|
passData.colorAccumulationBuffer = builder.CreateTransientTexture(new TextureDesc(Vector2.one, true, true)
|
|
{ format = GraphicsFormat.R16G16B16A16_SFloat, clearBuffer = true, clearColor = Color.black, name = "LightVolumeColorAccumulation" });
|
|
passData.debugLightVolumesTexture = builder.CreateTransientTexture(new TextureDesc(Vector2.one, true, true)
|
|
{ format = GraphicsFormat.R16G16B16A16_SFloat, clearBuffer = true, clearColor = Color.black, enableRandomWrite = true, name = "LightVolumeDebugLightVolumesTexture" });
|
|
passData.depthBuffer = depthBuffer;
|
|
builder.SetRenderAttachmentDepth(depthBuffer, AccessFlags.ReadWrite);
|
|
passData.destination = destination;
|
|
builder.UseTexture(passData.destination, AccessFlags.Write);
|
|
|
|
builder.SetRenderFunc(
|
|
(RenderLightVolumesPassData data, UnsafeGraphContext ctx) =>
|
|
{
|
|
var natCmd = CommandBufferHelpers.GetNativeCommandBuffer(ctx.cmd);
|
|
var mpb = ctx.renderGraphPool.GetTempMaterialPropertyBlock();
|
|
RenderTargetIdentifier[] mrt = ctx.renderGraphPool.GetTempArray<RenderTargetIdentifier>(2);
|
|
mrt[0] = data.lightCountBuffer;
|
|
mrt[1] = data.colorAccumulationBuffer;
|
|
|
|
if (data.lightOverlapEnabled)
|
|
{
|
|
// We only need the accumulation buffer, not the color (we only display the outline of the light shape in this mode).
|
|
CoreUtils.SetRenderTarget(natCmd, mrt[0], depthBuffer);
|
|
|
|
// The cull result doesn't contains overlapping lights so we use a custom list
|
|
foreach (var overlappingHDLight in HDAdditionalLightData.s_overlappingHDLights)
|
|
{
|
|
RenderLightVolume(natCmd, data.debugLightVolumeMaterial, overlappingHDLight, overlappingHDLight.legacyLight, mpb);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Set the render target array
|
|
CoreUtils.SetRenderTarget(natCmd, mrt, depthBuffer);
|
|
|
|
// First of all let's do the regions for the light sources (we only support Punctual and Area)
|
|
int numLights = data.cullResults.visibleLights.Length;
|
|
for (int lightIdx = 0; lightIdx < numLights; ++lightIdx)
|
|
{
|
|
// Let's build the light's bounding sphere matrix
|
|
Light currentLegacyLight = data.cullResults.visibleLights[lightIdx].light;
|
|
if (currentLegacyLight == null) continue;
|
|
if (!currentLegacyLight.TryGetComponent<HDAdditionalLightData>(out var currentHDRLight)) continue;
|
|
|
|
RenderLightVolume(natCmd, data.debugLightVolumeMaterial, currentHDRLight, currentLegacyLight, mpb);
|
|
}
|
|
|
|
// When we enable the light overlap mode we hide probes as they can't be baked in shadow masks
|
|
if (!data.lightOverlapEnabled)
|
|
{
|
|
// Now let's do the same but for reflection probes
|
|
int numProbes = data.cullResults.visibleReflectionProbes.Length;
|
|
for (int probeIdx = 0; probeIdx < numProbes; ++probeIdx)
|
|
{
|
|
// Let's build the light's bounding sphere matrix
|
|
ReflectionProbe currentLegacyProbe = data.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);
|
|
natCmd.DrawMesh(targetMesh, positionMat, data.debugLightVolumeMaterial, 0, 0, m_MaterialProperty);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set the input params for the compute
|
|
natCmd.SetComputeTextureParam(data.debugLightVolumeCS, data.debugLightVolumeKernel, _DebugLightCountBufferShaderID, data.lightCountBuffer);
|
|
natCmd.SetComputeTextureParam(data.debugLightVolumeCS, data.debugLightVolumeKernel, _DebugColorAccumulationBufferShaderID, data.colorAccumulationBuffer);
|
|
natCmd.SetComputeTextureParam(data.debugLightVolumeCS, data.debugLightVolumeKernel, _DebugLightVolumesTextureShaderID, data.debugLightVolumesTexture);
|
|
natCmd.SetComputeTextureParam(data.debugLightVolumeCS, data.debugLightVolumeKernel, _ColorGradientTextureShaderID, data.colorGradientTexture);
|
|
natCmd.SetComputeIntParam(data.debugLightVolumeCS, _MaxDebugLightCountShaderID, data.maxDebugLightCount);
|
|
natCmd.SetComputeFloatParam(data.debugLightVolumeCS, _BorderRadiusShaderID, data.borderRadius);
|
|
|
|
// Texture dimensions
|
|
int texWidth = data.hdCamera.actualWidth;
|
|
int texHeight = data.hdCamera.actualHeight;
|
|
|
|
// Dispatch the compute
|
|
int lightVolumesTileSize = 8;
|
|
int numTilesX = (texWidth + (lightVolumesTileSize - 1)) / lightVolumesTileSize;
|
|
int numTilesY = (texHeight + (lightVolumesTileSize - 1)) / lightVolumesTileSize;
|
|
natCmd.DispatchCompute(data.debugLightVolumeCS, data.debugLightVolumeKernel, numTilesX, numTilesY, data.hdCamera.viewCount);
|
|
|
|
// Blit this into the camera target
|
|
CoreUtils.SetRenderTarget(natCmd, destination);
|
|
mpb.SetTexture(HDShaderIDs._BlitTexture, data.debugLightVolumesTexture);
|
|
natCmd.DrawProcedural(Matrix4x4.identity, data.debugLightVolumeMaterial, 1, MeshTopology.Triangles, 3, 1, mpb);
|
|
});
|
|
}
|
|
}
|
|
|
|
static void RenderLightVolume(
|
|
CommandBuffer cmd,
|
|
Material debugLightVolumeMaterial,
|
|
HDAdditionalLightData currentHDRLight,
|
|
Light currentLegacyLight,
|
|
MaterialPropertyBlock mpb)
|
|
{
|
|
Matrix4x4 positionMat = Matrix4x4.Translate(currentLegacyLight.transform.position);
|
|
|
|
switch (currentLegacyLight.type)
|
|
{
|
|
case LightType.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, debugLightVolumeMaterial, 0, 0, mpb);
|
|
break;
|
|
case LightType.Spot:
|
|
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, debugLightVolumeMaterial, 0, 0, mpb);
|
|
break;
|
|
case LightType.Box:
|
|
mpb.SetColor(_ColorShaderID, new Color(1.0f, 0.5f, 0.0f, 1.0f));
|
|
mpb.SetVector(_RangeShaderID, new Vector3(currentLegacyLight.areaSize.x, currentLegacyLight.areaSize.y, currentLegacyLight.range));
|
|
mpb.SetVector(_OffsetShaderID, new Vector3(0, 0, currentLegacyLight.range / 2.0f));
|
|
cmd.DrawMesh(DebugShapes.instance.RequestBoxMesh(), currentLegacyLight.gameObject.transform.localToWorldMatrix, debugLightVolumeMaterial, 0, 0, mpb);
|
|
break;
|
|
case LightType.Pyramid:
|
|
float bottomX = Mathf.Tan(currentLegacyLight.spotAngle * Mathf.PI / 360.0f) * currentLegacyLight.range;
|
|
float bottomY = Mathf.Tan(currentLegacyLight.innerSpotAngle * Mathf.PI / 360.0f) * currentLegacyLight.range;
|
|
mpb.SetColor(_ColorShaderID, new Color(1.0f, 0.5f, 0.0f, 1.0f));
|
|
mpb.SetVector(_RangeShaderID, new Vector3(bottomY * 2, bottomX * 2, currentLegacyLight.range));
|
|
mpb.SetVector(_OffsetShaderID, new Vector3(0, 0, 0));
|
|
cmd.DrawMesh(DebugShapes.instance.RequestPyramidMesh(), currentLegacyLight.gameObject.transform.localToWorldMatrix, debugLightVolumeMaterial, 0, 0, mpb);
|
|
break;
|
|
case LightType.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, debugLightVolumeMaterial, 0, 0, mpb);
|
|
break;
|
|
case LightType.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, debugLightVolumeMaterial, 0, 0, mpb);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|