using System ;
#if UNITY_EDITOR
using UnityEditor ;
using UnityEditor ;
public class SSSConstants
public class SssConstants
public const int SSS_PROFILES_MAX = 8 ;
public const int SSS_N_PROFILES = 1 6 ; // Max. number of profiles, including the slot taken by the neutral profile
public const int SSS_NEUTRAL_PROFILE_ID = SSS_N_PROFILES - 1 ; // Does not result in blurring
public const int SSS_N_SAMPLES_NEAR_FIELD = 6 4 ; // Used for extreme close ups; must be a power of 2
public const int SSS_N_SAMPLES_FAR_FIELD = 3 2 ; // Used at a regular distance; must be a power of 2
public const int SSS_TRSM_MODE_NONE = 0 ;
public const int SSS_TRSM_MODE_THIN = 1 ;
public enum TexturingMode : int { PreAndPostScatter = 0 , PostScatter = 1 } ;
public enum TexturingMode : uint { PreAndPostScatter = 0 , PostScatter = 1 } ;
public enum TransmissionMode : uint { None = SssConstants . SSS_TRSM_MODE_NONE , ThinObject = SssConstants . SSS_TRSM_MODE_THIN , Regular } ;
public const int numSamples = 1 1 ; // Must be an odd number
[ColorUsage(false, true, 0.05f, 2.0f, 1.0f, 1.0f)]
public Color scatterDistance1 ;
[ColorUsage(false, true, 0.05f, 2.0f, 1.0f, 1.0f)]
public Color scatterDistance2 ;
public float lerpWeight ;
public Color surfaceAlbedo ; // Color, 0 to 1
public float lenVolMeanFreePath ; // Length of the volume mean free path (in millimeters)
public bool enableTransmission ;
public bool enableThinObject ;
public Color tintColor ;
public Vector2 thicknessRemap ;
public TransmissionMode transmissionMode ;
public Vector2 thicknessRemap ; // X = min, Y = max (in millimeters)
public float worldScale ; // Size of the world unit in meters
public int settingsIndex ;
public int settingsIndex ; // SubsurfaceScatteringSettings.profiles[i]
Vector4 [ ] m_FilterKernel ;
Vector3 m_S ; // RGB = shape parameter: S = 1 / D
[SerializeField]
float m_ScatteringDistance ; // Filter radius (in millimeters)
Vector3 [ ] m_HalfRcpVariances ;
Vector2 [ ] m_FilterKernelNearField ; // X = radius, Y = reciprocal of the PDF
Vector4 m_HalfRcpWeightedVariances ;
Vector2 [ ] m_FilterKernelFarField ; // X = radius, Y = reciprocal of the PDF
scatterDistance1 = new Color ( 0.3f , 0.3f , 0.3f , 0.0f ) ;
scatterDistance2 = new Color ( 0.6f , 0.6f , 0.6f , 0.0f ) ;
lerpWeight = 0.5f ;
texturingMode = TexturingMode . PreAndPostScatter ;
enableTransmission = false ;
enableThinObject = false ;
tintColor = Color . white ;
thicknessRemap = new Vector2 ( 0 , 1 ) ;
settingsIndex = SubsurfaceScatteringSettings . neutralProfileID ; // Updated by SubsurfaceScatteringSettings.OnValidate() once assigned
UpdateKernelAndVarianceData ( ) ;
}
surfaceAlbedo = Color . white ;
lenVolMeanFreePath = 0.5f ;
texturingMode = TexturingMode . PreAndPostScatter ;
transmissionMode = TransmissionMode . None ;
thicknessRemap = new Vector2 ( 0.0f , 5.0f ) ;
worldScale = 1.0f ;
settingsIndex = SssConstants . SSS_NEUTRAL_PROFILE_ID ; // Updated by SubsurfaceScatteringSettings.OnValidate() once assigned
public Vector4 [ ] filterKernel
{
// Set via UpdateKernelAndVarianceData().
get { return m_FilterKernel ; }
BuildKernel ( ) ;
public Vector3 [ ] halfRcpVariances
// Ref: Approximate Reflectance Profiles for Efficient Subsurface Scattering by Pixar.
public void BuildKernel ( )
// Set via UpdateKernelAndVarianceData().
get { return m_HalfRcpVariances ; }
}
public Vector4 halfRcpWeightedVariances
{
// Set via UpdateKernelAndVarianceData().
get { return m_HalfRcpWeightedVariances ; }
}
public void UpdateKernelAndVarianceData ( )
{
if ( m_FilterKernel = = null | | m_FilterKernel . Length ! = numSamples )
if ( m_FilterKernelNearField = = null | | m_FilterKernelNearField . Length ! = SssConstants . SSS_N_SAMPLES_NEAR_FIELD )
m_FilterKernel = new Vector4 [ numSamples ] ;
m_FilterKernelNearField = new Vector2 [ SssConstants . SSS_N_SAMPLES_NEAR_FIELD ] ;
if ( m_HalfRcpVariances = = null )
if ( m_FilterKernelFarField = = null | | m_FilterKernelFarField . Length ! = SssConstants . SSS_N_SAMPLES_FAR_FIELD )
m_HalfRcpVariances = new Vector3 [ 2 ] ;
m_FilterKernelFarField = new Vector2 [ SssConstants . SSS_N_SAMPLES_FAR_FIELD ] ;
// Apply the three-sigma rule.
Color stdDev1 = scatterDistance1 * ( 1.0f / 3.0f ) ;
Color stdDev2 = scatterDistance2 * ( 1.0f / 3.0f ) ;
// 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 * 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 symmetric.
m_S = new Vector3 ( ) ;
// Find the widest Gaussian across 3 color channels.
float maxStdDev1 = Mathf . Max ( stdDev1 . r , stdDev1 . g , stdDev1 . b ) ;
float maxStdDev2 = Mathf . Max ( stdDev2 . r , stdDev2 . g , stdDev2 . b ) ;
// Evaluate the fit for diffuse surface transmission.
m_S . x = FindFitForS ( surfaceAlbedo . r ) ;
m_S . y = FindFitForS ( surfaceAlbedo . g ) ;
m_S . z = FindFitForS ( surfaceAlbedo . b ) ;
Vector3 weightSum = new Vector3 ( 0 , 0 , 0 ) ;
// Compute { 1 / D = S / L } as you can substitute s = 1 / d in all formulas.
m_S . x * = 1.0f / lenVolMeanFreePath ;
m_S . y * = 1.0f / lenVolMeanFreePath ;
m_S . z * = 1.0f / lenVolMeanFreePath ;
float rcpNumSamples = 1.0f / numSamples ;
// We importance sample the color channel with the highest albedo value,
// since higher albedo values result in scattering over a larger distance.
// S(A) is a monotonically decreasing function.
float s = Mathf . Min ( m_S . x , m_S . y , m_S . z ) ;
// Importance sample the linear combination of two Gaussians.
for ( uint i = 0 ; i < numSamples ; i + + )
// Importance sample the normalized diffusion profile for the computed value of 's'.
// ------------------------------------------------------------------------------------
// R(r, s) = s * (Exp[-r * s] + Exp[-r * s / 3]) / (8 * Pi * r)
// PDF(r, s) = s * (Exp[-r * s] + Exp[-r * s / 3]) / 4
// CDF(r, s) = 1 - 1/4 * Exp[-r * s] - 3/4 * Exp[-r * s / 3]
// ------------------------------------------------------------------------------------
// Importance sample the near field kernel.
for ( int i = 0 ; i < SssConstants . SSS_N_SAMPLES_NEAR_FIELD ; i + + )
float u = ( i < = numSamples / 2 ) ? 0.5f - i * rcpNumSamples // The center and to the left
: ( i + 0.5f ) * rcpNumSamples ; // From the center to the right
float p = i * ( 1.0f / SssConstants . SSS_N_SAMPLES_NEAR_FIELD ) ;
float r = KernelCdfInverse ( p , s ) ;
// N.b.: computation of normalized weights, and multiplication by the surface albedo
// of the actual geometry is performed at runtime (in the shader).
m_FilterKernelNearField [ i ] . x = r ;
m_FilterKernelNearField [ i ] . y = 1.0f / KernelPdf ( r , s ) ;
}
float pos = GaussianCombinationCdfInverse ( u , maxStdDev1 , maxStdDev2 , lerpWeight ) ;
float pdf = GaussianCombination ( pos , maxStdDev1 , maxStdDev2 , lerpWeight ) ;
m_ScatteringDistance = m_FilterKernelNearField [ SssConstants . SSS_N_SAMPLES_NEAR_FIELD - 1 ] . x ;
Vector3 val ;
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 ) ;
// TODO: far field.
}
public Vector3 shapeParameter
{
// Set in BuildKernel().
get { return m_S ; }
}
// 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 ;
public float scatteringDistance
{
// Set in BuildKernel().
get { return m_ScatteringDistance ; }
}
weightSum . x + = m_FilterKernel [ i ] . x ;
weightSum . y + = m_FilterKernel [ i ] . y ;
weightSum . z + = m_FilterKernel [ i ] . z ;
}
public Vector2 [ ] filterKernelNearField
{
// Set in BuildKernel().
get { return m_FilterKernelNearField ; }
}
public Vector2 [ ] filterKernelFarField
{
// Set in BuildKernel().
get { return m_FilterKernelFarField ; }
}
// Renormalize the weights to conserve energy.
for ( uint i = 0 ; i < numSamples ; i + + )
{
m_FilterKernel [ i ] . x * = 1 / weightSum . x ;
m_FilterKernel [ i ] . y * = 1 / weightSum . y ;
m_FilterKernel [ i ] . z * = 1 / weightSum . z ;
}
// --- Private Methods ---
// 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 ) ;
static float FindFitForS ( float A )
{
return 1.9f - A + 3.5f * ( A - 0.8f ) * ( A - 0.8f ) ;
}
Vector4 weightedStdDev ;
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 ) ;
static float KernelVal ( float r , float s )
{
return s * ( Mathf . Exp ( - r * s ) + Mathf . Exp ( - r * s * ( 1.0f / 3.0f ) ) ) / ( 8.0f * Mathf . PI * r ) ;
}
// 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 ) ;
// Computes the value of the integrand over a disk: (2 * PI * r) * KernelVal().
static float KernelValCircle ( float r , float s )
{
return 0.25f * s * ( Mathf . Exp ( - r * s ) + Mathf . Exp ( - r * s * ( 1.0f / 3.0f ) ) ) ;
// --- Private Methods ---
static float KernelPdf ( float r , float s )
{
return KernelValCircle ( r , s ) ;
}
static float Gaussian ( float x , float stdDev )
static float KernelCdf ( float r , float s )
float variance = stdDev * stdDev ;
return Mathf . Exp ( - x * x / ( 2 * variance ) ) / Mathf . Sqrt ( 2 * Mathf . PI * variance ) ;
return 1.0f - 0.25f * Mathf . Exp ( - r * s ) - 0.75f * Mathf . Exp ( - r * s * ( 1.0f / 3.0f ) ) ;
static float GaussianCombination ( float x , float stdDev1 , float stdDev2 , float lerpWeight )
static float KernelCdfDerivative1 ( float r , float s )
return Mathf . Lerp ( Gaussian ( x , stdDev1 ) , Gaussian ( x , stdDev2 ) , lerpWeight ) ;
return 0.25f * s * Mathf . Exp ( - r * s ) * ( 1.0f + Mathf . Exp ( r * s * ( 2.0f / 3.0f ) ) ) ;
static float RationalApproximation ( float t )
static float KernelCdfDerivative2 ( float r , float s )
// 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 ) ;
return ( - 1.0f / 1 2.0f ) * s * s * Mathf . Exp ( - r * s ) * ( 3.0f + Mathf . Exp ( r * s * ( 2.0f / 3.0f ) ) ) ;
// Ref: https://www.johndcook.com/blog/csharp_phi_inverse/
static float NormalCdfInverse ( float p , float stdDev )
// The CDF is not analytically invertible, so we use Halley's Method of root finding.
// { f(r, s, p) = CDF(r, s) - p = 0 } with the initial guess { r = (10^p - 1) / s }.
static float KernelCdfInverse ( float p , float s )
float x ;
// Supply the initial guess.
float r = ( Mathf . Pow ( 1 0.0f , p ) - 1.0f ) / s ;
float t = float . MaxValue ;
if ( p < 0.5 )
{
// F^-1(p) = - G^-1(p)
x = - RationalApproximation ( Mathf . Sqrt ( - 2.0f * Mathf . Log ( p ) ) ) ;
}
else
while ( true )
// F^-1(p) = G^-1(1-p)
x = RationalApproximation ( Mathf . Sqrt ( - 2.0f * Mathf . Log ( 1.0f - p ) ) ) ;
float f0 = KernelCdf ( r , s ) - p ;
float f1 = KernelCdfDerivative1 ( r , s ) ;
float f2 = KernelCdfDerivative2 ( r , s ) ;
float dr = f0 / ( f1 * ( 1.0f - f0 * f2 / ( 2.0f * f1 * f1 ) ) ) ;
if ( Mathf . Abs ( dr ) < t )
{
r = r - dr ;
t = Mathf . Abs ( dr ) ;
}
else
{
// Converged to the best result.
break ;
}
return x * stdDev ;
}
static float GaussianCombinationCdfInverse ( float p , float stdDev1 , float stdDev2 , float lerpWeight )
{
return Mathf . Lerp ( NormalCdfInverse ( p , stdDev1 ) , NormalCdfInverse ( p , stdDev2 ) , lerpWeight ) ;
return r ;
}
}
public const int neutralProfileID = SSSConstants . SSS_PROFILES_MAX - 1 ;
public int numProfiles ;
public int numProfiles ; // Excluding the neutral profile
// Below is the cache filled during OnValidate().
[NonSerialized] public int texturingModeFlags ; // 1 bit/profile; 0 = PreAndPostScatter, 1 = PostScatter
[NonSerialized] public float [ ] transmissionType ; // TODO: no int array suppport in shader in Unity :(
[NonSerialized] public Vector4 [ ] tintColors ; // For transmission; alpha is unused
[NonSerialized] public float [ ] thicknessRemaps ; // Remap: 0 = start, 1 = end - start
[NonSerialized] public Vector4 [ ] halfRcpVariancesAndLerpWeights ;
[NonSerialized] public Vector4 [ ] halfRcpWeightedVariances ;
[NonSerialized] public Vector4 [ ] filterKernels ;
// Below are the cached values.
[NonSerialized] public uint texturingModeFlags ; // 1 bit/profile; 0 = PreAndPostScatter, 1 = PostScatter
[NonSerialized] public uint transmissionFlags ; // 2 bit/profile; 0 = inf. thick, 1 = thin, 2 = regular
[NonSerialized] public float [ ] thicknessRemaps ; // Remap: 0 = start, 1 = end - start
[NonSerialized] public Vector4 [ ] shapeParameters ; // RGB = S = 1 / D, A = filter radius
[NonSerialized] public Vector4 [ ] surfaceAlbedos ; // RGB = color, A = unused
[NonSerialized] public float [ ] worldScales ; // Size of the world unit in meters
[NonSerialized] public float [ ] filterKernelsNearField ; // 0 = radius, 1 = reciprocal of the PDF
[NonSerialized] public float [ ] filterKernelsFarField ; // 0 = radius, 1 = reciprocal of the PDF
numProfiles = 1 ;
profiles = new SubsurfaceScatteringProfile [ numProfiles ] ;
profiles [ 0 ] = null ;
transmissionType = null ;
texturingModeFlags = 0 ;
tintColors = null ;
thicknessRemaps = null ;
halfRcpVariancesAndLerpWeights = null ;
halfRcpWeightedVariances = null ;
filterKernels = null ;
numProfiles = 1 ;
profiles = new SubsurfaceScatteringProfile [ numProfiles ] ;
profiles [ 0 ] = null ;
texturingModeFlags = 0 ;
transmissionFlags = 0 ;
thicknessRemaps = null ;
shapeParameters = null ;
filterKernelsNearField = null ;
filterKernelsFarField = null ;
UpdateCache ( ) ;
}
// Reserve one slot for the neutral profile.
numProfiles = Math . Min ( profiles . Length , SSS Constants . SSS_PROFILES_MAX - 1 ) ;
numProfiles = Math . Min ( profiles . Length , Sss Constants . SSS_N_ PROFILES - 1 ) ;
if ( profiles . Length ! = numProfiles )
{
}
}
Color c = new Color ( ) ;
c . r = Mathf . Clamp ( profiles [ i ] . scatterDistance1 . r , 0.05f , 2.0f ) ;
c . g = Mathf . Clamp ( profiles [ i ] . scatterDistance1 . g , 0.05f , 2.0f ) ;
c . b = Mathf . Clamp ( profiles [ i ] . scatterDistance1 . b , 0.05f , 2.0f ) ;
c . a = 0.0f ;
profiles [ i ] . scatterDistance1 = c ;
c . r = Mathf . Clamp ( profiles [ i ] . scatterDistance2 . r , 0.05f , 2.0f ) ;
c . g = Mathf . Clamp ( profiles [ i ] . scatterDistance2 . g , 0.05f , 2.0f ) ;
c . b = Mathf . Clamp ( profiles [ i ] . scatterDistance2 . b , 0.05f , 2.0f ) ;
c . a = 0.0f ;
profiles [ i ] . scatterDistance2 = c ;
profiles [ i ] . lerpWeight = Mathf . Clamp01 ( profiles [ i ] . lerpWeight ) ;
profiles [ i ] . tintColor . r = Mathf . Clamp01 ( profiles [ i ] . tintColor . r ) ;
profiles [ i ] . tintColor . g = Mathf . Clamp01 ( profiles [ i ] . tintColor . g ) ;
profiles [ i ] . tintColor . b = Mathf . Clamp01 ( profiles [ i ] . tintColor . b ) ;
profiles [ i ] . tintColor . a = 1.0f ;
profiles [ i ] . thicknessRemap . y = Mathf . Max ( profiles [ i ] . thicknessRemap . y , 0 ) ;
profiles [ i ] . thicknessRemap . y = Mathf . Max ( profiles [ i ] . thicknessRemap . x , profiles [ i ] . thicknessRemap . y ) ;
profiles [ i ] . worldScale = Mathf . Max ( profiles [ i ] . worldScale , 0.001f ) ;
profiles [ i ] . UpdateKernelAndVarianceData ( ) ;
profiles [ i ] . BuildKernel ( ) ;
}
UpdateCache ( ) ;
{
texturingModeFlags = 0 ;
texturingModeFlags = transmissionFlags = 0 ;
if ( transmissionType = = null | | transmissionType . Length ! = ( SSSConstants . SSS_PROFILES_MAX ) )
const int thicknessRemapsLen = SssConstants . SSS_N_PROFILES * 2 ;
if ( thicknessRemaps = = null | | thicknessRemaps . Length ! = thicknessRemapsLen )
transmissionType = new float [ SSSConstants . SSS_PROFILES_MAX ] ;
thicknessRemaps = new float [ thicknessRemapsLen ] ;
if ( tintColors = = null | | tintColors . Length ! = SSSConstants . SSS_PROFILES_MAX )
const int worldScalesLen = SssConstants . SSS_N_PROFILES ;
if ( worldScales = = null | | worldScales . Length ! = worldScalesLen )
tintColors = new Vector4 [ SSSConstants . SSS_PROFILES_MAX ] ;
worldScales = new float [ worldScalesLen ] ;
if ( thicknessRemaps = = null | | thicknessRemaps . Length ! = ( SSSConstants . SSS_PROFILES_MAX * 2 ) )
const int shapeParametersLen = SssConstants . SSS_N_PROFILES ;
if ( shapeParameters = = null | | shapeParameters . Length ! = shapeParametersLen )
thicknessRemaps = new float [ SSSConstants . SSS_PROFILES_MAX * 2 ] ;
shapeParameters = new Vector4 [ shapeParametersLen ] ;
if ( halfRcpVariancesAndLerpWeights = = null | | halfRcpVariancesAndLerpWeights . Length ! = ( SSSConstants . SSS_PROFILES_MAX * 2 ) )
const int surfaceAlbedosLen = SssConstants . SSS_N_PROFILES ;
if ( surfaceAlbedos = = null | | surfaceAlbedos . Length ! = surfaceAlbedosLen )
halfRcpVariancesAndLerpWeights = new Vector4 [ SSSConstants . SSS_PROFILES_MAX * 2 ] ;
surfaceAlbedos = new Vector4 [ surfaceAlbedosLen ] ;
if ( halfRcpWeightedVariances = = null | | halfRcpWeightedVariances . Length ! = SSSConstants . SSS_PROFILES_MAX )
const int filterKernelsNearFieldLen = 2 * SssConstants . SSS_N_PROFILES * SssConstants . SSS_N_SAMPLES_NEAR_FIELD ;
if ( filterKernelsNearField = = null | | filterKernelsNearField . Length ! = filterKernelsNearFieldLen )
halfRcpWeightedVariances = new Vector4 [ SSSConstants . SSS_PROFILES_MAX ] ;
filterKernelsNearField = new float [ filterKernelsNearFieldLen ] ;
if ( filterKernels = = null | | filterKernels . Length ! = ( SSSConstants . SSS_PROFILES_MAX * SubsurfaceScatteringProfile . numSamples ) )
const int filterKernelsFarFieldLen = 2 * SssConstants . SSS_N_PROFILES * SssConstants . SSS_N_SAMPLES_FAR_FIELD ;
if ( filterKernelsFarField = = null | | filterKernelsFarField . Length ! = filterKernelsFarFieldLen )
filterKernels = new Vector4 [ SSSConstants . SSS_PROFILES_MAX * SubsurfaceScatteringProfile . numSamples ] ;
filterKernelsFarField = new float [ filterKernelsFarFieldLen ] ;
}
for ( int i = 0 ; i < numProfiles ; i + + )
texturingModeFlags | = ( ( int ) profiles [ i ] . texturingMode ) < < i ;
if ( profiles [ i ] . enableTransmission )
{
transmissionType [ i ] = ( float ) ( profiles [ i ] . enableThinObject ? Lit . TransmissionType . ThinObject : Lit . TransmissionType . Regular ) ;
}
else
Debug . Assert ( numProfiles < 1 6 , "Transmission flags (32-bit integer) cannot support more than 16 profiles." ) ;
texturingModeFlags | = ( uint ) profiles [ i ] . texturingMode < < i ;
transmissionFlags | = ( uint ) profiles [ i ] . transmissionMode < < i * 2 ;
thicknessRemaps [ 2 * i ] = profiles [ i ] . thicknessRemap . x ;
thicknessRemaps [ 2 * i + 1 ] = profiles [ i ] . thicknessRemap . y - profiles [ i ] . thicknessRemap . x ;
worldScales [ i ] = profiles [ i ] . worldScale ;
shapeParameters [ i ] = profiles [ i ] . shapeParameter ;
shapeParameters [ i ] . w = profiles [ i ] . scatteringDistance ;
surfaceAlbedos [ i ] = profiles [ i ] . surfaceAlbedo ;
for ( int j = 0 , n = SssConstants . SSS_N_SAMPLES_NEAR_FIELD ; j < n ; j + + )
transmissionType [ i ] = ( float ) Lit . TransmissionType . None ;
filterKernelsNearField [ 2 * ( n * i + j ) + 0 ] = profiles [ i ] . filterKernelNearField [ j ] . x ;
filterKernelsNearField [ 2 * ( n * i + j ) + 1 ] = profiles [ i ] . filterKernelNearField [ j ] . y ;
tintColors [ i ] = profiles [ i ] . tintColor ;
thicknessRemaps [ 2 * i ] = profiles [ i ] . thicknessRemap . x ;
thicknessRemaps [ 2 * i + 1 ] = profiles [ i ] . thicknessRemap . y - profiles [ i ] . thicknessRemap . x ;
halfRcpVariancesAndLerpWeights [ 2 * i ] = profiles [ i ] . halfRcpVariances [ 0 ] ;
halfRcpVariancesAndLerpWeights [ 2 * i ] . w = 1.0f - profiles [ i ] . lerpWeight ;
halfRcpVariancesAndLerpWeights [ 2 * i + 1 ] = profiles [ i ] . halfRcpVariances [ 1 ] ;
halfRcpVariancesAndLerpWeights [ 2 * i + 1 ] . w = profiles [ i ] . lerpWeight ;
halfRcpWeightedVariances [ i ] = profiles [ i ] . halfRcpWeightedVariances ;
for ( int j = 0 , n = SubsurfaceScatteringProfile . numSamples ; j < n ; j + + )
for ( int j = 0 , n = SssConstants . SSS_N_SAMPLES_FAR_FIELD ; j < n ; j + + )
filterKernels [ n * i + j ] = profiles [ i ] . filterKernel [ j ] ;
filterKernelsFarField [ 2 * ( n * i + j ) + 0 ] = profiles [ i ] . filterKernelFarField [ j ] . x ;
filterKernelsFarField [ 2 * ( n * i + j ) + 1 ] = profiles [ i ] . filterKernelFarField [ j ] . y ;
int i = neutralProfileID ;
int i = SssConstants . SSS_NEUTRAL_PROFILE_ID ;
shapeParameters [ i ] = Vector4 . zero ;
surfaceAlbedos [ i ] = Vector4 . zero ;
worldScales [ i ] = 1.0f ;
halfRcpWeightedVariances [ i ] = Vector4 . one ;
for ( int j = 0 , n = SssConstants . SSS_N_SAMPLES_NEAR_FIELD ; j < n ; j + + )
{
filterKernelsNearField [ 2 * ( n * i + j ) + 0 ] = 0.0f ;
filterKernelsNearField [ 2 * ( n * i + j ) + 1 ] = 1.0f ;
}
for ( int j = 0 , n = SubsurfaceScatteringProfile . numSamples ; j < n ; j + + )
for ( int j = 0 , n = SssConstants . SSS_N_SAMPLES_FAR_FIELD ; j < n ; j + + )
filterKernels [ n * i + j ] = Vector4 . one ;
filterKernels [ n * i + j ] . w = 0.0f ;
filterKernelsFarField [ 2 * ( n * i + j ) + 0 ] = 0.0f ;
filterKernelsFarField [ 2 * ( n * i + j ) + 1 ] = 1.0f ;
}
}
}
{
private class Styles
{
public readonly GUIContent sssProfilePreview0 = new GUIContent ( "Profile Preview" ) ;
public readonly GUIContent sssProfilePreview1 = new GUIContent ( "Shows the fraction of light scattered from the source as the radius increases to 1." ) ;
public readonly GUIContent sssProfilePreview2 = new GUIContent ( "Note that the intensity of the region in the center may be clamped." ) ;
public readonly GUIContent sssTransmittancePreview0 = new GUIContent ( "Transmittance Preview" ) ;
public readonly GUIContent sssTransmittancePreview1 = new GUIContent ( "Shows the fraction of light passing through the object for thickness values from the remap." ) ;
public readonly GUIContent sssTransmittancePreview2 = new GUIContent ( "Can be thought of as a cross section of a slab of material illuminated by a white light from the left." ) ;
public readonly GUIContent sssProfileScatterDistance1 = new GUIContent ( "Scatter Distance #1" , "The radius (in centimeters) of the 1st Gaussian filter, one per color channel. Alpha is ignored. The blur is energy-preserving, so a wide filter results in a large area with small contributions of individual samples. Smaller values increase the sharpness." ) ;
public readonly GUIContent sssProfileScatterDistance2 = new GUIContent ( "Scatter Distance #2" , "The radius (in centimeters) of the 2nd Gaussian filter, one per color channel. Alpha is ignored. The blur is energy-preserving, so a wide filter results in a large area with small contributions of individual samples. Smaller values increase the sharpness." ) ;
public readonly GUIContent sssProfileLerpWeight = new GUIContent ( "Filter Interpolation" , "Controls linear interpolation between the two Gaussian filters." ) ;
public readonly GUIContent sssTexturingMode = new GUIContent ( "Texturing Mode" , "Specifies when the diffuse texture should be applied." ) ;
public readonly GUIContent [ ] sssTexturingModeOptions = new GUIContent [ 2 ]
public readonly GUIContent sssProfilePreview0 = new GUIContent ( "Profile Preview" ) ;
public readonly GUIContent sssProfilePreview1 = new GUIContent ( "Shows the fraction of light scattered from the source (center)." ) ;
public readonly GUIContent sssProfilePreview2 = new GUIContent ( "The distance to the boundary of the image corresponds to the Scattering Distance." ) ;
public readonly GUIContent sssProfilePreview3 = new GUIContent ( "Note that the intensity of pixels around the center may be clipped." ) ;
public readonly GUIContent sssTransmittancePreview0 = new GUIContent ( "Transmittance Preview" ) ;
public readonly GUIContent sssTransmittancePreview1 = new GUIContent ( "Shows the fraction of light passing through the object for thickness values from the remap." ) ;
public readonly GUIContent sssTransmittancePreview2 = new GUIContent ( "Can be viewed as a cross section of a slab of material illuminated by white light from the left." ) ;
public readonly GUIContent sssProfileSurfaceAlbedo = new GUIContent ( "Surface Albedo" , "Color which determines the shape of the profile. Alpha is ignored." ) ;
public readonly GUIContent sssProfileLenVolMeanFreePath = new GUIContent ( "Volume Mean Free Path" , "The length of the volume mean free path (in millimeters) describes the average distance a photon travels within the volume before an extinction event occurs. Determines the effective radius of the filter." ) ;
public readonly GUIContent sssProfileScatteringDistance = new GUIContent ( "Scattering Distance" , "Effective radius of the filter (in millimeters). The blur is energy-preserving, so a wide filter results in a large area with small contributions of individual samples. Reducing the distance increases the sharpness of the result." ) ;
public readonly GUIContent sssTexturingMode = new GUIContent ( "Texturing Mode" , "Specifies when the diffuse texture should be applied." ) ;
public readonly GUIContent [ ] sssTexturingModeOptions = new GUIContent [ 2 ]
new GUIContent ( "Post-scatter" , "Texturing is performed only during the SSS pass. Effectively preserves the sharpness of the diffuse texture. Choose this mode if your diffuse texture already contains SSS lighting (e.g. a photo of skin)." )
new GUIContent ( "Post-scatter" , "Texturing is performed only during the SSS pass. Effectively preserves the sharpness of the diffuse texture. Choose this mode if your diffuse texture already contains SSS lighting (e.g. a photo of skin)." )
} ;
public readonly GUIContent sssProfileTransmissionMode = new GUIContent ( "Transmission Mode" , "Configures the simulation of light passing through thin objects. Depends on the thickness value (which is applied in the normal direction)." ) ;
public readonly GUIContent [ ] sssTransmissionModeOptions = new GUIContent [ 3 ]
{
new GUIContent ( "None" , "Disables transmission. Choose this mode for completely opaque, or very thick translucent objects." ) ,
new GUIContent ( "Thin Object" , "Choose this mode for thin objects, such as paper or leaves. Transmitted light reuses the shadowing state of the surface." ) ,
new GUIContent ( "Regular" , "Choose this mode for moderately thick objects. For performance reasons, transmitted light ignores occlusion (shadows)." )
public readonly GUIContent sssProfileTransmission = new GUIContent ( "Enable Transmission" , "Toggles simulation of light passing through thin objects. Depends on the thickness of the material." ) ;
public readonly GUIContent sssProfileTintColor = new GUIContent ( "Transmission Tint Color" , "Tints transmitted light." ) ;
public readonly GUIContent sssProfileThinObject = new GUIContent ( "Enable Thin Object" , "Define is the object is thin (paper, leaf) or not. Allow to get cheap transmission and shadow." ) ;
public readonly GUIContent sssProfileMinMaxThickness = new GUIContent ( "Min-Max Thickness" , "Shows the values of the thickness remap below (in centimeters)." ) ;
public readonly GUIContent sssProfileThicknessRemap = new GUIContent ( "Thickness Remap" , "Remaps the thickness parameter from [0, 1] to the desired range (in centimeters)." ) ;
public readonly GUIContent sssProfileMinMaxThickness = new GUIContent ( "Min-Max Thickness" , "Shows the values of the thickness remap below (in millimeters)." ) ;
public readonly GUIContent sssProfileThicknessRemap = new GUIContent ( "Thickness Remap" , "Remaps the thickness parameter from [0, 1] to the desired range (in millimeters)." ) ;
public readonly GUIContent sssProfileWorldScale = new GUIContent ( "World Scale" , "Size of the world unit in meters." ) ;
public readonly GUIStyle centeredMiniBoldLabel = new GUIStyle ( GUI . skin . label ) ;
private RenderTexture m_ProfileImage , m_TransmittanceImage ;
private Material m_ProfileMaterial , m_TransmittanceMaterial ;
private SerializedProperty m_ScatterDistance1 , m_ScatterDistance2 , m_LerpWeight , m_TintColor , m_ThinObject ,
m_TexturingMode , m_Transmission , m_ThicknessRemap ;
private SerializedProperty m_LenVolMeanFreePath , m_Scattering Distance , m_SurfaceAlbedo , m_S ,
m_TexturingMode , m_TransmissionMode , m_ThicknessRemap , m_WorldScale ;
m_ScatterDistance1 = serializedObject . FindProperty ( "scatterDistance1 " ) ;
m_ScatterDistance2 = serializedObject . FindProperty ( "scatterDistance2 " ) ;
m_LerpWeight = serializedObject . FindProperty ( "lerpWeight " ) ;
m_TexturingMode = serializedObject . FindProperty ( "texturingMode " ) ;
m_Transmission = serializedObject . FindProperty ( "enableTransmission " ) ;
m_ThinObject = serializedObject . FindProperty ( "enableThinObject " ) ;
m_TintColor = serializedObject . FindProperty ( "tintColor " ) ;
m_ThicknessRemap = serializedObject . FindProperty ( "thicknessRemap " ) ;
m_SurfaceAlbedo = serializedObject . FindProperty ( "surfaceAlbedo " ) ;
m_LenVolMeanFreePath = serializedObject . FindProperty ( "lenVolMeanFreePath " ) ;
m_ScatteringDistance = serializedObject . FindProperty ( "m_ScatteringDistance " ) ;
m_S = serializedObject . FindProperty ( "m_S " ) ;
m_TexturingMode = serializedObject . FindProperty ( "texturingMode " ) ;
m_TransmissionMode = serializedObject . FindProperty ( "transmissionMode " ) ;
m_ThicknessRemap = serializedObject . FindProperty ( "thicknessRemap " ) ;
m_WorldScale = serializedObject . FindProperty ( "worldScale " ) ;
m_ProfileMaterial = Utilities . CreateEngineMaterial ( "Hidden/HDRenderPipeline/DrawGaussian Profile" ) ;
m_ProfileMaterial = Utilities . CreateEngineMaterial ( "Hidden/HDRenderPipeline/DrawSss Profile" ) ;
m_TransmittanceImage = new RenderTexture ( 1 6 , 2 5 6 , 0 , RenderTextureFormat . DefaultHDR ) ;
m_TransmittanceImage = new RenderTexture ( 1 6 , 2 5 6 , 0 , RenderTextureFormat . DefaultHDR ) ;
}
public override void OnInspectorGUI ( )
EditorGUI . BeginChangeCheck ( ) ;
{
EditorGUILayout . PropertyField ( m_ScatterDistance1 , styles . sssProfileScatterDistance1 ) ;
EditorGUILayout . PropertyField ( m_ScatterDistance2 , styles . sssProfileScatterDistance2 ) ;
EditorGUILayout . PropertyField ( m_LerpWeight , styles . sssProfileLerpWeight ) ;
m_TexturingMode . intValue = EditorGUILayout . Popup ( styles . sssTexturingMode , m_TexturingMode . intValue , styles . sssTexturingModeOptions ) ;
EditorGUILayout . PropertyField ( m_Transmission , styles . sssProfileTransmission ) ;
EditorGUILayout . PropertyField ( m_TintColor , styles . sssProfileTintColor ) ;
EditorGUILayout . PropertyField ( m_SurfaceAlbedo , styles . sssProfileSurfaceAlbedo ) ;
m_LenVolMeanFreePath . floatValue = EditorGUILayout . Slider ( styles . sssProfileLenVolMeanFreePath , m_LenVolMeanFreePath . floatValue , 0.01f , 1.0f ) ;
GUI . enabled = false ;
EditorGUILayout . PropertyField ( m_ScatteringDistance , styles . sssProfileScatteringDistance ) ;
GUI . enabled = true ;
EditorGUILayout . PropertyField ( m_ThinObject , styles . sssProfileThinObject ) ;
m_TexturingMode . intValue = EditorGUILayout . Popup ( styles . sssTexturingMode , m_TexturingMode . intValue , styles . sssTexturingModeOptions ) ;
m_TransmissionMode . intValue = EditorGUILayout . Popup ( styles . sssProfileTransmissionMode , m_TransmissionMode . intValue , styles . sssTransmissionModeOptions ) ;
EditorGUILayout . MinMaxSlider ( styles . sssProfileThicknessRemap , ref thicknessRemap . x , ref thicknessRemap . y , 0 , 1 0 ) ;
EditorGUILayout . MinMaxSlider ( styles . sssProfileThicknessRemap , ref thicknessRemap . x , ref thicknessRemap . y , 0.0f , 5 0.0f ) ;
EditorGUILayout . PropertyField ( m_WorldScale , styles . sssProfileWorldScale ) ;
EditorGUILayout . LabelField ( styles . sssProfilePreview3 , EditorStyles . centeredGreyMiniLabel ) ;
// Apply the three-sigma rule.
Color stdDev1 = m_ScatterDistance1 . colorValue * ( 1.0f / 3.0f ) ;
Color stdDev2 = m_ScatterDistance2 . colorValue * ( 1.0f / 3.0f ) ;
float d = m_ScatteringDistance . floatValue ;
Vector4 A = m_SurfaceAlbedo . colorValue ;
Vector3 S = m_S . vector3Value ;
Vector2 R = m_ThicknessRemap . vector2Value ;
m_ProfileMaterial . SetColor ( "_StdDev1" , stdDev1 ) ;
m_ProfileMaterial . SetColor ( "_StdDev2" , stdDev2 ) ;
m_ProfileMaterial . SetFloat ( "_LerpWeight" , m_LerpWeight . floatValue ) ;
m_ProfileMaterial . SetFloat ( "_ScatteringDistance" , d ) ;
m_ProfileMaterial . SetVector ( "_SurfaceAlbedo" , A ) ;
m_ProfileMaterial . SetVector ( "_ShapeParameter" , S ) ;
EditorGUILayout . Space ( ) ;
EditorGUILayout . LabelField ( styles . sssTransmittancePreview0 , styles . centeredMiniBoldLabel ) ;
EditorGUILayout . LabelField ( styles . sssTransmittancePreview1 , EditorStyles . centeredGreyMiniLabel ) ;
bool transmissionEnabled = m_TransmissionMode . intValue ! = ( int ) SubsurfaceScatteringProfile . TransmissionMode . None ;
m_TransmittanceMaterial . SetColor ( "_StdDev1" , stdDev1 ) ;
m_TransmittanceMaterial . SetColor ( "_StdDev2" , stdDev2 ) ;
m_TransmittanceMaterial . SetFloat ( "_LerpWeight" , m_LerpWeight . floatValue ) ;
m_TransmittanceMaterial . SetVector ( "_ThicknessRemap" , m_ThicknessRemap . vector2Value ) ;
m_TransmittanceMaterial . SetVector ( "_TintColor" , m_TintColor . colorValue ) ;
m_TransmittanceMaterial . SetFloat ( "_ScatteringDistance" , d ) ;
m_TransmittanceMaterial . SetVector ( "_SurfaceAlbedo" , transmissionEnabled ? A : Vector4 . zero ) ;
m_TransmittanceMaterial . SetVector ( "_ShapeParameter" , S ) ;
m_TransmittanceMaterial . SetVector ( "_ThicknessRemap" , R ) ;
EditorGUI . DrawPreviewTexture ( GUILayoutUtility . GetRect ( 1 6 , 1 6 ) , m_TransmittanceImage , m_TransmittanceMaterial , ScaleMode . ScaleToFit , 1 6.0f ) ;
serializedObject . ApplyModifiedProperties ( ) ;
}
}
#endif
}
}