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 ! = numSample s )
if ( m_FilterKernel = = null | | m_FilterKernel . Length ! = numVector s )
m_FilterKernel = new Vector4 [ numSample s ] ;
m_FilterKernel = new Vector4 [ numVector s ] ;
}
// 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 ;
// Store (1 / (2 * variance)) instead of the distance to the 1st sample (which is implicitly 0).
float weightedStdDev = Mathf . Lerp ( maxStdDev1 , maxStdDev2 , m_LerpWeight ) ;
m_FilterKernel [ 0 ] . w = 1.0f / ( 2.0f * weightedStdDev * weightedStdDev ) ;
Vector3 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 ) ;
m_KernelNeedsUpdate = false ;
// Store (1 / (2 * WeightedVariance)) 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 ) ;
}
}
int m_NumProfiles ;
[SerializeField]
SubsurfaceScatteringProfile [ ] m_Profiles ;
[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 ( )
{
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" ) ;
}
}
}