A game about forced loneliness, made by TACStudios
1#ifndef UNITY_BSDF_INCLUDED 2#define UNITY_BSDF_INCLUDED 3 4#if SHADER_API_MOBILE || SHADER_API_GLES3 || SHADER_API_SWITCH || defined(UNITY_UNIFIED_SHADER_PRECISION_MODEL) 5#pragma warning (disable : 3205) // conversion of larger type to smaller 6#endif 7 8#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl" 9 10// Note: All NDF and diffuse term have a version with and without divide by PI. 11// Version with divide by PI are use for direct lighting. 12// Version without divide by PI are use for image based lighting where often the PI cancel during importance sampling 13 14//----------------------------------------------------------------------------- 15// Help for BSDF evaluation 16//----------------------------------------------------------------------------- 17 18// Cosine-weighted BSDF (a BSDF taking the projected solid angle into account). 19// If some of the values are monochromatic, the compiler will optimize accordingly. 20struct CBSDF 21{ 22 float3 diffR; // Diffuse reflection (T -> MS -> T, same sides) 23 float3 specR; // Specular reflection (R, RR, TRT, etc) 24 float3 diffT; // Diffuse transmission (rough T or TT, opposite sides) 25 float3 specT; // Specular transmission (T, TT, TRRT, etc) 26}; 27 28//----------------------------------------------------------------------------- 29// Fresnel term 30//----------------------------------------------------------------------------- 31 32real F_Schlick(real f0, real f90, real u) 33{ 34 real x = 1.0 - u; 35 real x2 = x * x; 36 real x5 = x * x2 * x2; 37 return (f90 - f0) * x5 + f0; // sub mul mul mul sub mad 38} 39 40real F_Schlick(real f0, real u) 41{ 42 return F_Schlick(f0, 1.0, u); // sub mul mul mul sub mad 43} 44 45real3 F_Schlick(real3 f0, real f90, real u) 46{ 47 real x = 1.0 - u; 48 real x2 = x * x; 49 real x5 = x * x2 * x2; 50 return f0 * (1.0 - x5) + (f90 * x5); // sub mul mul mul sub mul mad*3 51} 52 53real3 F_Schlick(real3 f0, real u) 54{ 55 return F_Schlick(f0, 1.0, u); // sub mul mul mul sub mad*3 56} 57 58// Does not handle TIR. 59real F_Transm_Schlick(real f0, real f90, real u) 60{ 61 real x = 1.0 - u; 62 real x2 = x * x; 63 real x5 = x * x2 * x2; 64 return (1.0 - f90 * x5) - f0 * (1.0 - x5); // sub mul mul mul mad sub mad 65} 66 67// Does not handle TIR. 68real F_Transm_Schlick(real f0, real u) 69{ 70 return F_Transm_Schlick(f0, 1.0, u); // sub mul mul mad mad 71} 72 73// Does not handle TIR. 74real3 F_Transm_Schlick(real3 f0, real f90, real u) 75{ 76 real x = 1.0 - u; 77 real x2 = x * x; 78 real x5 = x * x2 * x2; 79 return (1.0 - f90 * x5) - f0 * (1.0 - x5); // sub mul mul mul mad sub mad*3 80} 81 82// Does not handle TIR. 83real3 F_Transm_Schlick(real3 f0, real u) 84{ 85 return F_Transm_Schlick(f0, 1.0, u); // sub mul mul mad mad*3 86} 87 88// Compute the cos of critical angle: cos(asin(eta)) == sqrt(1.0 - eta*eta) 89// eta == IORMedium/IORSource 90// If eta >= 1 the it's an AirMedium interation, otherwise it's MediumAir interation 91real CosCriticalAngle(real eta) 92{ 93 return sqrt(max(1.0 - Sq(eta), 0.0)); 94 // For 1 <= IOR <= 4: Max error: 0.0268594 95 //return eta >= 1.0 ? 0.0 : (((3.0 + eta) * sqrt(max(0.0, 1.0 - eta))) / (2.0 * sqrt(2.0))); 96 // For 1 <= IOR <= 4: Max error: 0.00533065 97 //return eta >= 1.0 ? 0.0 : (-((-23.0 - 10.0 * eta + Sq(eta)) * sqrt(max(0.0, 1.0 - eta))) / (16.0 * sqrt(2.0))); 98 // For 1 <= IOR <= 4: Max error: 0.00129402 99 //return eta >= 1.0 ? 0.0 : (((91.0 + 43.0 * eta - 7.0 * Sq(eta) + pow(eta, 3)) * sqrt(max(0.0, 1.0 - eta))) / (64. * sqrt(2.0))); 100} 101 102// Ref: https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/ 103// Fresnel dielectric / dielectric 104real F_FresnelDielectric(real ior, real u) 105{ 106 real g = sqrt(Sq(ior) + Sq(u) - 1.0); 107 108 // The "1.0 - saturate(1.0 - result)" formulation allows to recover form cases where g is undefined, for IORs < 1 109 return 1.0 - saturate(1.0 - 0.5 * Sq((g - u) / (g + u)) * (1.0 + Sq(((g + u) * u - 1.0) / ((g - u) * u + 1.0)))); 110} 111 112// Fresnel dieletric / conductor 113// Note: etak2 = etak * etak (optimization for Artist Friendly Metallic Fresnel below) 114// eta = eta_t / eta_i and etak = k_t / n_i 115real3 F_FresnelConductor(real3 eta, real3 etak2, real cosTheta) 116{ 117 real cosTheta2 = cosTheta * cosTheta; 118 real sinTheta2 = 1.0 - cosTheta2; 119 real3 eta2 = eta * eta; 120 121 real3 t0 = eta2 - etak2 - sinTheta2; 122 real3 a2plusb2 = sqrt(t0 * t0 + 4.0 * eta2 * etak2); 123 real3 t1 = a2plusb2 + cosTheta2; 124 real3 a = sqrt(0.5 * (a2plusb2 + t0)); 125 real3 t2 = 2.0 * a * cosTheta; 126 real3 Rs = (t1 - t2) / (t1 + t2); 127 128 real3 t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2; 129 real3 t4 = t2 * sinTheta2; 130 real3 Rp = Rs * (t3 - t4) / (t3 + t4); 131 132 return 0.5 * (Rp + Rs); 133} 134 135// Conversion FO/IOR 136 137TEMPLATE_2_FLT_HALF(IorToFresnel0, transmittedIor, incidentIor, return Sq((transmittedIor - incidentIor) / (transmittedIor + incidentIor)) ) 138// ior is a value between 1.0 and 3.0. 1.0 is air interface 139real IorToFresnel0(real transmittedIor) 140{ 141 return IorToFresnel0(transmittedIor, 1.0); 142} 143 144// Assume air interface for top 145// Note: We don't handle the case fresnel0 == 1 146//real Fresnel0ToIor(real fresnel0) 147//{ 148// real sqrtF0 = sqrt(fresnel0); 149// return (1.0 + sqrtF0) / (1.0 - sqrtF0); 150//} 151TEMPLATE_1_FLT_HALF(Fresnel0ToIor, fresnel0, return ((1.0 + sqrt(fresnel0)) / (1.0 - sqrt(fresnel0))) ) 152 153// This function is a coarse approximation of computing fresnel0 for a different top than air (here clear coat of IOR 1.5) when we only have fresnel0 with air interface 154// This function is equivalent to IorToFresnel0(Fresnel0ToIor(fresnel0), 1.5) 155// mean 156// real sqrtF0 = sqrt(fresnel0); 157// return Sq(1.0 - 5.0 * sqrtF0) / Sq(5.0 - sqrtF0); 158// Optimization: Fit of the function (3 mad) for range [0.04 (should return 0), 1 (should return 1)] 159TEMPLATE_1_FLT_HALF(ConvertF0ForAirInterfaceToF0ForClearCoat15, fresnel0, return saturate(-0.0256868 + fresnel0 * (0.326846 + (0.978946 - 0.283835 * fresnel0) * fresnel0))) 160 161// Even coarser approximation of ConvertF0ForAirInterfaceToF0ForClearCoat15 (above) for mobile (2 mad) 162TEMPLATE_1_FLT_HALF(ConvertF0ForAirInterfaceToF0ForClearCoat15Fast, fresnel0, return saturate(fresnel0 * (fresnel0 * 0.526868 + 0.529324) - 0.0482256)) 163 164// Artist Friendly Metallic Fresnel Ref: http://jcgt.org/published/0003/04/03/paper.pdf 165 166real3 GetIorN(real3 f0, real3 edgeTint) 167{ 168 real3 sqrtF0 = sqrt(f0); 169 return lerp((1.0 - f0) / (1.0 + f0), (1.0 + sqrtF0) / (1.0 - sqrt(f0)), edgeTint); 170} 171 172real3 getIorK2(real3 f0, real3 n) 173{ 174 real3 nf0 = Sq(n + 1.0) * f0 - Sq(f0 - 1.0); 175 return nf0 / (1.0 - f0); 176} 177 178// same as regular refract except there is not the test for total internal reflection + the vector is flipped for processing 179real3 CoatRefract(real3 X, real3 N, real ieta) 180{ 181 real XdotN = saturate(dot(N, X)); 182 return ieta * X + (sqrt(1 + ieta * ieta * (XdotN * XdotN - 1)) - ieta * XdotN) * N; 183} 184 185//----------------------------------------------------------------------------- 186// Specular BRDF 187//----------------------------------------------------------------------------- 188 189float Lambda_GGX(float roughness, float3 V) 190{ 191 return 0.5 * (sqrt(1.0 + (Sq(roughness * V.x) + Sq(roughness * V.y)) / Sq(V.z)) - 1.0); 192} 193 194real D_GGXNoPI(real NdotH, real roughness) 195{ 196 real a2 = Sq(roughness); 197 real s = (NdotH * a2 - NdotH) * NdotH + 1.0; 198 199 // If roughness is 0, returns (NdotH == 1 ? 1 : 0). 200 // That is, it returns 1 for perfect mirror reflection, and 0 otherwise. 201 return SafeDiv(a2, s * s); 202} 203 204real D_GGX(real NdotH, real roughness) 205{ 206 return INV_PI * D_GGXNoPI(NdotH, roughness); 207} 208 209// Ref: Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs, p. 19, 29. 210// p. 84 (37/60) 211real G_MaskingSmithGGX(real NdotV, real roughness) 212{ 213 // G1(V, H) = HeavisideStep(VdotH) / (1 + Lambda(V)). 214 // Lambda(V) = -0.5 + 0.5 * sqrt(1 + 1 / a^2). 215 // a = 1 / (roughness * tan(theta)). 216 // 1 + Lambda(V) = 0.5 + 0.5 * sqrt(1 + roughness^2 * tan^2(theta)). 217 // tan^2(theta) = (1 - cos^2(theta)) / cos^2(theta) = 1 / cos^2(theta) - 1. 218 // Assume that (VdotH > 0), e.i. (acos(LdotV) < Pi). 219 220 return 1.0 / (0.5 + 0.5 * sqrt(1.0 + Sq(roughness) * (1.0 / Sq(NdotV) - 1.0))); 221} 222 223// Precompute part of lambdaV 224real GetSmithJointGGXPartLambdaV(real NdotV, real roughness) 225{ 226 real a2 = Sq(roughness); 227 return sqrt((-NdotV * a2 + NdotV) * NdotV + a2); 228} 229 230// Note: V = G / (4 * NdotL * NdotV) 231// Ref: http://jcgt.org/published/0003/02/03/paper.pdf 232real V_SmithJointGGX(real NdotL, real NdotV, real roughness, real partLambdaV) 233{ 234 real a2 = Sq(roughness); 235 236 // Original formulation: 237 // lambda_v = (-1 + sqrt(a2 * (1 - NdotL2) / NdotL2 + 1)) * 0.5 238 // lambda_l = (-1 + sqrt(a2 * (1 - NdotV2) / NdotV2 + 1)) * 0.5 239 // G = 1 / (1 + lambda_v + lambda_l); 240 241 // Reorder code to be more optimal: 242 real lambdaV = NdotL * partLambdaV; 243 real lambdaL = NdotV * sqrt((-NdotL * a2 + NdotL) * NdotL + a2); 244 245 // Simplify visibility term: (2.0 * NdotL * NdotV) / ((4.0 * NdotL * NdotV) * (lambda_v + lambda_l)) 246 return 0.5 / max(lambdaV + lambdaL, REAL_MIN); 247} 248 249real V_SmithJointGGX(real NdotL, real NdotV, real roughness) 250{ 251 real partLambdaV = GetSmithJointGGXPartLambdaV(NdotV, roughness); 252 return V_SmithJointGGX(NdotL, NdotV, roughness, partLambdaV); 253} 254 255// Inline D_GGX() * V_SmithJointGGX() together for better code generation. 256real DV_SmithJointGGX(real NdotH, real NdotL, real NdotV, real roughness, real partLambdaV) 257{ 258 real a2 = Sq(roughness); 259 real s = (NdotH * a2 - NdotH) * NdotH + 1.0; 260 261 real lambdaV = NdotL * partLambdaV; 262 real lambdaL = NdotV * sqrt((-NdotL * a2 + NdotL) * NdotL + a2); 263 264 real2 D = real2(a2, s * s); // Fraction without the multiplier (1/Pi) 265 real2 G = real2(1, lambdaV + lambdaL); // Fraction without the multiplier (1/2) 266 267 // This function is only used for direct lighting. 268 // If roughness is 0, the probability of hitting a punctual or directional light is also 0. 269 // Therefore, we return 0. The most efficient way to do it is with a max(). 270 return INV_PI * 0.5 * (D.x * G.x) / max(D.y * G.y, REAL_MIN); 271} 272 273real DV_SmithJointGGX(real NdotH, real NdotL, real NdotV, real roughness) 274{ 275 real partLambdaV = GetSmithJointGGXPartLambdaV(NdotV, roughness); 276 return DV_SmithJointGGX(NdotH, NdotL, NdotV, roughness, partLambdaV); 277} 278 279// Precompute a part of LambdaV. 280// Note on this linear approximation. 281// Exact for roughness values of 0 and 1. Also, exact when the cosine is 0 or 1. 282// Otherwise, the worst case relative error is around 10%. 283// https://www.desmos.com/calculator/wtp8lnjutx 284real GetSmithJointGGXPartLambdaVApprox(real NdotV, real roughness) 285{ 286 real a = roughness; 287 return NdotV * (1 - a) + a; 288} 289 290real V_SmithJointGGXApprox(real NdotL, real NdotV, real roughness, real partLambdaV) 291{ 292 real a = roughness; 293 294 real lambdaV = NdotL * partLambdaV; 295 real lambdaL = NdotV * (NdotL * (1 - a) + a); 296 297 return 0.5 / (lambdaV + lambdaL); 298} 299 300real V_SmithJointGGXApprox(real NdotL, real NdotV, real roughness) 301{ 302 real partLambdaV = GetSmithJointGGXPartLambdaVApprox(NdotV, roughness); 303 return V_SmithJointGGXApprox(NdotL, NdotV, roughness, partLambdaV); 304} 305 306// roughnessT -> roughness in tangent direction 307// roughnessB -> roughness in bitangent direction 308real D_GGXAnisoNoPI(real TdotH, real BdotH, real NdotH, real roughnessT, real roughnessB) 309{ 310 real a2 = roughnessT * roughnessB; 311 real3 v = real3(roughnessB * TdotH, roughnessT * BdotH, a2 * NdotH); 312 real s = dot(v, v); 313 314 // If roughness is 0, returns (NdotH == 1 ? 1 : 0). 315 // That is, it returns 1 for perfect mirror reflection, and 0 otherwise. 316 return SafeDiv(a2 * a2 * a2, s * s); 317} 318 319real D_GGXAniso(real TdotH, real BdotH, real NdotH, real roughnessT, real roughnessB) 320{ 321 return INV_PI * D_GGXAnisoNoPI(TdotH, BdotH, NdotH, roughnessT, roughnessB); 322} 323 324real GetSmithJointGGXAnisoPartLambdaV(real TdotV, real BdotV, real NdotV, real roughnessT, real roughnessB) 325{ 326 return length(real3(roughnessT * TdotV, roughnessB * BdotV, NdotV)); 327} 328 329// Note: V = G / (4 * NdotL * NdotV) 330// Ref: https://cedec.cesa.or.jp/2015/session/ENG/14698.html The Rendering Materials of Far Cry 4 331real V_SmithJointGGXAniso(real TdotV, real BdotV, real NdotV, real TdotL, real BdotL, real NdotL, real roughnessT, real roughnessB, real partLambdaV) 332{ 333 real lambdaV = NdotL * partLambdaV; 334 real lambdaL = NdotV * length(real3(roughnessT * TdotL, roughnessB * BdotL, NdotL)); 335 336 return 0.5 / (lambdaV + lambdaL); 337} 338 339real V_SmithJointGGXAniso(real TdotV, real BdotV, real NdotV, real TdotL, real BdotL, real NdotL, real roughnessT, real roughnessB) 340{ 341 real partLambdaV = GetSmithJointGGXAnisoPartLambdaV(TdotV, BdotV, NdotV, roughnessT, roughnessB); 342 return V_SmithJointGGXAniso(TdotV, BdotV, NdotV, TdotL, BdotL, NdotL, roughnessT, roughnessB, partLambdaV); 343} 344 345// Inline D_GGXAniso() * V_SmithJointGGXAniso() together for better code generation. 346real DV_SmithJointGGXAniso(real TdotH, real BdotH, real NdotH, real NdotV, 347 real TdotL, real BdotL, real NdotL, 348 real roughnessT, real roughnessB, real partLambdaV) 349{ 350 real a2 = roughnessT * roughnessB; 351 real3 v = real3(roughnessB * TdotH, roughnessT * BdotH, a2 * NdotH); 352 real s = dot(v, v); 353 354 real lambdaV = NdotL * partLambdaV; 355 real lambdaL = NdotV * length(real3(roughnessT * TdotL, roughnessB * BdotL, NdotL)); 356 357 real2 D = real2(a2 * a2 * a2, s * s); // Fraction without the multiplier (1/Pi) 358 real2 G = real2(1, lambdaV + lambdaL); // Fraction without the multiplier (1/2) 359 360 // This function is only used for direct lighting. 361 // If roughness is 0, the probability of hitting a punctual or directional light is also 0. 362 // Therefore, we return 0. The most efficient way to do it is with a max(). 363 return (INV_PI * 0.5) * (D.x * G.x) / max(D.y * G.y, REAL_MIN); 364} 365 366real DV_SmithJointGGXAniso(real TdotH, real BdotH, real NdotH, 367 real TdotV, real BdotV, real NdotV, 368 real TdotL, real BdotL, real NdotL, 369 real roughnessT, real roughnessB) 370{ 371 real partLambdaV = GetSmithJointGGXAnisoPartLambdaV(TdotV, BdotV, NdotV, roughnessT, roughnessB); 372 return DV_SmithJointGGXAniso(TdotH, BdotH, NdotH, NdotV, 373 TdotL, BdotL, NdotL, 374 roughnessT, roughnessB, partLambdaV); 375} 376 377// Get projected roughness for a certain normalized direction V in tangent space 378// and an anisotropic roughness 379// Ref: Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs, Heitz 2014, pp. 86, 88 - 39/60, 41/60 380float GetProjectedRoughness(float TdotV, float BdotV, float NdotV, float roughnessT, float roughnessB) 381{ 382 float2 roughness = float2(roughnessT, roughnessB); 383 float sinTheta2 = max((1 - Sq(NdotV)), FLT_MIN); 384 // if sinTheta^2 = 0, NdotV = 1, TdotV = BdotV = 0 and roughness is arbitrary, no real azimuth 385 // as there's a breakdown of the spherical parameterization, so we clamp under by FLT_MIN in any case 386 // for safe division 387 // Note: 388 // sin(thetaV)^2 * cos(phiV)^2 = (TdotV)^2 389 // sin(thetaV)^2 * sin(phiV)^2 = (BdotV)^2 390 float2 vProj2 = Sq(float2(TdotV, BdotV)) * rcp(sinTheta2); 391 // vProj2 = (cos^2(phi), sin^2(phi)) 392 float projRoughness = sqrt(dot(vProj2, roughness*roughness)); 393 return projRoughness; 394} 395 396//----------------------------------------------------------------------------- 397// Diffuse BRDF - diffuseColor is expected to be multiply by the caller 398//----------------------------------------------------------------------------- 399 400real LambertNoPI() 401{ 402 return 1.0; 403} 404 405real Lambert() 406{ 407 return INV_PI; 408} 409 410real DisneyDiffuseNoPI(real NdotV, real NdotL, real LdotV, real perceptualRoughness) 411{ 412 // (2 * LdotH * LdotH) = 1 + LdotV 413 // real fd90 = 0.5 + (2 * LdotH * LdotH) * perceptualRoughness; 414 real fd90 = 0.5 + (perceptualRoughness + perceptualRoughness * LdotV); 415 // Two schlick fresnel term 416 real lightScatter = F_Schlick(1.0, fd90, NdotL); 417 real viewScatter = F_Schlick(1.0, fd90, NdotV); 418 419 // Normalize the BRDF for polar view angles of up to (Pi/4). 420 // We use the worst case of (roughness = albedo = 1), and, for each view angle, 421 // integrate (brdf * cos(theta_light)) over all light directions. 422 // The resulting value is for (theta_view = 0), which is actually a little bit larger 423 // than the value of the integral for (theta_view = Pi/4). 424 // Hopefully, the compiler folds the constant together with (1/Pi). 425 return rcp(1.03571) * (lightScatter * viewScatter); 426} 427 428#ifndef BUILTIN_TARGET_API 429real DisneyDiffuse(real NdotV, real NdotL, real LdotV, real perceptualRoughness) 430{ 431 return INV_PI * DisneyDiffuseNoPI(NdotV, NdotL, LdotV, perceptualRoughness); 432} 433#endif 434 435// Ref: Diffuse Lighting for GGX + Smith Microsurfaces, p. 113. 436real3 DiffuseGGXNoPI(real3 albedo, real NdotV, real NdotL, real NdotH, real LdotV, real roughness) 437{ 438 real facing = 0.5 + 0.5 * LdotV; // (LdotH)^2 439 real rough = facing * (0.9 - 0.4 * facing) * (0.5 / NdotH + 1); 440 real transmitL = F_Transm_Schlick(0, NdotL); 441 real transmitV = F_Transm_Schlick(0, NdotV); 442 real smooth = transmitL * transmitV * 1.05; // Normalize F_t over the hemisphere 443 real single = lerp(smooth, rough, roughness); // Rescaled by PI 444 real multiple = roughness * (0.1159 * PI); // Rescaled by PI 445 446 return single + albedo * multiple; 447} 448 449real3 DiffuseGGX(real3 albedo, real NdotV, real NdotL, real NdotH, real LdotV, real roughness) 450{ 451 // Note that we could save 2 cycles by inlining the multiplication by INV_PI. 452 return INV_PI * DiffuseGGXNoPI(albedo, NdotV, NdotL, NdotH, LdotV, roughness); 453} 454 455//----------------------------------------------------------------------------- 456// Iridescence 457//----------------------------------------------------------------------------- 458 459// Ref: https://belcour.github.io/blog/research/2017/05/01/brdf-thin-film.html 460// Evaluation XYZ sensitivity curves in Fourier space 461real3 EvalSensitivity(real opd, real shift) 462{ 463 // Use Gaussian fits, given by 3 parameters: val, pos and var 464 real phase = 2.0 * PI * opd * 1e-6; 465 real3 val = real3(5.4856e-13, 4.4201e-13, 5.2481e-13); 466 real3 pos = real3(1.6810e+06, 1.7953e+06, 2.2084e+06); 467 real3 var = real3(4.3278e+09, 9.3046e+09, 6.6121e+09); 468 real3 xyz = val * sqrt(2.0 * PI * var) * cos(pos * phase + shift) * exp(-var * phase * phase); 469 xyz.x += 9.7470e-14 * sqrt(2.0 * PI * 4.5282e+09) * cos(2.2399e+06 * phase + shift) * exp(-4.5282e+09 * phase * phase); 470 xyz /= 1.0685e-7; 471 472 // Convert to linear sRGb color space here. 473 // EvalIridescence works in linear sRGB color space and does not switch... 474 real3 srgb = mul(XYZ_2_REC709_MAT, xyz); 475 return srgb; 476} 477 478// Evaluate the reflectance for a thin-film layer on top of a dielectric medum. 479real3 EvalIridescence(real eta_1, real cosTheta1, real iridescenceThickness, real3 baseLayerFresnel0, real iorOverBaseLayer = 0.0) 480{ 481 real3 I; 482 483 // iridescenceThickness unit is micrometer for this equation here. Mean 0.5 is 500nm. 484 real Dinc = 3.0 * iridescenceThickness; 485 486 // Note: Unlike the code provide with the paper, here we use schlick approximation 487 // Schlick is a very poor approximation when dealing with iridescence to the Fresnel 488 // term and there is no "neutral" value in this unlike in the original paper. 489 // We use Iridescence mask here to allow to have neutral value 490 491 // Hack: In order to use only one parameter (DInc), we deduced the ior of iridescence from current Dinc iridescenceThickness 492 // and we use mask instead to fade out the effect 493 real eta_2 = lerp(2.0, 1.0, iridescenceThickness); 494 // Following line from original code is not needed for us, it create a discontinuity 495 // Force eta_2 -> eta_1 when Dinc -> 0.0 496 // real eta_2 = lerp(eta_1, eta_2, smoothstep(0.0, 0.03, Dinc)); 497 // Evaluate the cosTheta on the base layer (Snell law) 498 real sinTheta2Sq = Sq(eta_1 / eta_2) * (1.0 - Sq(cosTheta1)); 499 500 // Handle TIR: 501 // (Also note that with just testing sinTheta2Sq > 1.0, (1.0 - sinTheta2Sq) can be negative, as emitted instructions 502 // can eg be a mad giving a small negative for (1.0 - sinTheta2Sq), while sinTheta2Sq still testing equal to 1.0), so we actually 503 // test the operand [cosTheta2Sq := (1.0 - sinTheta2Sq)] < 0 directly:) 504 real cosTheta2Sq = (1.0 - sinTheta2Sq); 505 // Or use this "artistic hack" to get more continuity even though wrong (no TIR, continue the effect by mirroring it): 506 // if( cosTheta2Sq < 0.0 ) => { sinTheta2Sq = 2 - sinTheta2Sq; => so cosTheta2Sq = sinTheta2Sq - 1 } 507 // ie don't test and simply do 508 // real cosTheta2Sq = abs(1.0 - sinTheta2Sq); 509 if (cosTheta2Sq < 0.0) 510 I = real3(1.0, 1.0, 1.0); 511 else 512 { 513 514 real cosTheta2 = sqrt(cosTheta2Sq); 515 516 // First interface 517 real R0 = IorToFresnel0(eta_2, eta_1); 518 real R12 = F_Schlick(R0, cosTheta1); 519 real R21 = R12; 520 real T121 = 1.0 - R12; 521 real phi12 = 0.0; 522 real phi21 = PI - phi12; 523 524 // Second interface 525 // The f0 or the base should account for the new computed eta_2 on top. 526 // This is optionally done if we are given the needed current ior over the base layer that is accounted for 527 // in the baseLayerFresnel0 parameter: 528 if (iorOverBaseLayer > 0.0) 529 { 530 // Fresnel0ToIor will give us a ratio of baseIor/topIor, hence we * iorOverBaseLayer to get the baseIor 531 real3 baseIor = iorOverBaseLayer * Fresnel0ToIor(baseLayerFresnel0 + 0.0001); // guard against 1.0 532 baseLayerFresnel0 = IorToFresnel0(baseIor, eta_2); 533 } 534 535 real3 R23 = F_Schlick(baseLayerFresnel0, cosTheta2); 536 real phi23 = 0.0; 537 538 // Phase shift 539 real OPD = Dinc * cosTheta2; 540 real phi = phi21 + phi23; 541 542 // Compound terms 543 real3 R123 = clamp(R12 * R23, 1e-5, 0.9999); 544 real3 r123 = sqrt(R123); 545 real3 Rs = Sq(T121) * R23 / (real3(1.0, 1.0, 1.0) - R123); 546 547 // Reflectance term for m = 0 (DC term amplitude) 548 real3 C0 = R12 + Rs; 549 I = C0; 550 551 // Reflectance term for m > 0 (pairs of diracs) 552 real3 Cm = Rs - T121; 553 for (int m = 1; m <= 2; ++m) 554 { 555 Cm *= r123; 556 real3 Sm = 2.0 * EvalSensitivity(m * OPD, m * phi); 557 //vec3 SmP = 2.0 * evalSensitivity(m*OPD, m*phi2.y); 558 I += Cm * Sm; 559 } 560 561 // Since out of gamut colors might be produced, negative color values are clamped to 0. 562 I = max(I, float3(0.0, 0.0, 0.0)); 563 } 564 565 return I; 566} 567 568//----------------------------------------------------------------------------- 569// Fabric 570//----------------------------------------------------------------------------- 571 572// Ref: https://knarkowicz.wordpress.com/2018/01/04/cloth-shading/ 573real D_CharlieNoPI(real NdotH, real roughness) 574{ 575 float invR = rcp(roughness); 576 float cos2h = NdotH * NdotH; 577 float sin2h = 1.0 - cos2h; 578 // Note: We have sin^2 so multiply by 0.5 to cancel it 579 return (2.0 + invR) * PositivePow(sin2h, invR * 0.5) / 2.0; 580} 581 582real D_Charlie(real NdotH, real roughness) 583{ 584 return INV_PI * D_CharlieNoPI(NdotH, roughness); 585} 586 587real CharlieL(real x, real r) 588{ 589 r = saturate(r); 590 r = 1.0 - (1.0 - r) * (1.0 - r); 591 592 float a = lerp(25.3245, 21.5473, r); 593 float b = lerp(3.32435, 3.82987, r); 594 float c = lerp(0.16801, 0.19823, r); 595 float d = lerp(-1.27393, -1.97760, r); 596 float e = lerp(-4.85967, -4.32054, r); 597 598 return a / (1. + b * PositivePow(x, c)) + d * x + e; 599} 600 601// Note: This version don't include the softening of the paper: Production Friendly Microfacet Sheen BRDF 602real V_Charlie(real NdotL, real NdotV, real roughness) 603{ 604 real lambdaV = NdotV < 0.5 ? exp(CharlieL(NdotV, roughness)) : exp(2.0 * CharlieL(0.5, roughness) - CharlieL(1.0 - NdotV, roughness)); 605 real lambdaL = NdotL < 0.5 ? exp(CharlieL(NdotL, roughness)) : exp(2.0 * CharlieL(0.5, roughness) - CharlieL(1.0 - NdotL, roughness)); 606 607 return 1.0 / ((1.0 + lambdaV + lambdaL) * (4.0 * NdotV * NdotL)); 608} 609 610// We use V_Ashikhmin instead of V_Charlie in practice for game due to the cost of V_Charlie 611real V_Ashikhmin(real NdotL, real NdotV) 612{ 613 // Use soft visibility term introduce in: Crafting a Next-Gen Material Pipeline for The Order : 1886 614 return 1.0 / (4.0 * (NdotL + NdotV - NdotL * NdotV)); 615} 616 617// A diffuse term use with fabric done by tech artist - empirical 618real FabricLambertNoPI(real roughness) 619{ 620 return lerp(1.0, 0.5, roughness); 621} 622 623real FabricLambert(real roughness) 624{ 625 return INV_PI * FabricLambertNoPI(roughness); 626} 627 628real G_CookTorrance(real NdotH, real NdotV, real NdotL, real HdotV) 629{ 630 return min(1.0, 2.0 * NdotH * min(NdotV, NdotL) / HdotV); 631} 632 633//----------------------------------------------------------------------------- 634// Hair 635//----------------------------------------------------------------------------- 636 637//http://web.engr.oregonstate.edu/~mjb/cs519/Projects/Papers/HairRendering.pdf 638real3 ShiftTangent(real3 T, real3 N, real shift) 639{ 640 return normalize(T + N * shift); 641} 642 643// Note: this is Blinn-Phong, the original paper uses Phong. 644real3 D_KajiyaKay(real3 T, real3 H, real specularExponent) 645{ 646 real TdotH = dot(T, H); 647 real sinTHSq = saturate(1.0 - TdotH * TdotH); 648 649 real dirAttn = saturate(TdotH + 1.0); // Evgenii: this seems like a hack? Do we really need this? 650 651 // Note: Kajiya-Kay is not energy conserving. 652 // We attempt at least some energy conservation by approximately normalizing Blinn-Phong NDF. 653 // We use the formulation with the NdotL. 654 // See http://www.thetenthplanet.de/archives/255. 655 real n = specularExponent; 656 real norm = (n + 2) * rcp(2 * PI); 657 658 return dirAttn * norm * PositivePow(sinTHSq, 0.5 * n); 659} 660 661#if SHADER_API_MOBILE || SHADER_API_GLES3 || SHADER_API_SWITCH 662#pragma warning (enable : 3205) // conversion of larger type to smaller 663#endif 664 665#endif // UNITY_BSDF_INCLUDED