浏览代码

Merge pull request #134 from EvgeniiG/master

Improve SSS bilateral filtering and add support for multiple kernels
/Branch_Batching2
GitHub 8 年前
当前提交
3f6a8195
共有 9 个文件被更改,包括 133 次插入93 次删除
  1. 51
      Assets/ScriptableRenderLoop/HDRenderPipeline/HDRenderPipeline.cs
  2. 2
      Assets/ScriptableRenderLoop/HDRenderPipeline/Lighting/Resources/Deferred.shader
  3. 4
      Assets/ScriptableRenderLoop/HDRenderPipeline/Lighting/TilePass/TilePass.cs
  4. 4
      Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/Lit.shader
  5. 4
      Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/LitTessellation.shader
  6. 94
      Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/Resources/CombineSubsurfaceScattering.shader
  7. 54
      Assets/ScriptableRenderLoop/HDRenderPipeline/SceneSettings/SubsurfaceScatteringParameters.cs
  8. 3
      Assets/ScriptableRenderLoop/HDRenderPipeline/Utilities.cs
  9. 10
      Assets/ScriptableRenderLoop/ShaderLibrary/Common.hlsl

51
Assets/ScriptableRenderLoop/HDRenderPipeline/HDRenderPipeline.cs


if (localSssParameters == null)
{
localSssParameters = new SubsurfaceScatteringParameters();
localSssParameters = CreateInstance<SubsurfaceScatteringParameters>();
}
return localSssParameters;

public Vector4 screenSize;
public Matrix4x4 viewProjectionMatrix;
public Matrix4x4 invViewProjectionMatrix;
public Matrix4x4 invProjectionMatrix;
}
public class GBufferManager

// Various set of material use in render loop
readonly Material m_DebugViewMaterialGBuffer;
readonly Material m_CombineSubsurfaceScattering;
readonly Material m_FilterSubsurfaceScattering;
readonly Material m_FilterAndCombineSubsurfaceScattering;
readonly Material m_DebugDisplayShadowMap;
// Various buffer

m_DebugViewMaterialGBuffer = Utilities.CreateEngineMaterial("Hidden/HDRenderPipeline/DebugViewMaterialGBuffer");
m_CombineSubsurfaceScattering = Utilities.CreateEngineMaterial("Hidden/HDRenderPipeline/CombineSubsurfaceScattering");
m_FilterSubsurfaceScattering = Utilities.CreateEngineMaterial("Hidden/HDRenderPipeline/CombineSubsurfaceScattering");
m_FilterSubsurfaceScattering.DisableKeyword("FILTER_HORIZONTAL");
m_FilterSubsurfaceScattering.SetFloat("_DstBlend", (float)BlendMode.Zero);
m_FilterAndCombineSubsurfaceScattering = Utilities.CreateEngineMaterial("Hidden/HDRenderPipeline/CombineSubsurfaceScattering");
m_FilterSubsurfaceScattering.EnableKeyword("FILTER_HORIZONTAL");
m_FilterAndCombineSubsurfaceScattering.SetFloat("_DstBlend", (float)BlendMode.One);
m_DebugDisplayShadowMap = Utilities.CreateEngineMaterial("Hidden/HDRenderPipeline/DebugDisplayShadowMap");
m_ShadowPass = new ShadowRenderPass(owner.shadowSettings);

// Currently, forward-rendered objects do not output split lighting required for the SSS pass.
if (debugParameters.ShouldUseForwardRenderingOnly()) return;
// Assume that the height of the projection window is 2 meters.
float distanceToProjectionWindow = 1.0f / Mathf.Tan(0.5f * Mathf.Deg2Rad * hdCamera.camera.fieldOfView);
m_CombineSubsurfaceScattering.SetFloat("_DistToProjWindow", distanceToProjectionWindow);
m_CombineSubsurfaceScattering.SetFloat("_BilateralScale", 0.05f * sssParameters.bilateralScale);
// TODO: use user-defined values for '_ProfileID' and '_FilterRadius.'
m_CombineSubsurfaceScattering.SetVectorArray("_FilterKernel", sssParameters.profiles[0].filterKernel);
m_CombineSubsurfaceScattering.SetFloat("_FilterRadius", 3.0f);
MaterialPropertyBlock properties = new MaterialPropertyBlock();
// Load the kernel data.
Vector4[] kernelData = new Vector4[SubsurfaceScatteringParameters.maxNumProfiles * SubsurfaceScatteringProfile.numVectors];
for (int j = 0, m = sssParameters.profiles.Length; j < m; j++)
{
for (int i = 0, n = SubsurfaceScatteringProfile.numVectors; i < n; i++)
{
kernelData[n * j + i] = sssParameters.profiles[j].filterKernel[i];
}
}
var cmd = new CommandBuffer() { name = "Combine Subsurface Scattering" };
var cmd = new CommandBuffer() { name = "Subsurface Scattering Pass" };
properties.SetFloat("_DstBlend", (float)BlendMode.Zero); // TODO: this doesn't work for some reason...
properties.SetFloat("_FilterHorizontal", 0);
m_FilterSubsurfaceScattering.SetMatrix("_InvProjMatrix", hdCamera.invProjectionMatrix);
m_FilterSubsurfaceScattering.SetVectorArray("_FilterKernels", kernelData);
Utilities.DrawFullscreen(cmd, m_CombineSubsurfaceScattering, hdCamera,
m_CameraFilteringBufferRT, m_CameraStencilBufferRT, properties);
Utilities.DrawFullscreen(cmd, m_FilterSubsurfaceScattering, hdCamera,
m_CameraFilteringBufferRT, m_CameraStencilBufferRT);
properties.SetFloat("_DstBlend", (float)BlendMode.One); // TODO: this doesn't work for some reason...
properties.SetFloat("_FilterHorizontal", 1);
m_FilterAndCombineSubsurfaceScattering.SetMatrix("_InvProjMatrix", hdCamera.invProjectionMatrix);
m_FilterAndCombineSubsurfaceScattering.SetVectorArray("_FilterKernels", kernelData);
Utilities.DrawFullscreen(cmd, m_CombineSubsurfaceScattering, hdCamera,
m_CameraColorBufferRT, m_CameraStencilBufferRT, properties);
Utilities.DrawFullscreen(cmd, m_FilterAndCombineSubsurfaceScattering, hdCamera,
m_CameraColorBufferRT, m_CameraStencilBufferRT);
context.ExecuteCommandBuffer(cmd);
cmd.Dispose();

2
Assets/ScriptableRenderLoop/HDRenderPipeline/Lighting/Resources/Deferred.shader


ZWrite Off
ZTest Always
Blend [_SrcBlend][_DstBlend]
Blend [_SrcBlend] [_DstBlend], One Zero
Cull Off
HLSLPROGRAM

4
Assets/ScriptableRenderLoop/HDRenderPipeline/Lighting/TilePass/TilePass.cs


m_DeferredIndirectMaterialSRT.DisableKeyword("OUTPUT_SPLIT_LIGHTING");
m_DeferredIndirectMaterialSRT.SetInt("_StencilRef", (int)StencilBits.None);
m_DeferredIndirectMaterialSRT.SetInt("_SrcBlend", (int)BlendMode.One);
m_DeferredIndirectMaterialSRT.SetInt("_DstBlend", (int)BlendMode.One); // Additive
m_DeferredIndirectMaterialSRT.SetInt("_DstBlend", (int)BlendMode.One); // Additive color & alpha source
m_DeferredIndirectMaterialMRT = Utilities.CreateEngineMaterial("Hidden/HDRenderPipeline/Deferred");
Utilities.SelectKeyword(m_DeferredIndirectMaterialMRT, tileKeywords, 1);

m_DeferredIndirectMaterialMRT.SetInt("_SrcBlend", (int)BlendMode.One);
m_DeferredIndirectMaterialMRT.SetInt("_DstBlend", (int)BlendMode.One); // Additive
m_DeferredIndirectMaterialMRT.SetInt("_DstBlend", (int)BlendMode.One); // Additive color & alpha source
m_DeferredAllMaterialSRT = Utilities.CreateEngineMaterial("Hidden/HDRenderPipeline/Deferred");
Utilities.SelectKeyword(m_DeferredAllMaterialSRT, tileKeywords, 2);

4
Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/Lit.shader


[Enum(Standard, 0, Subsurface Scattering, 1, Clear Coat, 2)] _MaterialID("Material Class", Int) = 0
_SubsurfaceProfile("Subsurface Profile", Int) = 0
_SubsurfaceRadius("Subsurface Radius", Range(0.0, 1.0)) = 0
_SubsurfaceRadius("Subsurface Radius", Range(0.004, 1.0)) = 0.5
_Thickness("Thickness", Range(0.0, 1.0)) = 0
_Thickness("Thickness", Range(0.004, 1.0)) = 0.5
_ThicknessMap("Thickness Map", 2D) = "white" {}
//_CoatCoverage("CoatCoverage", Range(0.0, 1.0)) = 0

4
Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/LitTessellation.shader


[Enum(Standard, 0, Subsurface Scattering, 1, Clear Coat, 2)] _MaterialID("Material Class", Int) = 0
_SubsurfaceProfile("Subsurface Profile", Int) = 0
_SubsurfaceRadius("Subsurface Radius", Range(0.0, 1.0)) = 0
_SubsurfaceRadius("Subsurface Radius", Range(0.004, 1.0)) = 0.5
_Thickness("Thickness", Range(0.0, 1.0)) = 0
_Thickness("Thickness", Range(0.004, 1.0)) = 0.5
_ThicknessMap("Thickness Map", 2D) = "white" {}
//_CoatCoverage("CoatCoverage", Range(0.0, 1.0)) = 0

94
Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/Resources/CombineSubsurfaceScattering.shader


{
Properties
{
_FilterRadius("", Float) = 20
_BilateralScale("", Float) = 0.1
[HideInInspector] _DstBlend("", Float) = 1 // Can be set to 1 for blending with specular
}

Pass Keep
}
Cull Off
Blend One [_DstBlend], Zero [_DstBlend]
Blend One [_DstBlend]
HLSLPROGRAM
#pragma target 4.5

#pragma fragment Frag
#pragma multi_compile _ FILTER_HORIZONTAL
//-------------------------------------------------------------------------------------
// Include
//-------------------------------------------------------------------------------------

// Inputs & outputs
//-------------------------------------------------------------------------------------
#define N_SAMPLES 7
#define N_PROFILES 8
#define N_SAMPLES 7
float _BilateralScale; // Uses world-space units
float _DistToProjWindow; // The height of the projection window is 2 meters
float _FilterHorizontal; // Vertical = 0, horizontal = 1
float4 _FilterKernel[7]; // RGB = weights, A = radial distance
float _FilterRadius; // Uses world-space units
float4 _FilterKernels[N_PROFILES][N_SAMPLES + 1]; // RGB = weights, A = radial distance
float4x4 _InvProjMatrix;
TEXTURE2D(_GBufferTexture2);
SAMPLER2D(sampler_IrradianceSource);
#define bilinearSampler sampler_IrradianceSource
//-------------------------------------------------------------------------------------
// Implementation

return output;
}
float4 Frag(Varyings input) : SV_Target
float3 Frag(Varyings input) : SV_Target
float rawDepth = LOAD_TEXTURE2D(_CameraDepthTexture, posInput.unPositionSS).r;
float centerDepth = LinearEyeDepth(rawDepth, _ZBufferParams);
float radiusScale = _FilterRadius * _DistToProjWindow / centerDepth;
float2 gBufferData = LOAD_TEXTURE2D(_GBufferTexture2, posInput.unPositionSS).ra;
int profileID = int(gBufferData.y * N_PROFILES);
float distScale = gBufferData.x * 0.01;
float invDistScale = rcp(distScale);
// Compute the filtering direction.
float2 unitDirection = _FilterHorizontal ? float2(1, 0) : float2(0, 1);
float2 scaledDirection = radiusScale * unitDirection;
// Reconstruct the view-space position.
float rawDepth = LOAD_TEXTURE2D(_CameraDepthTexture, posInput.unPositionSS).r;
float3 centerPosVS = ComputeViewSpacePosition(posInput.positionSS, rawDepth, _InvProjMatrix);
// Premultiply with the inverse of the screen size.
scaledDirection *= _ScreenSize.zw;
// Compute the dimensions of the surface fragment viewed as a quad facing the camera.
float fragWidth = ddx(centerPosVS.x);
float fragheight = ddy(centerPosVS.y);
float stepSizeX = rcp(fragWidth);
float stepSizeY = rcp(fragheight);
// Take the first (central) sample.
float3 sampleWeight = _FilterKernel[0].rgb;
float2 samplePosition = posInput.unPositionSS;
// Compute the filtering direction.
#ifdef FILTER_HORIZONTAL
float stepSize = stepSizeX;
float2 unitDirection = float2(1, 0);
#else
float stepSize = stepSizeY;
float2 unitDirection = float2(0, 1);
#endif
float2 scaledDirection = distScale * stepSize * unitDirection;
// Load (1 / (2 * Variance)) for bilateral weighting.
#ifdef RBG_BILATERAL_WEIGHTS
float3 halfRcpVariance = _FilterKernels[profileID][N_SAMPLES].rgb;
#else
float halfRcpVariance = _FilterKernels[profileID][N_SAMPLES].a;
#endif
// Take the first (central) sample.
float2 samplePosition = posInput.unPositionSS;
float3 sampleWeight = _FilterKernels[profileID][0].rgb;
float3 centerIrradiance = sampleIrradiance;
float3 filteredIrradiance = sampleIrradiance * sampleWeight;
float3 totalIrradiance = sampleIrradiance * sampleWeight;
// Make sure bilateral filtering does not cause energy loss.
// TODO: ask Morten if there is a better way to do this.
float3 totalWeight = sampleWeight;
sampleWeight = _FilterKernel[i].rgb;
samplePosition = posInput.positionSS + scaledDirection * _FilterKernel[i].a;
samplePosition = posInput.unPositionSS + scaledDirection * _FilterKernels[profileID][i].a;
sampleWeight = _FilterKernels[profileID][i].rgb;
sampleIrradiance = SAMPLE_TEXTURE2D_LOD(_IrradianceSource, bilinearSampler, samplePosition, 0).rgb;
rawDepth = SAMPLE_TEXTURE2D_LOD(_CameraDepthTexture, bilinearSampler, samplePosition, 0).r;
rawDepth = LOAD_TEXTURE2D(_CameraDepthTexture, samplePosition).r;
sampleIrradiance = LOAD_TEXTURE2D(_IrradianceSource, samplePosition).rgb;
// Apply bilateral filtering.
// Apply bilateral weighting.
// Ref #1: Skin Rendering by Pseudo–Separable Cross Bilateral Filtering.
// Ref #2: Separable SSS, Supplementary Materials, Section E.
float depthDiff = abs(sampleDepth - centerDepth);
float scaleDiff = _BilateralScale * _FilterRadius * _DistToProjWindow;
float t = saturate(depthDiff / scaleDiff);
float zDistance = invDistScale * sampleDepth - (invDistScale * centerPosVS.z);
sampleWeight *= exp(-zDistance * zDistance * halfRcpVariance);
// TODO: use real-world distances for weighting.
filteredIrradiance += lerp(sampleIrradiance, centerIrradiance, t) * sampleWeight;
totalIrradiance += sampleIrradiance * sampleWeight;
totalWeight += sampleWeight;
return float4(filteredIrradiance, 1.0);
return totalIrradiance / totalWeight;
}
ENDHLSL
}

54
Assets/ScriptableRenderLoop/HDRenderPipeline/SceneSettings/SubsurfaceScatteringParameters.cs


public class SubsurfaceScatteringProfile
{
public const int numSamples = 7; // Must be an odd number
public const int numVectors = 8; // numSamples + 1 for (1 / (2 * WeightedVariance))
[SerializeField, ColorUsage(false, true, 0.05f, 2.0f, 1.0f, 1.0f)]
Color m_StdDev1;

void ComputeKernel()
{
if (m_FilterKernel == null || m_FilterKernel.Length != numSamples)
if (m_FilterKernel == null || m_FilterKernel.Length != numVectors)
m_FilterKernel = new Vector4[numSamples];
m_FilterKernel = new Vector4[numVectors];
}
// Our goal is to blur the image using a filter which is represented

val.y = GaussianCombination(pos, m_StdDev1.g, m_StdDev2.g, m_LerpWeight);
val.z = GaussianCombination(pos, m_StdDev1.b, m_StdDev2.b, m_LerpWeight);
m_FilterKernel[i].x = val.x / (pdf * numSamples);
m_FilterKernel[i].y = val.y / (pdf * numSamples);
m_FilterKernel[i].z = val.z / (pdf * numSamples);
// We do not divide by 'numSamples' since we will renormalize, anyway.
m_FilterKernel[i].x = val.x * (1 / pdf);
m_FilterKernel[i].y = val.y * (1 / pdf);
m_FilterKernel[i].z = val.z * (1 / pdf);
m_FilterKernel[i].w = pos;
weightSum.x += m_FilterKernel[i].x;

// Renormalize the weights to conserve energy.
for (uint i = 0; i < numSamples; i++)
{
m_FilterKernel[i].x *= 1.0f / weightSum.x;
m_FilterKernel[i].y *= 1.0f / weightSum.y;
m_FilterKernel[i].z *= 1.0f / weightSum.z;
m_FilterKernel[i].x *= 1 / weightSum.x;
m_FilterKernel[i].y *= 1 / weightSum.y;
m_FilterKernel[i].z *= 1 / weightSum.z;
m_KernelNeedsUpdate = false;
Vector4 weightedStdDev;
weightedStdDev.x = Mathf.Lerp(m_StdDev1.r, m_StdDev2.r, m_LerpWeight);
weightedStdDev.y = Mathf.Lerp(m_StdDev1.g, m_StdDev2.g, m_LerpWeight);
weightedStdDev.z = Mathf.Lerp(m_StdDev1.b, m_StdDev2.b, m_LerpWeight);
weightedStdDev.w = Mathf.Lerp(maxStdDev1, maxStdDev2, m_LerpWeight);
// Store (1 / (2 * Variance)) per color channel.
m_FilterKernel[numSamples].x = 0.5f / (weightedStdDev.x * weightedStdDev.x);
m_FilterKernel[numSamples].y = 0.5f / (weightedStdDev.y * weightedStdDev.y);
m_FilterKernel[numSamples].z = 0.5f / (weightedStdDev.z * weightedStdDev.z);
m_FilterKernel[numSamples].w = 0.5f / (weightedStdDev.w * weightedStdDev.w);
const int m_maxNumProfiles = 8;
public const int maxNumProfiles = 8;
[SerializeField]
float m_BilateralScale;
m_NumProfiles = 1;
m_Profiles = new SubsurfaceScatteringProfile[m_NumProfiles];
m_BilateralScale = 0.1f;
m_NumProfiles = 1;
m_Profiles = new SubsurfaceScatteringProfile[m_NumProfiles];
for (int i = 0; i < m_NumProfiles; i++)
{

public SubsurfaceScatteringProfile[] profiles { set { m_Profiles = value; OnValidate(); } get { return m_Profiles; } }
public float bilateralScale { set { m_BilateralScale = value; OnValidate(); } get { return m_BilateralScale; } }
public SubsurfaceScatteringProfile[] profiles { set { m_Profiles = value; OnValidate(); } get { return m_Profiles; } }
public void SetDirtyFlag()
{

void OnValidate()
{
if (m_Profiles.Length > m_maxNumProfiles)
if (m_Profiles.Length > maxNumProfiles)
Array.Resize(ref m_Profiles, m_maxNumProfiles);
Array.Resize(ref m_Profiles, maxNumProfiles);
}
m_NumProfiles = m_Profiles.Length;

m_Profiles[i].lerpWeight = Mathf.Clamp01(m_Profiles[i].lerpWeight);
}
m_BilateralScale = Mathf.Clamp01(m_BilateralScale);
}
}

public readonly GUIContent sssProfileStdDev1 = new GUIContent("SSS profile standard deviation #1", "Determines the shape of the 1st Gaussian filter. Increases the strength and the radius of the blur of the corresponding color channel.");
public readonly GUIContent sssProfileStdDev2 = new GUIContent("SSS profile standard deviation #2", "Determines the shape of the 2nd Gaussian filter. Increases the strength and the radius of the blur of the corresponding color channel.");
public readonly GUIContent sssProfileLerpWeight = new GUIContent("SSS profile filter interpolation", "Controls linear interpolation between the two Gaussian filters.");
public readonly GUIContent sssBilateralScale = new GUIContent("SSS bilateral filtering scale", "Larger values make the filter more tolerant to depth differences.");
private SerializedProperty m_BilateralScale;
// --- Public Methods ---

EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_Profiles, true);
EditorGUILayout.PropertyField(m_BilateralScale, styles.sssBilateralScale);
if (EditorGUI.EndChangeCheck())
{
serializedObject.ApplyModifiedProperties();

void OnEnable()
{
m_Profiles = serializedObject.FindProperty("m_Profiles");
m_BilateralScale = serializedObject.FindProperty("m_BilateralScale");
m_Profiles = serializedObject.FindProperty("m_Profiles");
}
}
}

3
Assets/ScriptableRenderLoop/HDRenderPipeline/Utilities.cs


var gpuProj = GL.GetGPUProjectionMatrix(camera.projectionMatrix, false);
var gpuVP = gpuProj * camera.worldToCameraMatrix;
hdCamera.viewProjectionMatrix = gpuVP;
hdCamera.viewProjectionMatrix = gpuVP;
hdCamera.invProjectionMatrix = gpuProj.inverse;
return hdCamera;
}

10
Assets/ScriptableRenderLoop/ShaderLibrary/Common.hlsl


posInput.positionCS *= posInput.depthVS;
}
float3 ComputeViewSpacePosition(float2 positionSS, float rawDepth, float4x4 invProjMatrix)
{
float4 positionCS = float4(positionSS * 2.0 - 1.0, rawDepth, 1.0);
float4 positionVS = mul(invProjMatrix, positionCS);
// The view space uses a right-handed coordinate system.
positionVS.z = -positionVS.z;
return positionVS.xyz / positionVS.w;
}
// depthOffsetVS is always in the direction of the view vector (V)
void ApplyDepthOffsetPositionInput(float3 V, float depthOffsetVS, inout PositionInputs posInput)
{

// Just add the offset along the view vector is sufficiant for world position
posInput.positionWS += V * depthOffsetVS;
}
// Generates a triangle in homogeneous clip space, s.t.
// v0 = (-1, -1, 1), v1 = (3, -1, 1), v2 = (-1, 3, 1).

正在加载...
取消
保存