Skip to content

Commit

Permalink
feat: support iridescence in ShaderLab PBR
Browse files Browse the repository at this point in the history
  • Loading branch information
hhhhkrx committed Nov 6, 2024
1 parent 3be869b commit be59c8f
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 2 deletions.
9 changes: 9 additions & 0 deletions packages/shaderlab/src/shaders/PBR.gs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ Shader "PBR.gs" {
material_ClearCoatNormalTexture("ClearCoatNormalTexture", Texture2D);
}

Header("Thin Film Iridescence"){
material_IridescenceInfo("Iridescence", Vector4) = (0, 0, 0, 0);
material_IridescenceThicknessTexture("IridescenceThicknessTexture", Texture2D);
material_IridescenceTexture("IridescenceTexture", Texture2D);
}

Header("Common") {
material_AlphaCutoff( "AlphaCutoff", Range(0, 1, 0.01) ) = 0;
material_TilingOffset("TilingOffset", Vector4) = (1, 1, 0, 0);
Expand All @@ -60,6 +66,9 @@ Shader "PBR.gs" {
MATERIAL_HAS_CLEAR_COAT_TEXTURE("HAS_CLEAR_COAT_TEXTURE");
MATERIAL_HAS_CLEAR_COAT_ROUGHNESS_TEXTURE("HAS_CLEAR_COAT_ROUGHNESS_TEXTURE");
MATERIAL_HAS_CLEAR_COAT_NORMAL_TEXTURE("HAS_CLEAR_COAT_NORMAL_TEXTURE");
MATERIAL_ENABLE_IRIDESCENCE("ENABLE_IRIDESCENCE");
MATERIAL_HAS_IRIDESCENCE_THICKNESS_TEXTURE("HAS_IRIDESCENCE_THICKNESS_TEXTURE");
MATERIAL_HAS_IRIDESCENCE_TEXTURE("HAS_IRIDESCENCE_TEXTURE");
MATERIAL_IS_TRANSPARENT("IS_TRANSPARENT");
MATERIAL_IS_ALPHA_CUTOFF("IS_ALPHA_CUTOFF");
}
Expand Down
110 changes: 110 additions & 0 deletions packages/shaderlab/src/shaders/shadingPBR/BRDF.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ struct SurfaceData{
vec3 clearCoatNormal;
float clearCoatDotNV;
#endif

#ifdef MATERIAL_ENABLE_IRIDESCENCE
float iridesceceIor;
float iridesceceFactor;
float iridescenceThickness;
#endif
};


Expand All @@ -61,6 +67,10 @@ struct BRDFData{
vec3 clearCoatSpecularColor;
float clearCoatRoughness;
#endif

#ifdef MATERIAL_ENABLE_IRIDESCENCE
vec3 iridescenceSpecularColor;
#endif
};


Expand Down Expand Up @@ -202,6 +212,100 @@ vec3 BRDF_Diffuse_Lambert(vec3 diffuseColor) {
return RECIPROCAL_PI * diffuseColor;
}

#ifdef MATERIAL_ENABLE_IRIDESCENCE
float sqr(float x) {
return x * x;
}

// Conversion f0/ior
vec3 iorToFresnel(vec3 transmittedIor, float incidentIor) {
return pow((transmittedIor - incidentIor) / (transmittedIor + incidentIor),vec3(2.0));
}

float iorToFresnel(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 * sqr(phase));
xyz.x += 9.7470e-14 * sqrt(2.0 * PI * 4.5282e+09) * cos(2.2399e+06 * phase + shift[0]) * exp(-4.5282e+09 * sqr(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 EvalIridescence(float outsideIOR, float cosTheta1, float eta2, vec3 baseF0,float iridescenceThickness){
vec3 iridescence = vec3(1.0);
// Force iridescenceIOR -> outsideIOR when thinFilmThickness -> 0.0
float iridescenceIOR = mix( outsideIOR, eta2, 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( cosTheta1, 2.0));
float cosTheta2Sq = 1.0 - sinTheta2Sq;
// Handle total internal reflection
if (cosTheta2Sq < 0.0) {
return iridescence;
}
float cosTheta2 = sqrt(cosTheta2Sq);

// First interface
float R0 = iorToFresnel(iridescenceIOR, outsideIOR);
float R12 = F_Schlick(R0, cosTheta1);
float R21 = R12;
float T121 = 1.0 - R12;
float phi12 = 0.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 =iorToFresnel(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(R12 * R23, 1e-5, 0.9999);
vec3 r123 = sqrt(R123);
vec3 Rs = sqr(T121) * R23 / (vec3(1.0) - R123);
// Reflectance term for m = 0 (DC term amplitude)
vec3 C0 = R12 + Rs;
iridescence = C0;
// Reflectance term for m > 0 (pairs of diracs)
vec3 Cm = Rs - T121;
for (int m = 1; m <= 2; ++m) {
Cm *= r123;
vec3 Sm = 2.0 * evalSensitivity(float(m) * OPD, float(m) * phi);
iridescence += Cm * Sm;
}
iridescence = max(iridescence, vec3(0.0));
return iridescence;
}
#endif

void initBRDFData(SurfaceData surfaceData, out BRDFData brdfData){
vec3 albedoColor = surfaceData.albedoColor;
Expand All @@ -225,6 +329,12 @@ void initBRDFData(SurfaceData surfaceData, out BRDFData brdfData){
brdfData.clearCoatRoughness = max(MIN_PERCEPTUAL_ROUGHNESS, min(surfaceData.clearCoatRoughness + getAARoughnessFactor(surfaceData.clearCoatNormal), 1.0));
brdfData.clearCoatSpecularColor = vec3(0.04);
#endif

#ifdef MATERIAL_ENABLE_IRIDESCENCE
float topIor = 1.0;
float NdotV = saturate(dot(surfaceData.normal, surfaceData.viewDir ) );
brdfData.iridescenceSpecularColor = EvalIridescence(topIor, NdotV, surfaceData.iridesceceIor, brdfData.specularColor, surfaceData.iridescenceThickness);
#endif
}

#endif
31 changes: 31 additions & 0 deletions packages/shaderlab/src/shaders/shadingPBR/FragmentPBR.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,17 @@ float material_OcclusionTextureCoord;
#endif
#endif

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

#ifdef MATERIAL_HAS_IRIDESCENCE_TEXTURE
sampler2D material_IridescenceTexture;
#endif
#endif

// Texture
#ifdef MATERIAL_HAS_BASETEXTURE
sampler2D material_BaseTexture;
Expand Down Expand Up @@ -235,6 +246,26 @@ SurfaceData getSurfaceData(Varyings v, vec2 aoUV, bool isFrontFacing){
surfaceData.anisotropicN = getAnisotropicBentNormal(surfaceData);
#endif

//Iridescence
#ifdef MATERIAL_ENABLE_IRIDESCENCE
surfaceData.iridesceceFactor = material_IridescenceInfo.x;
surfaceData.iridesceceIor = material_IridescenceInfo.y;

float iridesceceThicknessMin = material_IridescenceInfo.z;
float iridesceceThicknessMax = material_IridescenceInfo.w;
#ifdef MATERIAL_HAS_IRIDESCENCE_THICKNESS_TEXTURE
vec3 iridescenceThicknessInfo = (texture2D( material_IridescenceThicknessTexture, uv)).rgb;
surfaceData.iridescenceThickness = mix(iridesceceThicknessMin, iridesceceThicknessMax, iridescenceThicknessInfo.g);
#else
surfaceData.iridescenceThickness = iridesceceThicknessMax;
#endif

#ifdef MATERIAL_HAS_IRIDESCENCE_TEXTURE
vec3 iridecenceIntensity = (texture2D( material_IridescenceTexture, uv)).rgb;
surfaceData.iridesceceFactor *= iridecenceIntensity.x;
#endif
#endif

// AO
float diffuseAO = 1.0;
float specularAO = 1.0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
#ifndef FUNCTION_CLEAR_COAT_LOBE
#define FUNCTION_CLEAR_COAT_LOBE clearCoatLobe
#endif
#ifndef FUNCTION_IRIDESCENCE_LOBE
#define FUNCTION_IRIDESCENCE_LOBE iridescenceLobe
#endif

#include "BRDF.glsl"
#include "Light.glsl"
Expand All @@ -34,7 +37,9 @@ void surfaceShading(Varyings varyings, SurfaceData surfaceData, BRDFData brdfDat
FUNCTION_DIFFUSE_LOBE(varyings, surfaceData, brdfData, attenuationIrradiance, diffuseColor);
// Specular Lobe
FUNCTION_SPECULAR_LOBE(varyings, surfaceData, brdfData, incidentDirection, attenuationIrradiance, specularColor);

// Iridescence Lobe
FUNCTION_IRIDESCENCE_LOBE(varyings, surfaceData, brdfData, incidentDirection, attenuationIrradiance, specularColor);

color += diffuseColor + specularColor;

}
Expand Down
19 changes: 18 additions & 1 deletion packages/shaderlab/src/shaders/shadingPBR/LightIndirectPBR.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
#ifndef FUNCTION_CLEAR_COAT_IBL
#define FUNCTION_CLEAR_COAT_IBL evaluateClearCoatIBL
#endif

#ifndef FUNCTION_IRIDESCENCE_IBL
#define FUNCTION_IRIDESCENCE_IBL evaluateIridescenceIBL
#endif

#include "BRDF.glsl"
#include "Light.glsl"
Expand Down Expand Up @@ -66,6 +68,18 @@ float evaluateClearCoatIBL(Varyings varyings, SurfaceData surfaceData, BRDFData
return radianceAttenuation;
}

void evaluateIridescenceIBL(Varyings varyings, SurfaceData surfaceData, BRDFData brdfData, float radianceAttenuation, inout vec3 specularColor){
vec3 radiance = getLightProbeRadiance(surfaceData, surfaceData.normal, brdfData.roughness);
vec3 envBRDF = envBRDFApprox(brdfData.specularColor, brdfData.roughness, surfaceData.dotNV);

#ifdef MATERIAL_ENABLE_IRIDESCENCE
envBRDF = mix(envBRDF, brdfData.iridescenceSpecularColor, surfaceData.iridesceceFactor);
#endif

specularColor += surfaceData.specularAO * radianceAttenuation * radiance * envBRDF;

}

void evaluateSpecularIBL(Varyings varyings, SurfaceData surfaceData, BRDFData brdfData, float radianceAttenuation, inout vec3 specularColor){
vec3 radiance = getLightProbeRadiance(surfaceData, surfaceData.normal, brdfData.roughness);
specularColor += surfaceData.specularAO * radianceAttenuation * radiance * envBRDFApprox(brdfData.specularColor, brdfData.roughness, surfaceData.dotNV );
Expand All @@ -85,6 +99,9 @@ void evaluateIBL(Varyings varyings, SurfaceData surfaceData, BRDFData brdfData,
// IBL specular
FUNCTION_SPECULAR_IBL(varyings, surfaceData, brdfData, radianceAttenuation, specularColor);

// IBL Iridescence
FUNCTION_IRIDESCENCE_IBL(varyings, surfaceData, brdfData, radianceAttenuation, specularColor);

color += diffuseColor + specularColor;
}

Expand Down
8 changes: 8 additions & 0 deletions packages/shaderlab/src/shaders/shadingPBR/ReflectionLobe.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ float clearCoatLobe(Varyings varyings, SurfaceData surfaceData, BRDFData brdfDat
return attenuation;
}

void iridescenceLobe(Varyings varyings, SurfaceData surfaceData, BRDFData brdfData, vec3 incidentDirection, vec3 attenuationIrradiance, inout vec3 specularColor){
vec3 BRDF_Specular = BRDF_Specular_GGX( incidentDirection, surfaceData, surfaceData.normal, brdfData.specularColor, brdfData.roughness);

#ifdef MATERIAL_ENABLE_IRIDESCENCE
BRDF_Specular = mix(BRDF_Specular, brdfData.iridescenceSpecularColor, surfaceData.iridesceceFactor);
#endif

specularColor += attenuationIrradiance * BRDF_Specular;
}

#endif

0 comments on commit be59c8f

Please sign in to comment.