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

319 lines
18 KiB
C#

using UnityEngine.Experimental.Rendering;
using UnityEngine.Experimental.Rendering.RenderGraphModule;
namespace UnityEngine.Rendering.HighDefinition
{
class SSGIDenoiser
{
// Resources used for the denoiser
ComputeShader m_SSGIDenoiserCS;
int m_SpatialFilterHalfKernel;
int m_SpatialFilterKernel;
int m_TemporalFilterHalfKernel;
int m_TemporalFilterKernel;
int m_CopyHistory;
static Color s_CoCgAccClearColor = new Color(0.501960784f, 0.501960784f, 0.0f, 0.0f);
public SSGIDenoiser()
{
}
public void Init(RenderPipelineResources rpResources)
{
// Keep track of the resources
m_SSGIDenoiserCS = rpResources.shaders.ssGIDenoiserCS;
// Fetch the kernels we are going to require
m_SpatialFilterHalfKernel = m_SSGIDenoiserCS.FindKernel("SpatialFilterHalf");
m_SpatialFilterKernel = m_SSGIDenoiserCS.FindKernel("SpatialFilter");
// Fetch the kernels we are going to require
m_TemporalFilterHalfKernel = m_SSGIDenoiserCS.FindKernel("TemporalFilterHalf");
m_TemporalFilterKernel = m_SSGIDenoiserCS.FindKernel("TemporalFilter");
m_CopyHistory = m_SSGIDenoiserCS.FindKernel("CopyHistory");
}
public void Release()
{
}
void EvaluateDispatchParameters(HDCamera hdCamera, bool halfResolution, int tileSize,
out int numTilesX, out int numTilesY, out Vector4 halfScreenSize)
{
if (halfResolution)
{
// Fetch texture dimensions
int texWidth = hdCamera.actualWidth / 2;
int texHeight = hdCamera.actualHeight / 2;
numTilesX = (texWidth + (tileSize - 1)) / tileSize;
numTilesY = (texHeight + (tileSize - 1)) / tileSize;
halfScreenSize = new Vector4(texWidth, texHeight, 1.0f / texWidth, 1.0f / texHeight);
}
else
{
// Fetch texture dimensions
int texWidth = hdCamera.actualWidth;
int texHeight = hdCamera.actualHeight;
numTilesX = (texWidth + (tileSize - 1)) / tileSize;
numTilesY = (texHeight + (tileSize - 1)) / tileSize;
halfScreenSize = new Vector4(texWidth / 2, texHeight / 2, 0.5f / texWidth, 0.5f / texHeight);
}
}
RTHandle IndirectDiffuseHistoryBufferAllocatorFunction(string viewName, int frameIndex, RTHandleSystem rtHandleSystem)
{
return rtHandleSystem.Alloc(Vector2.one, TextureXR.slices, colorFormat: GraphicsFormat.R16G16B16A16_SFloat, dimension: TextureXR.dimension,
enableRandomWrite: true, useMipMap: false, autoGenerateMips: false,
name: string.Format("IndirectDiffuseHistoryBuffer{0}", frameIndex));
}
struct SSGIDenoiserParameters
{
// Camera Parameters
public int numTilesX;
public int numTilesY;
public int viewCount;
public Vector4 halfScreenSize;
public Vector2 firstMipOffset;
// Denoising parameters
public int filterRadius;
public bool halfResolution;
public float historyValidity;
public bool historyNeedsClear;
public float pixelSpreadTangent;
// Shader
public ComputeShader ssgiDenoiserCS;
// Kernels
public int spatialFilterKernel;
public int temporalFilterKernel;
public int copyHistory;
}
SSGIDenoiserParameters PrepareSSGIDenoiserParameters(HDCamera hdCamera, bool halfResolution, float historyValidity, bool historyNeedsClear, HDUtils.PackedMipChainInfo depthMipInfo)
{
var giSettings = hdCamera.volumeStack.GetComponent<UnityEngine.Rendering.HighDefinition.GlobalIllumination>();
SSGIDenoiserParameters parameters = new SSGIDenoiserParameters();
// Compute the dispatch parameters based on if we are half res or not
int tileSize = 8;
EvaluateDispatchParameters(hdCamera, halfResolution, tileSize, out parameters.numTilesX, out parameters.numTilesY, out parameters.halfScreenSize);
parameters.firstMipOffset.Set(HDShadowUtils.Asfloat((uint)depthMipInfo.mipLevelOffsets[1].x), HDShadowUtils.Asfloat((uint)depthMipInfo.mipLevelOffsets[1].y));
parameters.historyValidity = historyValidity;
parameters.viewCount = hdCamera.viewCount;
parameters.pixelSpreadTangent = HDRenderPipeline.GetPixelSpreadTangent(hdCamera.camera.fieldOfView, hdCamera.actualWidth, hdCamera.actualHeight);
// Denoising parameters
parameters.filterRadius = giSettings.filterRadius;
parameters.halfResolution = halfResolution;
parameters.historyValidity = historyValidity;
parameters.historyNeedsClear = historyNeedsClear;
// Compute shader
parameters.ssgiDenoiserCS = m_SSGIDenoiserCS;
// Kernels
parameters.spatialFilterKernel = halfResolution ? m_SpatialFilterHalfKernel : m_SpatialFilterKernel;
parameters.temporalFilterKernel = halfResolution ? m_TemporalFilterHalfKernel : m_TemporalFilterKernel;
parameters.copyHistory = m_CopyHistory;
return parameters;
}
struct SSGIDenoiserResources
{
// Input Buffers
public RTHandle depthTexture;
public RTHandle normalBuffer;
public RTHandle motionVectorsBuffer;
// History Buffer
public RTHandle indirectDiffuseHistory0;
public RTHandle indirectDiffuseHistory1;
public RTHandle historyDepthBuffer;
// Intermediate buffer
public RTHandle intermediateBuffer0;
public RTHandle intermediateBuffer1;
// In-output Buffer
public RTHandle inputOutputBuffer0;
public RTHandle inputOutputBuffer1;
}
static void Denoise(CommandBuffer cmd, SSGIDenoiserParameters parameters, SSGIDenoiserResources resources)
{
if (resources.historyDepthBuffer == null)
return;
// Bind the input scalars
cmd.SetComputeVectorParam(parameters.ssgiDenoiserCS, HDShaderIDs._DepthPyramidFirstMipLevelOffset, parameters.firstMipOffset);
cmd.SetComputeIntParam(parameters.ssgiDenoiserCS, HDShaderIDs._IndirectDiffuseSpatialFilter, parameters.filterRadius);
cmd.SetComputeFloatParam(parameters.ssgiDenoiserCS, HDShaderIDs._PixelSpreadAngleTangent, parameters.pixelSpreadTangent);
// Inject half screen size if required
if (parameters.halfResolution)
cmd.SetComputeVectorParam(parameters.ssgiDenoiserCS, HDShaderIDs._HalfScreenSize, parameters.halfScreenSize);
// Bind the input buffers
cmd.SetComputeTextureParam(parameters.ssgiDenoiserCS, parameters.spatialFilterKernel, HDShaderIDs._DepthTexture, resources.depthTexture);
cmd.SetComputeTextureParam(parameters.ssgiDenoiserCS, parameters.spatialFilterKernel, HDShaderIDs._NormalBufferTexture, resources.normalBuffer);
cmd.SetComputeTextureParam(parameters.ssgiDenoiserCS, parameters.spatialFilterKernel, HDShaderIDs._InputNoisyBuffer0, resources.inputOutputBuffer0);
cmd.SetComputeTextureParam(parameters.ssgiDenoiserCS, parameters.spatialFilterKernel, HDShaderIDs._InputNoisyBuffer1, resources.inputOutputBuffer1);
// Bind the output buffer
cmd.SetComputeTextureParam(parameters.ssgiDenoiserCS, parameters.spatialFilterKernel, HDShaderIDs._OutputFilteredBuffer0, resources.intermediateBuffer0);
cmd.SetComputeTextureParam(parameters.ssgiDenoiserCS, parameters.spatialFilterKernel, HDShaderIDs._OutputFilteredBuffer1, resources.intermediateBuffer1);
// Do the spatial pass
cmd.DispatchCompute(parameters.ssgiDenoiserCS, parameters.spatialFilterKernel, parameters.numTilesX, parameters.numTilesY, parameters.viewCount);
// Grab the history buffer
if (parameters.historyNeedsClear)
{
// clear it to black if this is the first pass to avoid nans
CoreUtils.SetRenderTarget(cmd, resources.indirectDiffuseHistory0, ClearFlag.Color, Color.black);
CoreUtils.SetRenderTarget(cmd, resources.indirectDiffuseHistory1, ClearFlag.Color, s_CoCgAccClearColor);
}
// Bind the input buffers
cmd.SetComputeTextureParam(parameters.ssgiDenoiserCS, parameters.temporalFilterKernel, HDShaderIDs._DepthTexture, resources.depthTexture);
cmd.SetComputeTextureParam(parameters.ssgiDenoiserCS, parameters.temporalFilterKernel, HDShaderIDs._NormalBufferTexture, resources.normalBuffer);
cmd.SetComputeTextureParam(parameters.ssgiDenoiserCS, parameters.temporalFilterKernel, HDShaderIDs._CameraMotionVectorsTexture, resources.motionVectorsBuffer);
cmd.SetComputeFloatParam(parameters.ssgiDenoiserCS, HDShaderIDs._HistoryValidity, parameters.historyValidity);
if (parameters.halfResolution)
{
cmd.SetComputeTextureParam(parameters.ssgiDenoiserCS, parameters.temporalFilterKernel, HDShaderIDs._HistoryDepthTexture, resources.historyDepthBuffer);
cmd.SetComputeVectorParam(parameters.ssgiDenoiserCS, HDShaderIDs._DepthPyramidFirstMipLevelOffset, parameters.firstMipOffset);
}
else
{
cmd.SetComputeTextureParam(parameters.ssgiDenoiserCS, parameters.temporalFilterKernel, HDShaderIDs._HistoryDepthTexture, resources.historyDepthBuffer);
}
cmd.SetComputeTextureParam(parameters.ssgiDenoiserCS, parameters.temporalFilterKernel, HDShaderIDs._HistoryBuffer0, resources.indirectDiffuseHistory0);
cmd.SetComputeTextureParam(parameters.ssgiDenoiserCS, parameters.temporalFilterKernel, HDShaderIDs._HistoryBuffer1, resources.indirectDiffuseHistory1);
cmd.SetComputeTextureParam(parameters.ssgiDenoiserCS, parameters.temporalFilterKernel, HDShaderIDs._InputNoisyBuffer0, resources.intermediateBuffer0);
cmd.SetComputeTextureParam(parameters.ssgiDenoiserCS, parameters.temporalFilterKernel, HDShaderIDs._InputNoisyBuffer1, resources.intermediateBuffer1);
// Bind the output buffer
cmd.SetComputeTextureParam(parameters.ssgiDenoiserCS, parameters.temporalFilterKernel, HDShaderIDs._OutputFilteredBuffer0, resources.inputOutputBuffer0);
cmd.SetComputeTextureParam(parameters.ssgiDenoiserCS, parameters.temporalFilterKernel, HDShaderIDs._OutputFilteredBuffer1, resources.inputOutputBuffer1);
// Do the temporal pass
cmd.DispatchCompute(parameters.ssgiDenoiserCS, parameters.temporalFilterKernel, parameters.numTilesX, parameters.numTilesY, parameters.viewCount);
// Copy the new version into the history buffer
cmd.SetComputeTextureParam(parameters.ssgiDenoiserCS, parameters.copyHistory, HDShaderIDs._InputNoisyBuffer0, resources.inputOutputBuffer0);
cmd.SetComputeTextureParam(parameters.ssgiDenoiserCS, parameters.copyHistory, HDShaderIDs._InputNoisyBuffer1, resources.inputOutputBuffer1);
cmd.SetComputeTextureParam(parameters.ssgiDenoiserCS, parameters.copyHistory, HDShaderIDs._OutputFilteredBuffer0, resources.indirectDiffuseHistory0);
cmd.SetComputeTextureParam(parameters.ssgiDenoiserCS, parameters.copyHistory, HDShaderIDs._OutputFilteredBuffer1, resources.indirectDiffuseHistory1);
cmd.DispatchCompute(parameters.ssgiDenoiserCS, parameters.copyHistory, parameters.numTilesX, parameters.numTilesY, parameters.viewCount);
}
RTHandle RequestIndirectDiffuseHistory0(HDCamera hdCamera, out bool historyRequireClear)
{
historyRequireClear = false;
RTHandle indirectDiffuseHistory = hdCamera.GetCurrentFrameRT((int)HDCameraFrameHistoryType.RaytracedIndirectDiffuseHF);
if (indirectDiffuseHistory == null)
{
indirectDiffuseHistory = hdCamera.AllocHistoryFrameRT((int)HDCameraFrameHistoryType.RaytracedIndirectDiffuseHF, IndirectDiffuseHistoryBufferAllocatorFunction, 1);
historyRequireClear = true;
}
return indirectDiffuseHistory;
}
RTHandle RequestIndirectDiffuseHistory1(HDCamera hdCamera, out bool historyRequireClear)
{
historyRequireClear = false;
RTHandle indirectDiffuseHistory = hdCamera.GetCurrentFrameRT((int)HDCameraFrameHistoryType.RaytracedIndirectDiffuseLF);
if (indirectDiffuseHistory == null)
{
indirectDiffuseHistory = hdCamera.AllocHistoryFrameRT((int)HDCameraFrameHistoryType.RaytracedIndirectDiffuseLF, IndirectDiffuseHistoryBufferAllocatorFunction, 1);
historyRequireClear = true;
}
return indirectDiffuseHistory;
}
class DenoiseSSGIPassData
{
public SSGIDenoiserParameters parameters;
public TextureHandle depthTexture;
public TextureHandle normalBuffer;
public TextureHandle motionVectorsBuffer;
public TextureHandle indirectDiffuseHistory0;
public TextureHandle indirectDiffuseHistory1;
public TextureHandle historyDepthBuffer;
public TextureHandle intermediateBuffer0;
public TextureHandle intermediateBuffer1;
public TextureHandle inputOutputBuffer0;
public TextureHandle inputOutputBuffer1;
}
public struct SSGIDenoiserOutput
{
public TextureHandle outputBuffer0;
public TextureHandle outputBuffer1;
}
public SSGIDenoiserOutput Denoise(RenderGraph renderGraph, HDCamera hdCamera,
TextureHandle depthPyramid, TextureHandle normalBuffer, TextureHandle motionVectorsBuffer, TextureHandle inputOutputBuffer0, TextureHandle inputOutputBuffer1,
HDUtils.PackedMipChainInfo depthMipInfo, bool halfResolution = false, float historyValidity = 1.0f)
{
using (var builder = renderGraph.AddRenderPass<DenoiseSSGIPassData>("Denoise SSGI", out var passData, ProfilingSampler.Get(HDProfileId.SSGIDenoise)))
{
builder.EnableAsyncCompute(false);
// Input buffers
passData.depthTexture = builder.ReadTexture(depthPyramid);
passData.normalBuffer = builder.ReadTexture(normalBuffer);
passData.motionVectorsBuffer = builder.ReadTexture(motionVectorsBuffer);
// History buffer
bool historyRequireClear = false;
RTHandle indirectDiffuseHistory0 = RequestIndirectDiffuseHistory0(hdCamera, out historyRequireClear);
passData.indirectDiffuseHistory0 = builder.ReadWriteTexture(renderGraph.ImportTexture(indirectDiffuseHistory0));
RTHandle indirectDiffuseHistory1 = RequestIndirectDiffuseHistory1(hdCamera, out historyRequireClear);
passData.indirectDiffuseHistory1 = builder.ReadWriteTexture(renderGraph.ImportTexture(indirectDiffuseHistory1));
var historyDepthBuffer = halfResolution ? hdCamera.GetCurrentFrameRT((int)HDCameraFrameHistoryType.Depth1) : hdCamera.GetCurrentFrameRT((int)HDCameraFrameHistoryType.Depth);
passData.historyDepthBuffer = historyDepthBuffer != null ? builder.ReadTexture(renderGraph.ImportTexture(historyDepthBuffer)) : renderGraph.defaultResources.blackTextureXR;
passData.intermediateBuffer0 = builder.CreateTransientTexture(new TextureDesc(Vector2.one, true, true)
{ colorFormat = GraphicsFormat.R16G16B16A16_SFloat, enableRandomWrite = true, name = "SSGI Denoiser Intermediate0" });
passData.intermediateBuffer1 = builder.CreateTransientTexture(new TextureDesc(Vector2.one, true, true)
{ colorFormat = GraphicsFormat.R16G16B16A16_SFloat, enableRandomWrite = true, name = "SSGI Denoiser Intermediate1" });
passData.inputOutputBuffer0 = builder.ReadWriteTexture(inputOutputBuffer0);
passData.inputOutputBuffer1 = builder.ReadWriteTexture(inputOutputBuffer1);
passData.parameters = PrepareSSGIDenoiserParameters(hdCamera, halfResolution, historyValidity, historyRequireClear, depthMipInfo);
builder.SetRenderFunc(
(DenoiseSSGIPassData data, RenderGraphContext ctx) =>
{
// We need to fill the structure that holds the various resources
SSGIDenoiserResources resources = new SSGIDenoiserResources();
resources.depthTexture = data.depthTexture;
resources.normalBuffer = data.normalBuffer;
resources.motionVectorsBuffer = data.motionVectorsBuffer;
resources.indirectDiffuseHistory0 = data.indirectDiffuseHistory0;
resources.indirectDiffuseHistory1 = data.indirectDiffuseHistory1;
resources.historyDepthBuffer = data.historyDepthBuffer;
resources.intermediateBuffer0 = data.intermediateBuffer0;
resources.intermediateBuffer1 = data.intermediateBuffer1;
resources.inputOutputBuffer0 = data.inputOutputBuffer0;
resources.inputOutputBuffer1 = data.inputOutputBuffer1;
Denoise(ctx.cmd, data.parameters, resources);
});
SSGIDenoiserOutput denoiserOutput = new SSGIDenoiserOutput();
denoiserOutput.outputBuffer0 = inputOutputBuffer0;
denoiserOutput.outputBuffer1 = inputOutputBuffer1;
return denoiserOutput;
}
}
}
}