Files
SimUL/Assets/UMA/Content/Hair/Shader/UnityHairShader-master/Version6/CGIncludes/UnityAnisotropicBRDF.cginc
T
2025-01-07 18:54:46 +02:00

140 lines
5.6 KiB
HLSL

//#ifndef UNITY_ANISOTROPIC_BRDF_INCLUDED
//#define UNITY_ANISOTROPIC_BRDF_INCLUDED
// Anisotropic GGX
// From HDRenderPipeline
float D_GGXAnisotropic(float TdotH, float BdotH, float NdotH, float roughnessT, float roughnessB)
{
float f = TdotH * TdotH / (roughnessT * roughnessT) + BdotH * BdotH / (roughnessB * roughnessB) + NdotH * NdotH;
return 1.0 / (roughnessT * roughnessB * f * f);
}
// Smith Joint GGX Anisotropic Visibility
// Taken from https://cedec.cesa.or.jp/2015/session/ENG/14698.html
float SmithJointGGXAnisotropic(float TdotV, float BdotV, float NdotV, float TdotL, float BdotL, float NdotL, float roughnessT, float roughnessB)
{
float aT = roughnessT;
float aT2 = aT * aT;
float aB = roughnessB;
float aB2 = aB * aB;
float lambdaV = NdotL * sqrt(aT2 * TdotV * TdotV + aB2 * BdotV * BdotV + NdotV * NdotV);
float lambdaL = NdotV * sqrt(aT2 * TdotL * TdotL + aB2 * BdotL * BdotL + NdotL * NdotL);
return 0.5 / (lambdaV + lambdaL);
}
// Convert Anistropy to roughness
void ConvertAnisotropyToRoughness(float roughness, float anisotropy, out float roughnessT, out float roughnessB)
{
// (0 <= anisotropy <= 1), therefore (0 <= anisoAspect <= 1)
// The 0.9 factor limits the aspect ratio to 10:1.
float anisoAspect = sqrt(1.0 - 0.9 * anisotropy);
roughnessT = roughness / anisoAspect; // Distort along tangent (rougher)
roughnessB = roughness * anisoAspect; // Straighten along bitangent (smoother)
}
// Schlick Fresnel
float FresnelSchlick(float f0, float f90, float u)
{
float x = 1.0 - u;
float x5 = x * x;
x5 = x5 * x5 * x;
return (f90 - f0) * x5 + f0; // sub mul mul mul sub mad
}
//Clamp roughness
float ClampRoughnessForAnalyticalLights(float roughness)
{
return max(roughness, 0.000001);
}
// Ref: Donald Revie - Implementing Fur Using Deferred Shading (GPU Pro 2)
// The grain direction (e.g. hair or brush direction) is assumed to be orthogonal to the normal.
// The returned normal is NOT normalized.
float3 ComputeGrainNormal(float3 grainDir, float3 V)
{
float3 B = cross(-V, grainDir);
return cross(B, grainDir);
}
//Modify Normal for Anisotropic IBL (Realtime version)
// Fake anisotropic by distorting the normal.
// The grain direction (e.g. hair or brush direction) is assumed to be orthogonal to N.
// Anisotropic ratio (0->no isotropic; 1->full anisotropy in tangent direction)
float3 GetAnisotropicModifiedNormal(float3 grainDir, float3 N, float3 V, float anisotropy)
{
float3 grainNormal = ComputeGrainNormal(grainDir, V);
// TODO: test whether normalizing 'grainNormal' is worth it.
return normalize(lerp(N, grainNormal, anisotropy));
}
float4 AnisotropicBRDF(float3 diffColor, float3 specColor, float oneMinusReflectivity, float smoothness, float3 normal, float3x3 worldVectors,
float anisotropy, float metallic, float3 viewDir, UnityLight light, UnityIndirect gi)
{
//Unpack world vectors
float3 tangent = worldVectors[0];
float3 bitangent = worldVectors[1];
//Normal shift
float shiftAmount = dot(normal, viewDir);
normal = shiftAmount < 0.0f ? normal + viewDir * (-shiftAmount + 1e-5f) : normal;
//Regular vectors
float NdotL = saturate(dot(normal, light.dir));
float NdotV = abs(dot(normal, viewDir));
float LdotV = dot(light.dir, viewDir);
float3 H = Unity_SafeNormalize(light.dir + viewDir);
float invLenLV = rsqrt(abs(2 + 2 * normalize(LdotV)));
float NdotH = saturate(dot(normal, H));
float LdotH = saturate(dot(light.dir, H));
//Tangent vectors
float TdotH = dot(tangent, H);
float TdotL = dot(tangent, light.dir);
float BdotH = dot(bitangent, H);
float BdotL = dot(bitangent, light.dir);
float TdotV = dot(viewDir, tangent);
float BdotV = dot(viewDir, bitangent);
//Fresnels
half grazingTerm = saturate(smoothness + (1 - oneMinusReflectivity));
float3 F = FresnelLerp(specColor, grazingTerm, NdotV); //Original Schlick - Replace from SRP?
//float3 fresnel0 = lerp(specColor, diffColor, metallic);
//float3 F = FresnelSchlick(fresnel0, 1.0, LdotH);
//Calculate roughness
float roughnessT;
float roughnessB;
float perceptualRoughness = SmoothnessToPerceptualRoughness(smoothness);
float roughness = PerceptualRoughnessToRoughness(perceptualRoughness);
ConvertAnisotropyToRoughness(roughness, anisotropy, roughnessT, roughnessB);
//Clamp roughness
roughnessT = ClampRoughnessForAnalyticalLights(roughnessT);
roughnessB = ClampRoughnessForAnalyticalLights(roughnessB);
//Visibility & Distribution terms
float V = SmithJointGGXAnisotropic(TdotV, BdotV, NdotV, TdotL, BdotL, NdotL, roughnessT, roughnessB);
float D = D_GGXAnisotropic(TdotH, BdotH, NdotH, roughnessT, roughnessB);
//Specular term
float3 specularTerm = V * D;
# ifdef UNITY_COLORSPACE_GAMMA
specularTerm = sqrt(max(1e-4h, specularTerm));
# endif
// specularTerm * nl can be NaN on Metal in some cases, use max() to make sure it's a sane value
specularTerm = max(0, specularTerm * NdotL);
#if defined(_SPECULARHIGHLIGHTS_OFF)
specularTerm = 0.0;
#endif
//Diffuse term
float diffuseTerm = DisneyDiffuse(NdotV, NdotL, LdotH, perceptualRoughness) * NdotL;// - Need this NdotL multiply?
//Reduction
half surfaceReduction;
# ifdef UNITY_COLORSPACE_GAMMA
surfaceReduction = 1.0 - 0.28*roughness*perceptualRoughness; // 1-0.28*x^3 as approximation for (1/(x^4+1))^(1/2.2) on the domain [0;1]
# else
surfaceReduction = 1.0 / (roughness*roughness + 1.0); // fade \in [0.5;1]
# endif
//Final
half3 color = (diffColor * (gi.diffuse + light.color * diffuseTerm))
+ specularTerm * light.color * FresnelTerm(specColor, LdotH)
+ surfaceReduction * gi.specular * FresnelLerp(specColor, grazingTerm, NdotV);
return half4(color, 1);
}
//#endif UNITY_ANISOTROPIC_BRDF_INCLUDED