140 lines
5.6 KiB
HLSL
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
|