浏览代码

Improve SSS parameter caching

/Branch_Batching2
Evgenii Golubev 7 年前
当前提交
750eb7b3
共有 3 个文件被更改,包括 191 次插入139 次删除
  1. 20
      Assets/ScriptableRenderLoop/HDRenderPipeline/HDRenderPipeline.cs
  2. 8
      Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/Resources/CombineSubsurfaceScattering.shader
  3. 302
      Assets/ScriptableRenderLoop/HDRenderPipeline/SceneSettings/SubsurfaceScatteringParameters.cs

20
Assets/ScriptableRenderLoop/HDRenderPipeline/HDRenderPipeline.cs


// Currently, forward-rendered objects do not output split lighting required for the SSS pass.
if (m_Owner.renderingParameters.ShouldUseForwardRenderingOnly()) return;
// Load the kernel and variance data.
Vector4[] kernelData = new Vector4[SubsurfaceScatteringParameters.maxNumProfiles * SubsurfaceScatteringProfile.numSamples];
Vector4[] varianceData = new Vector4[SubsurfaceScatteringParameters.maxNumProfiles];
for (int j = 0, m = sssParameters.profiles.Length; j < m; j++)
{
for (int i = 0, n = SubsurfaceScatteringProfile.numSamples; i < n; i++)
{
kernelData[n * j + i] = sssParameters.profiles[j].filterKernel[i];
}
varianceData[j] = sssParameters.profiles[j].halfRcpVariance;
}
m_FilterSubsurfaceScattering.SetVectorArray("_FilterKernels", kernelData);
m_FilterSubsurfaceScattering.SetVectorArray("_HalfRcpVariances", varianceData);
m_FilterSubsurfaceScattering.SetVectorArray("_FilterKernels", sssParameters.filterKernels);
m_FilterSubsurfaceScattering.SetVectorArray("_HalfRcpWeightedVariances", sssParameters.halfRcpWeightedVariances);
cmd.SetGlobalTexture("_IrradianceSource", m_CameraSubsurfaceBufferRT);
Utilities.DrawFullScreen(cmd, m_FilterSubsurfaceScattering, hdCamera,
m_CameraFilteringBufferRT, m_CameraStencilBufferRT);

m_FilterAndCombineSubsurfaceScattering.SetVectorArray("_FilterKernels", kernelData);
m_FilterAndCombineSubsurfaceScattering.SetVectorArray("_HalfRcpVariances", varianceData);
m_FilterAndCombineSubsurfaceScattering.SetVectorArray("_FilterKernels", sssParameters.filterKernels);
m_FilterAndCombineSubsurfaceScattering.SetVectorArray("_HalfRcpWeightedVariances", sssParameters.halfRcpWeightedVariances);
cmd.SetGlobalTexture("_IrradianceSource", m_CameraFilteringBufferRT);
Utilities.DrawFullScreen(cmd, m_FilterAndCombineSubsurfaceScattering, hdCamera,
m_CameraColorBufferRT, m_CameraStencilBufferRT);

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


#define N_SAMPLES 7
float4 _FilterKernels[N_PROFILES][N_SAMPLES]; // RGB = weights, A = radial distance
float4 _HalfRcpVariances[N_PROFILES]; // RGB for chromatic, A for achromatic
float4 _HalfRcpWeightedVariances[N_PROFILES]; // RGB for chromatic, A for achromatic
float4x4 _InvProjMatrix;
TEXTURE2D_FLOAT(_CameraDepthTexture);

#endif
float2 scaledDirection = distScale * stepSize * unitDirection;
// Load (1 / (2 * Variance)) for bilateral weighting.
// Load (1 / (2 * WeightedVariance)) for bilateral weighting.
float3 halfRcpVariance = _HalfRcpVariances[profileID].rgb;
float3 halfRcpVariance = _HalfRcpWeightedVariances[profileID].rgb;
float halfRcpVariance = _HalfRcpVariances[profileID].a;
float halfRcpVariance = _HalfRcpWeightedVariances[profileID].a;
#endif
// Take the first (central) sample.
float2 samplePosition = posInput.unPositionSS;

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


public const int numSamples = 7; // Must be an odd number
[SerializeField, ColorUsage(false, true, 0.05f, 2.0f, 1.0f, 1.0f)]
Color m_StdDev1;
public Color stdDev1;
Color m_StdDev2;
public Color stdDev2;
float m_LerpWeight;
public float lerpWeight;
[SerializeField]
public bool enableTransmittance;
Vector4[] m_FilterKernel;
Vector4[] m_FilterKernel;
Vector4 m_HalfRcpVariance;
Vector3[] m_HalfRcpVariances;
public bool m_KernelNeedsUpdate;
Vector4 m_HalfRcpWeightedVariances;
m_StdDev1 = new Color(0.3f, 0.3f, 0.3f, 0.0f);
m_StdDev2 = new Color(1.0f, 1.0f, 1.0f, 0.0f);
m_LerpWeight = 0.5f;
ComputeKernel();
}
stdDev1 = new Color(0.3f, 0.3f, 0.3f, 0.0f);
stdDev2 = new Color(1.0f, 1.0f, 1.0f, 0.0f);
lerpWeight = 0.5f;
enableTransmittance = true;
m_FilterKernel = null;
m_HalfRcpVariances = null;
public Color stdDev1
{
get { return m_StdDev1; }
set { if (m_StdDev1 != value) { m_StdDev1 = value; m_KernelNeedsUpdate = true; } }
}
public Color stdDev2
{
get { return m_StdDev2; }
set { if (m_StdDev2 != value) { m_StdDev2 = value; m_KernelNeedsUpdate = true; } }
}
public float lerpWeight
{
get { return m_LerpWeight; }
set { if (m_LerpWeight != value) { m_LerpWeight = value; m_KernelNeedsUpdate = true; } }
UpdateKernelAndVarianceData();
get { if (m_KernelNeedsUpdate) ComputeKernel(); return m_FilterKernel; }
// Set via UpdateKernelAndVarianceData().
get { return m_FilterKernel; }
public Vector4 halfRcpVariance
{
get { if (m_KernelNeedsUpdate) ComputeKernel(); return m_HalfRcpVariance; }
}
public void SetDirtyFlag()
{
m_KernelNeedsUpdate = true;
}
// --- Private Methods ---
static float Gaussian(float x, float stdDev)
{
float variance = stdDev * stdDev;
return Mathf.Exp(-x * x / (2 * variance)) / Mathf.Sqrt(2 * Mathf.PI * variance);
public Vector3[] halfRcpVariances
{
// Set via UpdateKernelAndVarianceData().
get { return m_HalfRcpVariances; }
static float GaussianCombination(float x, float stdDev1, float stdDev2, float lerpWeight)
public Vector4 halfRcpWeightedVariances
return Mathf.Lerp(Gaussian(x, stdDev1), Gaussian(x, stdDev2), lerpWeight);
// Set via UpdateKernelAndVarianceData().
get { return m_HalfRcpWeightedVariances; }
static float RationalApproximation(float t)
public void UpdateKernelAndVarianceData()
// Abramowitz and Stegun formula 26.2.23.
// The absolute value of the error should be less than 4.5 e-4.
float[] c = {2.515517f, 0.802853f, 0.010328f};
float[] d = {1.432788f, 0.189269f, 0.001308f};
return t - ((c[2] * t + c[1]) * t + c[0]) / (((d[2] * t + d[1]) * t + d[0]) * t + 1.0f);
}
// Ref: https://www.johndcook.com/blog/csharp_phi_inverse/
static float NormalCdfInverse(float p, float stdDev)
{
float x;
if (p < 0.5)
if (m_FilterKernel == null)
// F^-1(p) = - G^-1(p)
x = -RationalApproximation(Mathf.Sqrt(-2.0f * Mathf.Log(p)));
}
else
{
// F^-1(p) = G^-1(1-p)
x = RationalApproximation(Mathf.Sqrt(-2.0f * Mathf.Log(1.0f - p)));
m_FilterKernel = new Vector4[numSamples];
return x * stdDev;
}
static float GaussianCombinationCdfInverse(float p, float stdDev1, float stdDev2, float lerpWeight)
{
return Mathf.Lerp(NormalCdfInverse(p, stdDev1), NormalCdfInverse(p, stdDev2), lerpWeight);
}
void ComputeKernel()
{
if (m_FilterKernel == null || m_FilterKernel.Length != numSamples)
if (m_HalfRcpVariances == null)
m_FilterKernel = new Vector4[numSamples];
m_HalfRcpVariances = new Vector3[2];
}
// Our goal is to blur the image using a filter which is represented

// It is separable by design, but generally not radially symmetric.
// Find the widest Gaussian across 3 color channels.
float maxStdDev1 = Mathf.Max(m_StdDev1.r, m_StdDev1.g, m_StdDev1.b);
float maxStdDev2 = Mathf.Max(m_StdDev2.r, m_StdDev2.g, m_StdDev2.b);
float maxStdDev1 = Mathf.Max(stdDev1.r, stdDev1.g, stdDev1.b);
float maxStdDev2 = Mathf.Max(stdDev2.r, stdDev2.g, stdDev2.b);
Vector3 weightSum = new Vector3(0, 0, 0);

float u = (i + 0.5f) / numSamples;
float pos = GaussianCombinationCdfInverse(u, maxStdDev1, maxStdDev2, m_LerpWeight);
float pdf = GaussianCombination(pos, maxStdDev1, maxStdDev2, m_LerpWeight);
float pos = GaussianCombinationCdfInverse(u, maxStdDev1, maxStdDev2, lerpWeight);
float pdf = GaussianCombination(pos, maxStdDev1, maxStdDev2, lerpWeight);
val.x = GaussianCombination(pos, m_StdDev1.r, m_StdDev2.r, m_LerpWeight);
val.y = GaussianCombination(pos, m_StdDev1.g, m_StdDev2.g, m_LerpWeight);
val.z = GaussianCombination(pos, m_StdDev1.b, m_StdDev2.b, m_LerpWeight);
val.x = GaussianCombination(pos, stdDev1.r, stdDev2.r, lerpWeight);
val.y = GaussianCombination(pos, stdDev1.g, stdDev2.g, lerpWeight);
val.z = GaussianCombination(pos, stdDev1.b, stdDev2.b, lerpWeight);
// We do not divide by 'numSamples' since we will renormalize, anyway.
m_FilterKernel[i].x = val.x * (1 / pdf);

m_FilterKernel[i].z *= 1 / weightSum.z;
}
// Store (1 / (2 * Variance)) per color channel per Gaussian.
m_HalfRcpVariances[0].x = 0.5f / (stdDev1.r * stdDev1.r);
m_HalfRcpVariances[0].y = 0.5f / (stdDev1.g * stdDev1.g);
m_HalfRcpVariances[0].z = 0.5f / (stdDev1.b * stdDev1.b);
m_HalfRcpVariances[1].x = 0.5f / (stdDev2.r * stdDev2.r);
m_HalfRcpVariances[1].y = 0.5f / (stdDev2.g * stdDev2.g);
m_HalfRcpVariances[1].z = 0.5f / (stdDev2.b * stdDev2.b);
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);
weightedStdDev.x = Mathf.Lerp(stdDev1.r, stdDev2.r, lerpWeight);
weightedStdDev.y = Mathf.Lerp(stdDev1.g, stdDev2.g, lerpWeight);
weightedStdDev.z = Mathf.Lerp(stdDev1.b, stdDev2.b, lerpWeight);
weightedStdDev.w = Mathf.Lerp(maxStdDev1, maxStdDev2, lerpWeight);
// Store (1 / (2 * Variance)) per color channel.
m_HalfRcpVariance.x = 0.5f / (weightedStdDev.x * weightedStdDev.x);
m_HalfRcpVariance.y = 0.5f / (weightedStdDev.y * weightedStdDev.y);
m_HalfRcpVariance.z = 0.5f / (weightedStdDev.z * weightedStdDev.z);
m_HalfRcpVariance.w = 0.5f / (weightedStdDev.w * weightedStdDev.w);
// Store (1 / (2 * WeightedVariance)) per color channel.
m_HalfRcpWeightedVariances.x = 0.5f / (weightedStdDev.x * weightedStdDev.x);
m_HalfRcpWeightedVariances.y = 0.5f / (weightedStdDev.y * weightedStdDev.y);
m_HalfRcpWeightedVariances.z = 0.5f / (weightedStdDev.z * weightedStdDev.z);
m_HalfRcpWeightedVariances.w = 0.5f / (weightedStdDev.w * weightedStdDev.w);
}
// --- Private Methods ---
static float Gaussian(float x, float stdDev)
{
float variance = stdDev * stdDev;
return Mathf.Exp(-x * x / (2 * variance)) / Mathf.Sqrt(2 * Mathf.PI * variance);
}
static float GaussianCombination(float x, float stdDev1, float stdDev2, float lerpWeight)
{
return Mathf.Lerp(Gaussian(x, stdDev1), Gaussian(x, stdDev2), lerpWeight);
}
static float RationalApproximation(float t)
{
// Abramowitz and Stegun formula 26.2.23.
// The absolute value of the error should be less than 4.5 e-4.
float[] c = {2.515517f, 0.802853f, 0.010328f};
float[] d = {1.432788f, 0.189269f, 0.001308f};
return t - ((c[2] * t + c[1]) * t + c[0]) / (((d[2] * t + d[1]) * t + d[0]) * t + 1.0f);
}
// Ref: https://www.johndcook.com/blog/csharp_phi_inverse/
static float NormalCdfInverse(float p, float stdDev)
{
float x;
if (p < 0.5)
{
// F^-1(p) = - G^-1(p)
x = -RationalApproximation(Mathf.Sqrt(-2.0f * Mathf.Log(p)));
}
else
{
// F^-1(p) = G^-1(1-p)
x = RationalApproximation(Mathf.Sqrt(-2.0f * Mathf.Log(1.0f - p)));
}
return x * stdDev;
}
static float GaussianCombinationCdfInverse(float p, float stdDev1, float stdDev2, float lerpWeight)
{
return Mathf.Lerp(NormalCdfInverse(p, stdDev1), NormalCdfInverse(p, stdDev2), lerpWeight);
}
}

[SerializeField]
int m_NumProfiles;
int m_NumProfiles;
int m_TransmittanceFlags;
[SerializeField]
Vector4[] m_HalfRcpVariancesAndLerpWeights;
[SerializeField]
Vector4[] m_HalfRcpWeightedVariances;
[SerializeField]
Vector4[] m_FilterKernels;
// --- Public Methods ---

{
m_Profiles[i] = new SubsurfaceScatteringProfile();
}
OnValidate();
public SubsurfaceScatteringProfile[] profiles { set { m_Profiles = value; OnValidate(); } get { return m_Profiles; } }
public SubsurfaceScatteringProfile[] profiles {
// Set via serialization.
get { return m_Profiles; }
}
// Returns a bit mask s.t. the i-th bit indicates whether the i-th profile requires transmittance evaluation.
// Supplies '_TransmittanceFlags' to Lit.hlsl.
public int transmittanceFlags {
// Set during OnValidate().
get { return m_TransmittanceFlags; }
}
// Supplies '_HalfRcpVariancesAndLerpWeights' to Lit.hlsl.
public Vector4[] halfRcpVariancesAndLerpWeights {
// Set during OnValidate().
get { return m_HalfRcpVariancesAndLerpWeights; }
}
// Supplies '_HalfRcpWeightedVariances' to CombineSubsurfaceScattering.shader.
public Vector4[] halfRcpWeightedVariances {
// Set during OnValidate().
get { return m_HalfRcpWeightedVariances; }
}
public void SetDirtyFlag()
// Supplies '_FilterKernels' to CombineSubsurfaceScattering.shader.
public Vector4[] filterKernels
for (int i = 0; i < m_Profiles.Length; i++)
{
m_Profiles[i].SetDirtyFlag();
}
// Set during OnValidate().
get { return m_FilterKernels; }
}
// --- Private Methods ---

Array.Resize(ref m_Profiles, maxNumProfiles);
}
m_NumProfiles = m_Profiles.Length;
m_NumProfiles = m_Profiles.Length;
m_TransmittanceFlags = 0;
if (m_HalfRcpVariancesAndLerpWeights == null)
{
m_HalfRcpVariancesAndLerpWeights = new Vector4[maxNumProfiles * 2];
}
if (m_HalfRcpWeightedVariances == null)
{
m_HalfRcpWeightedVariances = new Vector4[maxNumProfiles];
}
if (m_FilterKernels == null)
{
m_FilterKernels = new Vector4[maxNumProfiles * SubsurfaceScatteringProfile.numSamples];
}
m_TransmittanceFlags |= (m_Profiles[i].enableTransmittance ? 1 : 0) << i;
c.r = Mathf.Clamp(m_Profiles[i].stdDev1.r, 0.05f, 2.0f);
c.g = Mathf.Clamp(m_Profiles[i].stdDev1.g, 0.05f, 2.0f);
c.b = Mathf.Clamp(m_Profiles[i].stdDev1.b, 0.05f, 2.0f);

m_Profiles[i].stdDev2 = c;
m_Profiles[i].lerpWeight = Mathf.Clamp01(m_Profiles[i].lerpWeight);
m_Profiles[i].UpdateKernelAndVarianceData();
}
// Use the updated data to fill the cache.
for (int i = 0; i < m_NumProfiles; i++)
{
m_HalfRcpVariancesAndLerpWeights[2 * i] = m_Profiles[i].halfRcpVariances[0];
m_HalfRcpVariancesAndLerpWeights[2 * i].w = 1.0f - m_Profiles[i].lerpWeight;
m_HalfRcpVariancesAndLerpWeights[2 * i + 1] = m_Profiles[i].halfRcpVariances[1];
m_HalfRcpVariancesAndLerpWeights[2 * i + 1].w = m_Profiles[i].lerpWeight;
m_HalfRcpWeightedVariances[i] = m_Profiles[i].halfRcpWeightedVariances;
for (int j = 0, n = SubsurfaceScatteringProfile.numSamples; j < n; j++)
{
m_FilterKernels[n * i + j] = m_Profiles[i].filterKernel[j];
}
}
}
}

{
private class Styles
{
public readonly GUIContent sssCategory = new GUIContent("Subsurface scattering");
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 sssCategory = new GUIContent("Subsurface scattering");
public readonly GUIContent sssProfileStdDev1 = new GUIContent("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("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("Filter interpolation", "Controls linear interpolation between the two Gaussian filters.");
public readonly GUIContent sssProfileTransmittance = new GUIContent("Enable transmittance", "Toggles simulation of light passing through thin objects. Depends on the thickness of the material.");
private SerializedProperty m_Profiles;
// --- Public Methods ---

{
serializedObject.Update();
SerializedProperty profiles = serializedObject.FindProperty("m_Profiles");
EditorGUILayout.PropertyField(m_Profiles, true);
EditorGUILayout.PropertyField(profiles, true);
// Serialization does not invoke setters, but does call OnValidate().
// Serialization ignores setters.
((SubsurfaceScatteringParameters)target).SetDirtyFlag();
}
// --- Private Methods ---
void OnEnable()
{
m_Profiles = serializedObject.FindProperty("m_Profiles");
}
}
#endif
正在加载...
取消
保存