您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
251 行
15 KiB
251 行
15 KiB
using UnityEngine.Rendering;
|
|
using System;
|
|
|
|
namespace UnityEngine.Experimental.Rendering.HDPipeline
|
|
{
|
|
public class SubsurfaceScatteringManager
|
|
{
|
|
// Currently we only support SSSBuffer with one buffer. If the shader code change, it may require to update the shader manager
|
|
public const int k_MaxSSSBuffer = 1;
|
|
|
|
public int sssBufferCount { get { return k_MaxSSSBuffer; } }
|
|
|
|
RTHandleSystem.RTHandle[] m_ColorMRTs = new RTHandleSystem.RTHandle[k_MaxSSSBuffer];
|
|
bool[] m_ExternalBuffer = new bool[k_MaxSSSBuffer];
|
|
|
|
// Disney SSS Model
|
|
ComputeShader m_SubsurfaceScatteringCS;
|
|
int m_SubsurfaceScatteringKernel;
|
|
Material m_CombineLightingPass;
|
|
|
|
RTHandleSystem.RTHandle m_HTile;
|
|
// End Disney SSS Model
|
|
|
|
// Jimenez SSS Model
|
|
Material m_SssVerticalFilterPass;
|
|
Material m_SssHorizontalFilterAndCombinePass;
|
|
// End Jimenez SSS Model
|
|
|
|
// Jimenez need an extra buffer and Disney need one for some platform
|
|
RTHandleSystem.RTHandle m_CameraFilteringBuffer;
|
|
|
|
// This is use to be able to read stencil value in compute shader
|
|
Material m_CopyStencilForSplitLighting;
|
|
|
|
public SubsurfaceScatteringManager()
|
|
{
|
|
}
|
|
|
|
public void InitSSSBuffers(GBufferManager gbufferManager, RenderPipelineSettings settings)
|
|
{
|
|
// TODO: For MSAA, at least initially, we can only support Jimenez, because we can't create MSAA + UAV render targets.
|
|
if (settings.supportForwardOnly)
|
|
{
|
|
// In case of full forward we must allocate the render target for forward SSS (or reuse one already existing)
|
|
// TODO: Provide a way to reuse a render target
|
|
m_ColorMRTs[0] = RTHandles.Alloc(Vector2.one, filterMode: FilterMode.Point, colorFormat: RenderTextureFormat.ARGB32, sRGB: true, name: "SSSBuffer");
|
|
m_ExternalBuffer[0] = false;
|
|
}
|
|
else
|
|
{
|
|
// In case of deferred, we must be in sync with SubsurfaceScattering.hlsl and lit.hlsl files and setup the correct buffers
|
|
m_ColorMRTs[0] = gbufferManager.GetBuffer(0); // Note: This buffer must be sRGB (which is the case with Lit.shader)
|
|
m_ExternalBuffer[0] = true;
|
|
}
|
|
|
|
if (ShaderConfig.k_UseDisneySSS == 0 || NeedTemporarySubsurfaceBuffer())
|
|
{
|
|
// Caution: must be same format as m_CameraSssDiffuseLightingBuffer
|
|
m_CameraFilteringBuffer = RTHandles.Alloc(Vector2.one, filterMode: FilterMode.Point, colorFormat: RenderTextureFormat.RGB111110Float, sRGB: false, enableRandomWrite: true, enableMSAA: true, name: "SSSCameraFiltering"); // Enable UAV
|
|
}
|
|
|
|
// We use 8x8 tiles in order to match the native GCN HTile as closely as possible.
|
|
m_HTile = RTHandles.Alloc(size => new Vector2Int((size.x + 7) / 8, (size.y + 7) / 8), filterMode: FilterMode.Point, colorFormat: RenderTextureFormat.R8, sRGB: false, enableRandomWrite: true, name: "SSSHtile"); // Enable UAV
|
|
}
|
|
|
|
public RTHandleSystem.RTHandle GetSSSBuffer(int index)
|
|
{
|
|
Debug.Assert(index < sssBufferCount);
|
|
return m_ColorMRTs[index];
|
|
}
|
|
|
|
public void Build(HDRenderPipelineAsset hdAsset)
|
|
{
|
|
// Disney SSS (compute + combine)
|
|
string kernelName = hdAsset.renderPipelineSettings.enableUltraQualitySSS ? "SubsurfaceScatteringQualityUltra" : "SubsurfaceScatteringQualityNormal";
|
|
m_SubsurfaceScatteringCS = hdAsset.renderPipelineResources.subsurfaceScatteringCS;
|
|
m_SubsurfaceScatteringKernel = m_SubsurfaceScatteringCS.FindKernel(kernelName);
|
|
m_CombineLightingPass = CoreUtils.CreateEngineMaterial(hdAsset.renderPipelineResources.combineLighting);
|
|
m_CombineLightingPass.SetInt(HDShaderIDs._StencilMask, (int)HDRenderPipeline.StencilBitMask.LightingMask);
|
|
|
|
// Jimenez SSS Model (shader)
|
|
m_SssVerticalFilterPass = CoreUtils.CreateEngineMaterial(hdAsset.renderPipelineResources.subsurfaceScattering);
|
|
m_SssVerticalFilterPass.DisableKeyword("SSS_FILTER_HORIZONTAL_AND_COMBINE");
|
|
m_SssVerticalFilterPass.SetFloat(HDShaderIDs._DstBlend, (float)BlendMode.Zero);
|
|
m_SssVerticalFilterPass.SetInt(HDShaderIDs._StencilMask, (int)HDRenderPipeline.StencilBitMask.LightingMask);
|
|
|
|
m_SssHorizontalFilterAndCombinePass = CoreUtils.CreateEngineMaterial(hdAsset.renderPipelineResources.subsurfaceScattering);
|
|
m_SssHorizontalFilterAndCombinePass.EnableKeyword("SSS_FILTER_HORIZONTAL_AND_COMBINE");
|
|
m_SssHorizontalFilterAndCombinePass.SetFloat(HDShaderIDs._DstBlend, (float)BlendMode.One);
|
|
m_SssHorizontalFilterAndCombinePass.SetInt(HDShaderIDs._StencilMask, (int)HDRenderPipeline.StencilBitMask.LightingMask);
|
|
|
|
m_CopyStencilForSplitLighting = CoreUtils.CreateEngineMaterial(hdAsset.renderPipelineResources.copyStencilBuffer);
|
|
m_CopyStencilForSplitLighting.SetInt(HDShaderIDs._StencilRef, (int)StencilLightingUsage.SplitLighting);
|
|
m_CopyStencilForSplitLighting.SetInt(HDShaderIDs._StencilMask, (int)HDRenderPipeline.StencilBitMask.LightingMask);
|
|
}
|
|
|
|
public void Cleanup()
|
|
{
|
|
CoreUtils.Destroy(m_CombineLightingPass);
|
|
CoreUtils.Destroy(m_SssVerticalFilterPass);
|
|
CoreUtils.Destroy(m_SssHorizontalFilterAndCombinePass);
|
|
CoreUtils.Destroy(m_CopyStencilForSplitLighting);
|
|
|
|
for (int i = 0; i < k_MaxSSSBuffer; ++i)
|
|
{
|
|
if (!m_ExternalBuffer[i])
|
|
{
|
|
RTHandles.Release(m_ColorMRTs[i]);
|
|
}
|
|
}
|
|
|
|
RTHandles.Release(m_CameraFilteringBuffer);
|
|
RTHandles.Release(m_HTile);
|
|
}
|
|
|
|
public void PushGlobalParams(HDCamera hdCamera, CommandBuffer cmd, DiffusionProfileSettings sssParameters)
|
|
{
|
|
// Broadcast SSS parameters to all shaders.
|
|
cmd.SetGlobalInt(HDShaderIDs._EnableSubsurfaceScattering, hdCamera.frameSettings.enableSubsurfaceScattering ? 1 : 0);
|
|
unsafe
|
|
{
|
|
// Warning: Unity is not able to losslessly transfer integers larger than 2^24 to the shader system.
|
|
// Therefore, we bitcast uint to float in C#, and bitcast back to uint in the shader.
|
|
uint texturingModeFlags = sssParameters.texturingModeFlags;
|
|
uint transmissionFlags = sssParameters.transmissionFlags;
|
|
cmd.SetGlobalFloat(HDShaderIDs._TexturingModeFlags, *(float*)&texturingModeFlags);
|
|
cmd.SetGlobalFloat(HDShaderIDs._TransmissionFlags, *(float*)&transmissionFlags);
|
|
}
|
|
cmd.SetGlobalVectorArray(HDShaderIDs._ThicknessRemaps, sssParameters.thicknessRemaps);
|
|
cmd.SetGlobalVectorArray(HDShaderIDs._ShapeParams, sssParameters.shapeParams);
|
|
cmd.SetGlobalVectorArray(HDShaderIDs._HalfRcpVariancesAndWeights, sssParameters.halfRcpVariancesAndWeights);
|
|
// To disable transmission, we simply nullify the transmissionTint
|
|
cmd.SetGlobalVectorArray(HDShaderIDs._TransmissionTintsAndFresnel0, hdCamera.frameSettings.enableTransmission ? sssParameters.transmissionTintsAndFresnel0 : sssParameters.disabledTransmissionTintsAndFresnel0);
|
|
cmd.SetGlobalVectorArray(HDShaderIDs._WorldScales, sssParameters.worldScales);
|
|
}
|
|
|
|
bool NeedTemporarySubsurfaceBuffer()
|
|
{
|
|
// Caution: need to be in sync with SubsurfaceScattering.cs USE_INTERMEDIATE_BUFFER (Can't make a keyword as it is a compute shader)
|
|
// Typed UAV loads from FORMAT_R16G16B16A16_FLOAT is an optional feature of Direct3D 11.
|
|
// Most modern GPUs support it. We can avoid performing a costly copy in this case.
|
|
// TODO: test/implement for other platforms.
|
|
return SystemInfo.graphicsDeviceType != GraphicsDeviceType.PlayStation4 &&
|
|
SystemInfo.graphicsDeviceType != GraphicsDeviceType.XboxOne &&
|
|
SystemInfo.graphicsDeviceType != GraphicsDeviceType.XboxOneD3D12;
|
|
}
|
|
|
|
// Combines specular lighting and diffuse lighting with subsurface scattering.
|
|
public void SubsurfaceScatteringPass(HDCamera hdCamera, CommandBuffer cmd, DiffusionProfileSettings sssParameters,
|
|
RTHandleSystem.RTHandle colorBufferRT, RTHandleSystem.RTHandle diffuseBufferRT, RTHandleSystem.RTHandle depthStencilBufferRT, RTHandleSystem.RTHandle depthTextureRT)
|
|
{
|
|
if (sssParameters == null || !hdCamera.frameSettings.enableSubsurfaceScattering)
|
|
return;
|
|
|
|
// TODO: For MSAA, at least initially, we can only support Jimenez, because we can't
|
|
// create MSAA + UAV render targets.
|
|
|
|
using (new ProfilingSample(cmd, "Subsurface Scattering", CustomSamplerId.SubsurfaceScattering.GetSampler()))
|
|
{
|
|
// For Jimenez we always need an extra buffer, for Disney it depends on platform
|
|
if (ShaderConfig.k_UseDisneySSS == 0 || NeedTemporarySubsurfaceBuffer())
|
|
{
|
|
// Clear the SSS filtering target
|
|
using (new ProfilingSample(cmd, "Clear SSS filtering target", CustomSamplerId.ClearSSSFilteringTarget.GetSampler()))
|
|
{
|
|
HDUtils.SetRenderTarget(cmd, hdCamera, m_CameraFilteringBuffer, ClearFlag.Color, CoreUtils.clearColorAllBlack);
|
|
}
|
|
}
|
|
|
|
if (ShaderConfig.s_UseDisneySSS == 1) // use static here to quiet the compiler warning
|
|
{
|
|
using (new ProfilingSample(cmd, "HTile for SSS", CustomSamplerId.HTileForSSS.GetSampler()))
|
|
{
|
|
// Currently, Unity does not offer a way to access the GCN HTile even on PS4 and Xbox One.
|
|
// Therefore, it's computed in a pixel shader, and optimized to only contain the SSS bit.
|
|
|
|
// Clear the HTile texture. TODO: move this to ClearBuffers(). Clear operations must be batched!
|
|
HDUtils.SetRenderTarget(cmd, hdCamera, m_HTile, ClearFlag.Color, CoreUtils.clearColorAllBlack);
|
|
|
|
HDUtils.SetRenderTarget(cmd, hdCamera, depthStencilBufferRT); // No need for color buffer here
|
|
cmd.SetRandomWriteTarget(1, m_HTile); // This need to be done AFTER SetRenderTarget
|
|
// Generate HTile for the split lighting stencil usage. Don't write into stencil texture (shaderPassId = 2)
|
|
// Use ShaderPassID 1 => "Pass 2 - Export HTILE for stencilRef to output"
|
|
CoreUtils.DrawFullScreen(cmd, m_CopyStencilForSplitLighting, null, 2);
|
|
cmd.ClearRandomWriteTargets();
|
|
}
|
|
|
|
unsafe
|
|
{
|
|
// Warning: Unity is not able to losslessly transfer integers larger than 2^24 to the shader system.
|
|
// Therefore, we bitcast uint to float in C#, and bitcast back to uint in the shader.
|
|
uint texturingModeFlags = sssParameters.texturingModeFlags;
|
|
cmd.SetComputeFloatParam(m_SubsurfaceScatteringCS, HDShaderIDs._TexturingModeFlags, *(float*)&texturingModeFlags);
|
|
}
|
|
|
|
cmd.SetComputeVectorArrayParam(m_SubsurfaceScatteringCS, HDShaderIDs._WorldScales, sssParameters.worldScales);
|
|
cmd.SetComputeVectorArrayParam(m_SubsurfaceScatteringCS, HDShaderIDs._FilterKernels, sssParameters.filterKernels);
|
|
cmd.SetComputeVectorArrayParam(m_SubsurfaceScatteringCS, HDShaderIDs._ShapeParams, sssParameters.shapeParams);
|
|
|
|
cmd.SetComputeTextureParam(m_SubsurfaceScatteringCS, m_SubsurfaceScatteringKernel, HDShaderIDs._DepthTexture, depthTextureRT);
|
|
cmd.SetComputeTextureParam(m_SubsurfaceScatteringCS, m_SubsurfaceScatteringKernel, HDShaderIDs._SSSHTile, m_HTile);
|
|
cmd.SetComputeTextureParam(m_SubsurfaceScatteringCS, m_SubsurfaceScatteringKernel, HDShaderIDs._IrradianceSource, diffuseBufferRT);
|
|
|
|
for (int i = 0; i < sssBufferCount; ++i)
|
|
{
|
|
cmd.SetComputeTextureParam(m_SubsurfaceScatteringCS, m_SubsurfaceScatteringKernel, HDShaderIDs._SSSBufferTexture[i], GetSSSBuffer(i));
|
|
}
|
|
|
|
if (NeedTemporarySubsurfaceBuffer())
|
|
{
|
|
cmd.SetComputeTextureParam(m_SubsurfaceScatteringCS, m_SubsurfaceScatteringKernel, HDShaderIDs._CameraFilteringBuffer, m_CameraFilteringBuffer);
|
|
|
|
// Perform the SSS filtering pass which fills 'm_CameraFilteringBufferRT'.
|
|
cmd.DispatchCompute(m_SubsurfaceScatteringCS, m_SubsurfaceScatteringKernel, ((int)hdCamera.screenSize.x + 15) / 16, ((int)hdCamera.screenSize.y + 15) / 16, 1);
|
|
|
|
cmd.SetGlobalTexture(HDShaderIDs._IrradianceSource, m_CameraFilteringBuffer); // Cannot set a RT on a material
|
|
|
|
// Additively blend diffuse and specular lighting into 'm_CameraColorBufferRT'.
|
|
CoreUtils.DrawFullScreen(cmd, m_CombineLightingPass, colorBufferRT, depthStencilBufferRT);
|
|
}
|
|
else
|
|
{
|
|
cmd.SetComputeTextureParam(m_SubsurfaceScatteringCS, m_SubsurfaceScatteringKernel, HDShaderIDs._CameraColorTexture, colorBufferRT);
|
|
|
|
// Perform the SSS filtering pass which performs an in-place update of 'colorBuffer'.
|
|
cmd.DispatchCompute(m_SubsurfaceScatteringCS, m_SubsurfaceScatteringKernel, ((int)hdCamera.screenSize.x + 15) / 16, ((int)hdCamera.screenSize.y + 15) / 16, 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < sssBufferCount; ++i)
|
|
{
|
|
cmd.SetGlobalTexture(HDShaderIDs._SSSBufferTexture[i], GetSSSBuffer(i));
|
|
}
|
|
|
|
cmd.SetGlobalTexture(HDShaderIDs._IrradianceSource, diffuseBufferRT); // Cannot set a RT on a material
|
|
m_SssVerticalFilterPass.SetVectorArray(HDShaderIDs._FilterKernelsBasic, sssParameters.filterKernelsBasic);
|
|
m_SssVerticalFilterPass.SetVectorArray(HDShaderIDs._HalfRcpWeightedVariances, sssParameters.halfRcpWeightedVariances);
|
|
// Perform the vertical SSS filtering pass which fills 'm_CameraFilteringBufferRT'.
|
|
CoreUtils.DrawFullScreen(cmd, m_SssVerticalFilterPass, m_CameraFilteringBuffer, depthStencilBufferRT);
|
|
|
|
cmd.SetGlobalTexture(HDShaderIDs._IrradianceSource, m_CameraFilteringBuffer); // Cannot set a RT on a material
|
|
m_SssHorizontalFilterAndCombinePass.SetVectorArray(HDShaderIDs._FilterKernelsBasic, sssParameters.filterKernelsBasic);
|
|
m_SssHorizontalFilterAndCombinePass.SetVectorArray(HDShaderIDs._HalfRcpWeightedVariances, sssParameters.halfRcpWeightedVariances);
|
|
// Perform the horizontal SSS filtering pass, and combine diffuse and specular lighting into 'm_CameraColorBufferRT'.
|
|
CoreUtils.DrawFullScreen(cmd, m_SssHorizontalFilterAndCombinePass, colorBufferRT, depthStencilBufferRT);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|