浏览代码

Implement the initial version of custom SSS filters

/main
Evgenii Golubev 8 年前
当前提交
df6d69e8
共有 6 个文件被更改,包括 255 次插入44 次删除
  1. 23
      Assets/ScriptableRenderLoop/HDRenderPipeline/HDRenderPipeline.cs
  2. 40
      Assets/ScriptableRenderLoop/HDRenderPipeline/Material/Lit/Resources/CombineSubsurfaceScattering.shader
  3. 49
      Assets/ScriptableRenderLoop/HDRenderPipeline/SceneSettings/CommonSettings.cs
  4. 28
      Assets/ScriptableRenderLoop/HDRenderPipeline/SceneSettings/Editor/CommonSettingsEditor.cs
  5. 147
      Assets/ScriptableRenderLoop/HDRenderPipeline/SceneSettings/SubsurfaceScatteringParameters.cs
  6. 12
      Assets/ScriptableRenderLoop/HDRenderPipeline/SceneSettings/SubsurfaceScatteringParameters.cs.meta

23
Assets/ScriptableRenderLoop/HDRenderPipeline/HDRenderPipeline.cs


set { m_TextureSettings = value; }
}
[SerializeField]
SubsurfaceScatteringParameters m_SssParameters = SubsurfaceScatteringParameters.Default;
public SubsurfaceScatteringParameters sssParameters
{
get { return m_SssParameters; }
set { m_SssParameters = value; }
}
// Various set of material use in render loop
Material m_DebugViewMaterialGBuffer;
Material m_CombineSubsurfaceScattering;

// 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("_FilterRadius", 20.0f);
m_CombineSubsurfaceScattering.SetFloat("_BilateralScale", 0.1f);
// Note: the user-provided bilateral scale is reversed (compared to the one in the shader).
m_CombineSubsurfaceScattering.SetFloat("_BilateralScale", (1.0f - sssParameters.bilateralScale));
// TODO: use user-defined values for '_ProfileID' and '_FilterRadius.'
m_CombineSubsurfaceScattering.SetVectorArray("_FilterKernel", sssParameters.profiles[0].filterKernel);
m_CombineSubsurfaceScattering.SetFloat("_FilterRadius", 5.0f);
MaterialPropertyBlock properties = new MaterialPropertyBlock();

m_ShadowSettings.maxShadowDistance = ShadowSettings.Default.maxShadowDistance;
m_ShadowSettings.directionalLightCascadeCount = ShadowSettings.Default.directionalLightCascadeCount;
m_ShadowSettings.directionalLightCascades = ShadowSettings.Default.directionalLightCascades;
sssParameters = SubsurfaceScatteringParameters.Default;
}
else
{

sssParameters.profiles[0].filter1Variance = m_CommonSettings.sssProfileFilter1Variance;
sssParameters.profiles[0].filter2Variance = m_CommonSettings.sssProfileFilter2Variance;
sssParameters.profiles[0].filterLerpWeight = m_CommonSettings.sssProfileFilterLerpWeight;
sssParameters.bilateralScale = m_CommonSettings.sssBilateralScale;
}
}

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


// Inputs & outputs
//-------------------------------------------------------------------------------------
float _FilterRadius; // Uses world-space units
float _BilateralScale; // Uses world-space units
float _FilterHorizontal; // Vertical = 0, horizontal = 1
float _DistToProjWindow; // The height of the projection window is 2 meters
#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
TEXTURE2D(_CameraDepthTexture);
TEXTURE2D(_IrradianceSource);

return output;
}
#define N_SAMPLES 17
static const float4 kernel[] = {
float4(0.536343, 0.624624, 0.748867, 0),
float4(0.00317394, 0.000134823, 3.77269e-005, -2),
float4(0.0100386, 0.000914679, 0.000275702, -1.53125),
float4(0.0144609, 0.00317269, 0.00106399, -1.125),
float4(0.0216301, 0.00794618, 0.00376991, -0.78125),
float4(0.0347317, 0.0151085, 0.00871983, -0.5),
float4(0.0571056, 0.0287432, 0.0172844, -0.28125),
float4(0.0582416, 0.0659959, 0.0411329, -0.125),
float4(0.0324462, 0.0656718, 0.0532821, -0.03125),
float4(0.0324462, 0.0656718, 0.0532821, 0.03125),
float4(0.0582416, 0.0659959, 0.0411329, 0.125),
float4(0.0571056, 0.0287432, 0.0172844, 0.28125),
float4(0.0347317, 0.0151085, 0.00871983, 0.5),
float4(0.0216301, 0.00794618, 0.00376991, 0.78125),
float4(0.0144609, 0.00317269, 0.00106399, 1.125),
float4(0.0100386, 0.000914679, 0.000275702, 1.53125),
float4(0.00317394, 0.000134823, 3.77269e-005, 2),
};
float4 Frag(Varyings input) : SV_Target
{
PositionInputs posInput = GetPositionInput(input.positionCS.xy, _ScreenSize.zw);

scaledDirection *= _ScreenSize.zw;
// Take the first (central) sample.
float3 sWeight = kernel[0].rgb;
float3 sWeight = _FilterKernel[0].rgb;
float2 sPosition = posInput.unPositionSS;
float3 sIrradiance = LOAD_TEXTURE2D(_IrradianceSource, sPosition).rgb;

[unroll]
for (int i = 1; i < N_SAMPLES; i++)
{
sWeight = kernel[i].rgb;
sPosition = posInput.positionSS + scaledDirection * kernel[i].a;
sWeight = _FilterKernel[i].rgb; // TODO: normalize weights
sPosition = posInput.positionSS + scaledDirection * _FilterKernel[i].a;
sIrradiance = SAMPLE_TEXTURE2D_LOD(_IrradianceSource, bilinearSampler, sPosition, 0).rgb;
rawDepth = SAMPLE_TEXTURE2D_LOD(_CameraDepthTexture, bilinearSampler, sPosition, 0).r;

float dScale = _FilterRadius * _DistToProjWindow * _BilateralScale;
float t = saturate(dScale * dDepth);
// TODO: use real-world distances for weighting.
filteredIrradiance += lerp(sIrradiance, cIrradiance, t) * sWeight;
}

49
Assets/ScriptableRenderLoop/HDRenderPipeline/SceneSettings/CommonSettings.cs


{
[SerializeField] private string m_SkyRendererTypeName = ""; // Serialize a string because serialize a Type.
[SerializeField] float m_ShadowMaxDistance = ShadowSettings.Default.maxShadowDistance;
[SerializeField] int m_ShadowCascadeCount = ShadowSettings.Default.directionalLightCascadeCount;
[SerializeField] float m_ShadowCascadeSplit0 = ShadowSettings.Default.directionalLightCascades.x;
[SerializeField] float m_ShadowCascadeSplit1 = ShadowSettings.Default.directionalLightCascades.y;
[SerializeField] float m_ShadowCascadeSplit2 = ShadowSettings.Default.directionalLightCascades.z;
public Type skyRendererType
{
set { m_SkyRendererTypeName = value != null ? value.FullName : ""; OnSkyRendererChanged(); }

public float shadowMaxDistance { set { m_ShadowMaxDistance = value; OnValidate(); } get { return m_ShadowMaxDistance; } }
public int shadowCascadeCount { set { m_ShadowCascadeCount = value; OnValidate(); } get { return m_ShadowCascadeCount; } }
// Shadows
[SerializeField] float m_ShadowMaxDistance = ShadowSettings.Default.maxShadowDistance;
[SerializeField] int m_ShadowCascadeCount = ShadowSettings.Default.directionalLightCascadeCount;
[SerializeField] float m_ShadowCascadeSplit0 = ShadowSettings.Default.directionalLightCascades.x;
[SerializeField] float m_ShadowCascadeSplit1 = ShadowSettings.Default.directionalLightCascades.y;
[SerializeField] float m_ShadowCascadeSplit2 = ShadowSettings.Default.directionalLightCascades.z;
public float shadowMaxDistance { set { m_ShadowMaxDistance = value; OnValidate(); } get { return m_ShadowMaxDistance; } }
public int shadowCascadeCount { set { m_ShadowCascadeCount = value; OnValidate(); } get { return m_ShadowCascadeCount; } }
// Subsurface scattering
[SerializeField] Color m_SssProfileFilter1Variance = SubsurfaceScatteringProfile.Default.filter1Variance;
[SerializeField] Color m_SssProfileFilter2Variance = SubsurfaceScatteringProfile.Default.filter2Variance;
[SerializeField] float m_SssProfileFilterLerpWeight = SubsurfaceScatteringProfile.Default.filterLerpWeight;
[SerializeField] float m_SssBilateralScale = SubsurfaceScatteringParameters.Default.bilateralScale;
public Color sssProfileFilter1Variance { set { m_SssProfileFilter1Variance = value; OnValidate(); } get { return m_SssProfileFilter1Variance; } }
public Color sssProfileFilter2Variance { set { m_SssProfileFilter2Variance = value; OnValidate(); } get { return m_SssProfileFilter2Variance; } }
public float sssProfileFilterLerpWeight { set { m_SssProfileFilterLerpWeight = value; OnValidate(); } get { return m_SssProfileFilterLerpWeight; } }
public float sssBilateralScale { set { m_SssBilateralScale = value; OnValidate(); } get { return m_SssBilateralScale; } }
void OnEnable()
{
HDRenderPipeline renderPipeline = Utilities.GetHDRenderPipeline();

void OnValidate()
{
m_ShadowMaxDistance = Mathf.Max(0.0f, m_ShadowMaxDistance);
m_ShadowCascadeCount = Math.Min(4, Math.Max(1, m_ShadowCascadeCount));
m_ShadowCascadeSplit0 = Mathf.Min(1.0f, Mathf.Max(0.0f, m_ShadowCascadeSplit0));
m_ShadowCascadeSplit1 = Mathf.Min(1.0f, Mathf.Max(0.0f, m_ShadowCascadeSplit1));
m_ShadowCascadeSplit2 = Mathf.Min(1.0f, Mathf.Max(0.0f, m_ShadowCascadeSplit2));
m_ShadowMaxDistance = Mathf.Max(0.0f, m_ShadowMaxDistance);
m_ShadowCascadeCount = Math.Min(4, Math.Max(1, m_ShadowCascadeCount));
m_ShadowCascadeSplit0 = Mathf.Clamp01(m_ShadowCascadeSplit0);
m_ShadowCascadeSplit1 = Mathf.Clamp01(m_ShadowCascadeSplit1);
m_ShadowCascadeSplit2 = Mathf.Clamp01(m_ShadowCascadeSplit2);
m_SssProfileFilter1Variance.r = Mathf.Max(0.1f, m_SssProfileFilter1Variance.r);
m_SssProfileFilter1Variance.g = Mathf.Max(0.1f, m_SssProfileFilter1Variance.g);
m_SssProfileFilter1Variance.b = Mathf.Max(0.1f, m_SssProfileFilter1Variance.b);
m_SssProfileFilter1Variance.a = 0.0f;
m_SssProfileFilter2Variance.r = Mathf.Max(0.1f, m_SssProfileFilter2Variance.r);
m_SssProfileFilter2Variance.g = Mathf.Max(0.1f, m_SssProfileFilter2Variance.g);
m_SssProfileFilter2Variance.b = Mathf.Max(0.1f, m_SssProfileFilter2Variance.b);
m_SssProfileFilter2Variance.a = 0.0f;
m_SssProfileFilterLerpWeight = Mathf.Clamp01(m_SssProfileFilterLerpWeight);
m_SssBilateralScale = Mathf.Clamp01(m_SssBilateralScale);
OnSkyRendererChanged();
}

28
Assets/ScriptableRenderLoop/HDRenderPipeline/SceneSettings/Editor/CommonSettingsEditor.cs


public readonly GUIContent shadowsCascades = new GUIContent("Cascade values");
public readonly GUIContent[] shadowSplits = new GUIContent[] { new GUIContent("Split 0"), new GUIContent("Split 1"), new GUIContent("Split 2") };
public readonly GUIContent sssCategory = new GUIContent("Subsurface scattering");
public readonly GUIContent sssProfileFilter1Variance = new GUIContent("SSS profile filter #1 variance", "Determines the shape of the 1st Gaussian filter. Increases the strength of the blur of the corresponding color channel.");
public readonly GUIContent sssProfileFilter2Variance = new GUIContent("SSS profile filter #2 variance", "Determines the shape of the 2nd Gaussian filter. Increases the strength of the blur of the corresponding color channel.");
public readonly GUIContent sssProfileFilterLerpWeight = 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 static Styles s_Styles = null;

private SerializedProperty m_ShadowCascadeCount;
private SerializedProperty[] m_ShadowCascadeSplits = new SerializedProperty[3];
// Subsurface scattering
private SerializedProperty m_SssProfileFilter1Variance;
private SerializedProperty m_SssProfileFilter2Variance;
private SerializedProperty m_SssProfileFilterLerpWeight;
private SerializedProperty m_SssBilateralScale;
void OnEnable()
{
m_SkyRenderer = serializedObject.FindProperty("m_SkyRendererTypeName");

m_SkyRendererFullTypeNames.Add("");
m_SkyRendererTypeValues.Add(m_SkyRendererTypeValues.Count);
m_SkyRendererTypes.Add(null);
m_SssProfileFilter1Variance = serializedObject.FindProperty("m_SssProfileFilter1Variance");
m_SssProfileFilter2Variance = serializedObject.FindProperty("m_SssProfileFilter2Variance");
m_SssProfileFilterLerpWeight = serializedObject.FindProperty("m_SssProfileFilterLerpWeight");
m_SssBilateralScale = serializedObject.FindProperty("m_SssBilateralScale");
}
void OnSkyInspectorGUI()

EditorGUI.indentLevel--;
}
void OnSubsurfaceInspectorGUI()
{
EditorGUILayout.LabelField(styles.sssCategory);
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(m_SssProfileFilter1Variance, styles.sssProfileFilter1Variance);
EditorGUILayout.PropertyField(m_SssProfileFilter2Variance, styles.sssProfileFilter2Variance);
EditorGUILayout.PropertyField(m_SssProfileFilterLerpWeight, styles.sssProfileFilterLerpWeight);
EditorGUILayout.PropertyField(m_SssBilateralScale, styles.sssBilateralScale);
EditorGUI.indentLevel--;
}
public override void OnInspectorGUI()
{
serializedObject.Update();

OnSubsurfaceInspectorGUI();
serializedObject.ApplyModifiedProperties();
}

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


namespace UnityEngine.Experimental.Rendering
{
[System.Serializable]
public class SubsurfaceScatteringProfile
{
public const int numSamples = 7;
Color m_filter1Variance;
Color m_filter2Variance;
float m_filterLerpWeight;
Vector4[] m_filterKernel;
bool m_kernelNeedsUpdate;
// --- Methods ---
public Color filter1Variance
{
get { return m_filter1Variance; }
set { if (m_filter1Variance != value) { m_filter1Variance = value; m_kernelNeedsUpdate = true; } }
}
public Color filter2Variance
{
get { return m_filter2Variance; }
set { if (m_filter2Variance != value) { m_filter2Variance = value; m_kernelNeedsUpdate = true; } }
}
public float filterLerpWeight
{
get { return m_filterLerpWeight; }
set { if (m_filterLerpWeight != value) { m_filterLerpWeight = value; m_kernelNeedsUpdate = true; } }
}
public Vector4[] filterKernel
{
get { if (m_kernelNeedsUpdate) ComputeKernel(); return m_filterKernel; }
}
public static SubsurfaceScatteringProfile Default
{
get
{
SubsurfaceScatteringProfile profile = new SubsurfaceScatteringProfile();
profile.filter1Variance = new Color(0.3f, 0.3f, 0.3f, 0.0f);
profile.filter2Variance = new Color(1.0f, 1.0f, 1.0f, 0.0f);
profile.filterLerpWeight = 0.5f;
profile.ComputeKernel();
return profile;
}
}
static float EvaluateZeroMeanGaussian(float x, float variance)
{
return Mathf.Exp(-x * x / (2 * variance)) / Mathf.Sqrt(2 * Mathf.PI * variance);
}
static float EvaluateGaussianCombination(float x, float variance1, float variance2, float lerpWeight)
{
return Mathf.Lerp(EvaluateZeroMeanGaussian(x, variance1),
EvaluateZeroMeanGaussian(x, variance2), lerpWeight);
}
void ComputeKernel()
{
// Our goal is to blur the image using a filter which is represented
// as a product of a linear combination of two normalized 1D Gaussians
// as suggested by Jimenez et al. in "Separable Subsurface Scattering".
// A normalized (i.e. energy-preserving) 1D Gaussian with the mean of 0
// is defined as follows: G1(x, v) = exp(-x� / (2 * v)) / sqrt(2 * Pi * v),
// where 'v' is variance and 'x' is the radial distance from the origin.
// Using the weight 'w', our 1D and the resulting 2D filters are given as:
// A1(v1, v2, w, x) = G1(x, v1) * (1 - w) + G1(r, v2) * w,
// A2(v1, v2, w, x, y) = A1(v1, v2, w, x) * A1(v1, v2, w, y).
// The resulting filter function is a non-Gaussian PDF.
// It is separable by design, but generally not radially symmmetric.
// TODO: importance-sample the truncated normal distribution:
// https://people.sc.fsu.edu/~jburkardt/cpp_src/truncated_normal/truncated_normal.html
// ALternatively, use a quadrature rule for the truncated normal PDF:
// https://people.sc.fsu.edu/~jburkardt/cpp_src/truncated_normal_rule/truncated_normal_rule.html
// For now, we use an ad-hoc approach.
// We truncate the distribution at the radius of 3 standard deviations.
float averageRadius1 = Mathf.Sqrt(m_filter1Variance.r)
+ Mathf.Sqrt(m_filter1Variance.g)
+ Mathf.Sqrt(m_filter1Variance.b);
float averageRadius2 = Mathf.Sqrt(m_filter2Variance.r)
+ Mathf.Sqrt(m_filter2Variance.g)
+ Mathf.Sqrt(m_filter2Variance.b);
float radius = Mathf.Lerp(averageRadius1, averageRadius2, m_filterLerpWeight);
// We compute sample positions and weights using Gauss�Legendre quadrature.
// The formula for the interval [a, b] is given here:
// https://en.wikipedia.org/wiki/Gaussian_quadrature#Change_of_interval
// Ref: http://keisan.casio.com/exec/system/1329114617
float[] unitAbscissae = { 0.0f, 0.40584515f, -0.40584515f, 0.74153118f, -0.74153118f, 0.94910791f, -0.94910791f };
float[] unitWeights = { 0.41795918f, 0.38183005f, 0.38183005f, 0.27970539f, 0.27970539f, 0.12948496f, 0.12948496f };
if (m_filterKernel == null)
{
m_filterKernel = new Vector4[numSamples];
}
for (int i = 0; i < numSamples; ++i)
{
// Perform the change of interval: {a, b} = {-radius, radius}.
float weight = radius * unitWeights[i];
float position = radius * unitAbscissae[i];
m_filterKernel[i].x = weight * EvaluateGaussianCombination(position, m_filter1Variance.r, m_filter2Variance.r, m_filterLerpWeight);
m_filterKernel[i].y = weight * EvaluateGaussianCombination(position, m_filter1Variance.g, m_filter2Variance.g, m_filterLerpWeight);
m_filterKernel[i].z = weight * EvaluateGaussianCombination(position, m_filter1Variance.b, m_filter2Variance.b, m_filterLerpWeight);
m_filterKernel[i].w = position;
}
m_kernelNeedsUpdate = false;
}
}
[System.Serializable]
public class SubsurfaceScatteringParameters
{
public const int numProfiles = 1;
public SubsurfaceScatteringProfile[] profiles;
public float bilateralScale;
// --- Methods ---
public static SubsurfaceScatteringParameters Default
{
get
{
SubsurfaceScatteringParameters parameters = new SubsurfaceScatteringParameters();
parameters.profiles = new SubsurfaceScatteringProfile[numProfiles];
for (int i = 0; i < numProfiles; i++)
{
parameters.profiles[i] = SubsurfaceScatteringProfile.Default;
}
parameters.bilateralScale = 0.4f;
return parameters;
}
}
}
}

12
Assets/ScriptableRenderLoop/HDRenderPipeline/SceneSettings/SubsurfaceScatteringParameters.cs.meta


fileFormatVersion: 2
guid: e9493dbe9d624704d9a80a75d52835b3
timeCreated: 1485361779
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
正在加载...
取消
保存