You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
165 lines
6.7 KiB
165 lines
6.7 KiB
TEXTURE3D(_HairAttenuation);
|
|
|
|
#define FRONT 0
|
|
#define BACK 1
|
|
|
|
struct HairScatteringData
|
|
{
|
|
half3 averageScattering [2];
|
|
};
|
|
|
|
#define HALF_SQRT_INV_PI 0.28209479177387814347
|
|
#define HALF_SQRT_3_DIV_PI 0.48860251190291992158
|
|
|
|
// Returns the approximate strand count in direction L from an L1 band spherical harmonic.
|
|
float DecodeHairStrandCount(float3 L, float4 strandCountProbe)
|
|
{
|
|
float4 Ylm = float4(
|
|
HALF_SQRT_INV_PI,
|
|
HALF_SQRT_3_DIV_PI * L.y,
|
|
HALF_SQRT_3_DIV_PI * L.z,
|
|
HALF_SQRT_3_DIV_PI * L.x
|
|
);
|
|
|
|
return max(dot(strandCountProbe, Ylm), 0);
|
|
}
|
|
|
|
float GetDirectFraction(BSDFData bsdfData, float strandCount)
|
|
{
|
|
// Defer to the higher quality spline visibility for this light, if any.
|
|
// Otherwise fall back to the coarse approximation from the spherical harmonic.
|
|
return bsdfData.visibility > -1 ? bsdfData.visibility : 1 - saturate(strandCount);
|
|
}
|
|
|
|
float3 ComputeDualScattering(BSDFData bsdfData, HairAngle angles, int strandCount, inout float3 Fs)
|
|
{
|
|
// Fetch pre-computed data
|
|
// ---------------------------------------------------------
|
|
|
|
HairScatteringData scatteringData;
|
|
ZERO_INITIALIZE(HairScatteringData, scatteringData);
|
|
{
|
|
// Prepare the sampling coordinate.
|
|
// (Note, currently clamping the smoothness due to noise in the LUT, this can be fixed by preintegrating with importance sampling).
|
|
const half X = min(PerceptualRoughnessToPerceptualSmoothness(bsdfData.perceptualRoughness), 0.6);
|
|
const half Y = abs(angles.sinThetaI);
|
|
const half3 Z = bsdfData.diffuseColor;
|
|
|
|
// Sample the LUT for each wavelength.
|
|
// Note that we parameterize by diffuse color, not absorption, to fit in [0, 1].
|
|
// It might be possible to fully support azimuthal roughness by separating the integral and using extra 2D lut.
|
|
// However the effect of azimuthal is subtle for the scattering term, mostly producing a much more saturated result for low absorptions.
|
|
// Because of this, it might be much simpler and easier to approximate the a. roughness by modulating the attenuation below.
|
|
const half2 R = SAMPLE_TEXTURE3D_LOD(_HairAttenuation, s_linear_clamp_sampler, float3(X, Y, Z.r), 0).xy;
|
|
const half2 G = SAMPLE_TEXTURE3D_LOD(_HairAttenuation, s_linear_clamp_sampler, float3(X, Y, Z.g), 0).xy;
|
|
const half2 B = SAMPLE_TEXTURE3D_LOD(_HairAttenuation, s_linear_clamp_sampler, float3(X, Y, Z.b), 0).xy;
|
|
|
|
scatteringData.averageScattering[FRONT] = float3(R.x, G.x, B.x);
|
|
scatteringData.averageScattering[BACK] = float3(R.y, G.y, B.y);
|
|
}
|
|
|
|
const half3 alpha = half3(
|
|
bsdfData.cuticleAngleR,
|
|
bsdfData.cuticleAngleTT,
|
|
bsdfData.cuticleAngleTRT
|
|
);
|
|
|
|
const half3 beta = sqrt(half3(
|
|
bsdfData.roughnessR,
|
|
bsdfData.roughnessTT,
|
|
bsdfData.roughnessTRT
|
|
));
|
|
|
|
// Declare shorthand symbols / variables from the paper
|
|
// ---------------------------------------------------------
|
|
const half n = max(strandCount - 1, 0);
|
|
const half3 af = min(scatteringData.averageScattering[FRONT], 0.99); // Need to clamp in case of NaNs.
|
|
const half3 ab = min(scatteringData.averageScattering[BACK], 0.99); // Need to clamp in case of NaNs.
|
|
|
|
const half3 fw = af / (af.r + af.g + af.b);
|
|
const half3 bw = ab / (ab.r + ab.g + ab.b);
|
|
const half3 sf = dot(alpha, fw);
|
|
const half3 sb = dot(alpha, bw);
|
|
const half3 Bf = dot(beta, fw);
|
|
const half3 Bb = dot(beta, bw);
|
|
const half3 Bf2 = Sq(Bf);
|
|
const half3 Bb2 = Sq(Bb);
|
|
|
|
// Compute global scattering
|
|
// ---------------------------------------------------------
|
|
#if _MATERIAL_FEATURE_HAIR_MARSCHNER_CINEMATIC
|
|
// Predicate term for switching the model between direct / scatter evaluation.
|
|
const half directFraction = GetDirectFraction(bsdfData, n);
|
|
|
|
// Following the observation of Zinke et. al., density factor (ratio of occlusion of the shading point by neighboring strands)
|
|
// can be approximated with this constant to match most path traced references.
|
|
const half df = 0.7;
|
|
|
|
// Approximate the transmittance by assuming that all hair strands between the shading point and the light are
|
|
// oriented the same. This is suitable for long, straighter hair ( Eq. 6 Disney ).
|
|
half3 Tf = df * pow(max(af, 0), n);
|
|
|
|
// Approximate the accumulated variance, by assuming strands all have the same average roughness and inclination. ( Eq. 7 Disney )
|
|
half3 sigmaF = Bf2 * max(1, n);
|
|
#else
|
|
half3 sigmaF = Bf2;
|
|
#endif
|
|
|
|
// Compute local scattering
|
|
// ---------------------------------------------------------
|
|
|
|
// Similarly to front scattering, this same density coefficient is suggested for matching most path traced references.
|
|
const half db = 0.7;
|
|
|
|
// Compute the average backscattering attenuation, the attenuation in the neighborhood of x.
|
|
// Here we only model the first and third backscattering event, as the following are negligible.
|
|
|
|
// Ex. of a single backward scattering event. Where L is the incident light, V is the camera, (F) is a fiber cross-section
|
|
// with a forward scattering event, and (B) is a fiber cross section with a backward scattering event.
|
|
//
|
|
// V <---------------
|
|
// |
|
|
// (F) <--- ... ---> (B)
|
|
// L -------------->|
|
|
|
|
half3 af1 = af;
|
|
half3 af2 = Sq(af1);
|
|
|
|
half3 afI1 = 1 - af2;
|
|
half3 afI2 = Sq(afI1);
|
|
half3 afI3 = afI1 * afI1 * afI1;
|
|
|
|
half3 ab1 = ab;
|
|
half3 ab2 = Sq(ab1);
|
|
half3 ab3 = ab2 * ab1;
|
|
|
|
// Analytic solutions to the potential infinite permutations of backward scattering
|
|
// in a volume of fibers for one and three backward scatters ( Eq. 11, 13, & 14 ).
|
|
half3 A1 = ab1 * af2 / afI1;
|
|
half3 A3 = ab3 * af2 / afI3;
|
|
half3 Ab = A1 + A3;
|
|
|
|
// Computes the average longitudinal shift ( Eq. 16 ).
|
|
half3 shiftB = 1 - ((2 * ab2) / afI2);
|
|
half3 shiftF = ((2 * afI2) + (4 * af2 * ab2)) / afI3;
|
|
half3 deltaB = (sb * shiftB) + (sf * shiftF);
|
|
|
|
// Compute the average back scattering standard deviation ( Eq. 17 ).
|
|
half3 sigmaB = (1 + db * af2);
|
|
sigmaB *= (ab * sqrt((2 * Bf2) + Bb2)) + (ab3 * sqrt((2 * Bf2) + (3 * Bb2)));
|
|
sigmaB *= rcp(ab + (ab3 * ((2 * Bf)) + (3 * Bb)));
|
|
sigmaB = Sq(sigmaB);
|
|
|
|
half3 psiL = 2 * Ab * db * Gaussian(angles.thetaH - deltaB, sigmaB + sigmaF) * INV_PI * rcp(Sq(angles.cosThetaD));
|
|
|
|
#if _MATERIAL_FEATURE_HAIR_MARSCHNER_CINEMATIC
|
|
const half3 Fdirect = directFraction * (Fs + psiL);
|
|
const half3 Fscatter = (Tf - directFraction) * df * (Fs + PI * psiL);
|
|
#else
|
|
const half3 Fdirect = Fs + psiL;
|
|
const half3 Fscatter = 0; // No global scattering contribution.
|
|
#endif
|
|
|
|
// Ref: Section 3.3 & 4 of Zinke et. al.
|
|
return angles.cosThetaI * (saturate(Fdirect) + saturate(Fscatter));
|
|
}
|