Skip to content

Commit

Permalink
feat: support Iridescence
Browse files Browse the repository at this point in the history
  • Loading branch information
zhuxudong committed Jan 20, 2025
1 parent 195605d commit afd64de
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 14 deletions.
112 changes: 102 additions & 10 deletions packages/core/src/shaderlib/pbr/brdf.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -102,16 +102,14 @@ float D_GGX(float alpha, float dotNH ) {
}
#endif

vec3 isotropicLobe(vec3 specularColor, float alpha, float dotNV, float dotNL, float dotNH, float dotLH) {
vec3 F = F_Schlick( specularColor, dotLH );
float DG_GGX(float alpha, float dotNV, float dotNL, float dotNH) {
float D = D_GGX( alpha, dotNH );
float G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );

return F * ( G * D );
return G * D;
}

#ifdef MATERIAL_ENABLE_ANISOTROPY
vec3 anisotropicLobe(vec3 h, vec3 l, Geometry geometry, vec3 specularColor, float alpha, float dotNV, float dotNL, float dotNH, float dotLH) {
float DG_GGX_anisotropic(vec3 h, vec3 l, Geometry geometry, float alpha, float dotNV, float dotNL, float dotNH) {
vec3 t = geometry.anisotropicT;
vec3 b = geometry.anisotropicB;
vec3 v = geometry.viewDir;
Expand All @@ -129,16 +127,104 @@ vec3 isotropicLobe(vec3 specularColor, float alpha, float dotNV, float dotNL, fl
float ab = max(alpha * (1.0 - geometry.anisotropy), MIN_ROUGHNESS);

// specular anisotropic BRDF
vec3 F = F_Schlick( specularColor, dotLH );
float D = D_GGX_Anisotropic(at, ab, dotTH, dotBH, dotNH);
float G = G_GGX_SmithCorrelated_Anisotropic(at, ab, dotTV, dotBV, dotTL, dotBL, dotNV, dotNL);

return F * ( G * D );
return G * D;
}
#endif

#ifdef MATERIAL_ENABLE_IRIDESCENCE
vec3 iorToFresnel0(vec3 transmittedIOR, float incidentIOR) {
return pow((transmittedIOR - incidentIOR) / (transmittedIOR + incidentIOR),vec3(2.0));
}

float iorToFresnel0(float transmittedIOR, float incidentIOR) {
return pow((transmittedIOR - incidentIOR) / (transmittedIOR + incidentIOR),2.0);
}

// Assume air interface for top
// Note: We don't handle the case fresnel0 == 1
vec3 fresnelToIOR(vec3 f0){
vec3 sqrtF0 = sqrt(f0);
return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
}

// Fresnel equations for dielectric/dielectric interfaces.
// Ref: https://belcour.github.io/blog/research/publication/2017/05/01/brdf-thin-film.html
// Evaluation XYZ sensitivity curves in Fourier space
vec3 evalSensitivity(float opd, vec3 shift){
// Use Gaussian fits, given by 3 parameters: val, pos and var
float phase = 2.0 * PI * opd * 1.0e-9;
const vec3 val = vec3(5.4856e-13, 4.4201e-13, 5.2481e-13);
const vec3 pos = vec3(1.6810e+06, 1.7953e+06, 2.2084e+06);
const vec3 var = vec3(4.3278e+09, 9.3046e+09, 6.6121e+09);
vec3 xyz = val * sqrt(2.0 * PI * var) * cos(pos * phase + shift) * exp(-var * pow2(phase));
xyz.x += 9.7470e-14 * sqrt(2.0 * PI * 4.5282e+09) * cos(2.2399e+06 * phase + shift[0]) * exp(-4.5282e+09 * pow2(phase));
xyz /= 1.0685e-7;
// XYZ to RGB color space
const mat3 XYZ_TO_RGB = mat3( 3.2404542, -0.9692660, 0.0556434,
-1.5371385, 1.8760108, -0.2040259,
-0.4985314, 0.0415560, 1.0572252);
vec3 rgb = XYZ_TO_RGB * xyz;
return rgb;
}

vec3 evalIridescenceSpecular(float outsideIOR, float dotNV, float thinIOR, vec3 baseF0,float iridescenceThickness){
vec3 iridescence = vec3(1.0);
// Force iridescenceIOR -> outsideIOR when thinFilmThickness -> 0.0
float iridescenceIOR = mix( outsideIOR, thinIOR, smoothstep( 0.0, 0.03, iridescenceThickness ) );
// Evaluate the cosTheta on the base layer (Snell law)
float sinTheta2Sq = pow( outsideIOR / iridescenceIOR, 2.0) * (1.0 - pow( dotNV, 2.0));
float cosTheta2Sq = 1.0 - sinTheta2Sq;
// Handle total internal reflection
if (cosTheta2Sq < 0.0) {
return iridescence;
}
float cosTheta2 = sqrt(cosTheta2Sq);

// First interface
float f0 = iorToFresnel0(iridescenceIOR, outsideIOR);
float reflectance = F_Schlick(f0, dotNV);
float t121 = 1.0 - reflectance;
float phi12 = 0.0;
// iridescenceIOR has limited greater than 1.0
// if (iridescenceIOR < outsideIOR) {phi12 = PI;}
float phi21 = PI - phi12;

// Second interface
vec3 baseIOR = fresnelToIOR(clamp(baseF0, 0.0, 0.9999)); // guard against 1.0
vec3 r1 = iorToFresnel0(baseIOR, iridescenceIOR);
vec3 r23 = F_Schlick(r1, cosTheta2);
vec3 phi23 =vec3(0.0);
if (baseIOR[0] < iridescenceIOR) {phi23[0] = PI;}
if (baseIOR[1] < iridescenceIOR) {phi23[1] = PI;}
if (baseIOR[2] < iridescenceIOR) {phi23[2] = PI;}

// Phase shift
float opd = 2.0 * iridescenceIOR * iridescenceThickness * cosTheta2;
vec3 phi = vec3(phi21) + phi23;

// Compound terms
vec3 r123 = clamp(reflectance * r23, 1e-5, 0.9999);
vec3 sr123 = sqrt(r123);
vec3 rs = pow2(t121) * r23 / (vec3(1.0) - r123);
// Reflectance term for m = 0 (DC term amplitude)
vec3 c0 = reflectance + rs;
iridescence = c0;
// Reflectance term for m > 0 (pairs of diracs)
vec3 cm = rs - t121;
for (int m = 1; m <= 2; ++m) {
cm *= sr123;
vec3 sm = 2.0 * evalSensitivity(float(m) * opd, float(m) * phi);
iridescence += cm * sm;
}
return iridescence = max(iridescence, vec3(0.0));
}
#endif

// GGX Distribution, Schlick Fresnel, GGX-Smith Visibility
vec3 BRDF_Specular_GGX(vec3 incidentDirection, Geometry geometry, vec3 normal, vec3 specularColor, float roughness ) {
vec3 BRDF_Specular_GGX(vec3 incidentDirection, Geometry geometry, Material material, vec3 normal, vec3 specularColor, float roughness ) {

float alpha = pow2( roughness ); // UE4's roughness

Expand All @@ -149,12 +235,18 @@ vec3 BRDF_Specular_GGX(vec3 incidentDirection, Geometry geometry, vec3 normal, v
float dotNH = saturate( dot( normal, halfDir ) );
float dotLH = saturate( dot( incidentDirection, halfDir ) );

vec3 F = F_Schlick( specularColor, dotLH );
#ifdef MATERIAL_ENABLE_IRIDESCENCE
F = mix(F, material.iridescenceSpecularColor, material.iridescenceFactor);
#endif

#ifdef MATERIAL_ENABLE_ANISOTROPY
return anisotropicLobe(halfDir, incidentDirection, geometry, specularColor, alpha, dotNV, dotNL, dotNH, dotLH);
float GD = DG_GGX_anisotropic(halfDir, incidentDirection, geometry, alpha, dotNV, dotNL, dotNH);
#else
return isotropicLobe(specularColor, alpha, dotNV, dotNL, dotNH, dotLH);
float GD = DG_GGX(alpha, dotNV, dotNL, dotNH);
#endif

return F * GD;
}

vec3 BRDF_Diffuse_Lambert(vec3 diffuseColor ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ void addDirectRadiance(vec3 incidentDirection, vec3 color, Geometry geometry, Ma
float clearCoatDotNL = saturate( dot( geometry.clearCoatNormal, incidentDirection ) );
vec3 clearCoatIrradiance = clearCoatDotNL * color;

reflectedLight.directSpecular += material.clearCoat * clearCoatIrradiance * BRDF_Specular_GGX( incidentDirection, geometry, geometry.clearCoatNormal, vec3( 0.04 ), material.clearCoatRoughness );
reflectedLight.directSpecular += material.clearCoat * clearCoatIrradiance * BRDF_Specular_GGX( incidentDirection, geometry, material, geometry.clearCoatNormal, vec3( 0.04 ), material.clearCoatRoughness );
attenuation -= material.clearCoat * F_Schlick(material.f0, geometry.clearCoatDotNV);
#endif

float dotNL = saturate( dot( geometry.normal, incidentDirection ) );
vec3 irradiance = dotNL * color * PI;

reflectedLight.directSpecular += attenuation * irradiance * BRDF_Specular_GGX( incidentDirection, geometry, geometry.normal, material.specularColor, material.roughness);
reflectedLight.directSpecular += attenuation * irradiance * BRDF_Specular_GGX( incidentDirection, geometry, material, geometry.normal, material.specularColor, material.roughness);
reflectedLight.directDiffuse += attenuation * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );

// Sheen Lobe
Expand Down
8 changes: 7 additions & 1 deletion packages/core/src/shaderlib/pbr/pbr_frag.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,13 @@ float radianceAttenuation = 1.0;
radianceAttenuation -= material.clearCoat * F_Schlick(material.f0, geometry.clearCoatDotNV);
#endif

reflectedLight.indirectSpecular += material.specularAO * radianceAttenuation * radiance * material.envSpecularDFG;
#ifdef MATERIAL_ENABLE_IRIDESCENCE
vec3 speculaColor = mix(material.specularColor, material.iridescenceSpecularColor, material.iridescenceFactor);
#else
vec3 speculaColor = material.specularColor;
#endif

reflectedLight.indirectSpecular += material.specularAO * radianceAttenuation * radiance * envBRDFApprox(speculaColor, material.roughness, geometry.dotNV);


// IBL Sheen
Expand Down
19 changes: 19 additions & 0 deletions packages/core/src/shaderlib/pbr/pbr_frag_define.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,18 @@ uniform float material_OcclusionTextureCoord;
#endif
#endif


#ifdef MATERIAL_ENABLE_IRIDESCENCE
uniform vec4 material_IridescenceInfo;
#ifdef MATERIAL_HAS_IRIDESCENCE_THICKNESS_TEXTURE
uniform sampler2D material_IridescenceThicknessTexture;
#endif

#ifdef MATERIAL_HAS_IRIDESCENCE_TEXTURE
uniform sampler2D material_IridescenceTexture;
#endif
#endif

// Runtime
struct ReflectedLight {
vec3 directDiffuse;
Expand Down Expand Up @@ -118,6 +130,13 @@ struct Material {
float clearCoatRoughness;
#endif

#ifdef MATERIAL_ENABLE_IRIDESCENCE
float iridescenceIOR;
float iridescenceFactor;
float iridescenceThickness;
vec3 iridescenceSpecularColor;
#endif

#ifdef MATERIAL_ENABLE_SHEEN
float sheenRoughness;
vec3 sheenColor;
Expand Down
22 changes: 22 additions & 0 deletions packages/core/src/shaderlib/pbr/pbr_helper.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,28 @@ void initMaterial(out Material material, inout Geometry geometry){
material.sheenScaling = 1.0 - material.approxIBLSheenDG * max(max(material.sheenColor.r, material.sheenColor.g), material.sheenColor.b);
#endif

// Iridescence
#ifdef MATERIAL_ENABLE_IRIDESCENCE
material.iridescenceFactor = material_IridescenceInfo.x;
material.iridescenceIOR = material_IridescenceInfo.y;

#ifdef MATERIAL_HAS_IRIDESCENCE_THICKNESS_TEXTURE
float iridescenceThicknessWeight = texture2D( material_IridescenceThicknessTexture, v_uv).g;
material.iridescenceThickness = mix(material_IridescenceInfo.z, material_IridescenceInfo.w, iridescenceThicknessWeight);
#else
material.iridescenceThickness = material_IridescenceInfo.w;
#endif

#ifdef MATERIAL_HAS_IRIDESCENCE_TEXTURE
material.iridescenceFactor *= texture2D( material_IridescenceTexture, v_uv).r;
#endif

#ifdef MATERIAL_ENABLE_IRIDESCENCE
float topIOR = 1.0;
material.iridescenceSpecularColor = evalIridescenceSpecular(topIOR, geometry.dotNV, material.iridescenceIOR, material.specularColor, material.iridescenceThickness);
#endif
#endif

}


Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ void evaluateSpecularIBL(Varyings varyings, SurfaceData surfaceData, BRDFData br
vec3 speculaColor = brdfData.specularColor;
#endif

outSpecularColor += surfaceData.specularAO * radianceAttenuation * radiance * brdfData.envSpecularDFG;
outSpecularColor += surfaceData.specularAO * radianceAttenuation * radiance * envBRDFApprox(speculaColor, brdfData.roughness, surfaceData.dotNV);
}

void evaluateSheenIBL(Varyings varyings, SurfaceData surfaceData, BRDFData brdfData, float radianceAttenuation, inout vec3 diffuseColor, inout vec3 specularColor){
Expand Down

0 comments on commit afd64de

Please sign in to comment.