From be59c8f0c2cc9d50183ad3a44bb083fc5649ec1a Mon Sep 17 00:00:00 2001 From: hhhhkrx Date: Wed, 6 Nov 2024 15:13:21 +0800 Subject: [PATCH 1/2] feat: support iridescence in ShaderLab PBR --- packages/shaderlab/src/shaders/PBR.gs | 9 ++ .../src/shaders/shadingPBR/BRDF.glsl | 110 ++++++++++++++++++ .../src/shaders/shadingPBR/FragmentPBR.glsl | 31 +++++ .../shaders/shadingPBR/LightDirectPBR.glsl | 7 +- .../shaders/shadingPBR/LightIndirectPBR.glsl | 19 ++- .../shaders/shadingPBR/ReflectionLobe.glsl | 8 ++ 6 files changed, 182 insertions(+), 2 deletions(-) diff --git a/packages/shaderlab/src/shaders/PBR.gs b/packages/shaderlab/src/shaders/PBR.gs index f53f7e5c..500ffe0d 100644 --- a/packages/shaderlab/src/shaders/PBR.gs +++ b/packages/shaderlab/src/shaders/PBR.gs @@ -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); @@ -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"); } diff --git a/packages/shaderlab/src/shaders/shadingPBR/BRDF.glsl b/packages/shaderlab/src/shaders/shadingPBR/BRDF.glsl index dbfb122c..bf89b260 100644 --- a/packages/shaderlab/src/shaders/shadingPBR/BRDF.glsl +++ b/packages/shaderlab/src/shaders/shadingPBR/BRDF.glsl @@ -49,6 +49,12 @@ struct SurfaceData{ vec3 clearCoatNormal; float clearCoatDotNV; #endif + + #ifdef MATERIAL_ENABLE_IRIDESCENCE + float iridesceceIor; + float iridesceceFactor; + float iridescenceThickness; + #endif }; @@ -61,6 +67,10 @@ struct BRDFData{ vec3 clearCoatSpecularColor; float clearCoatRoughness; #endif + + #ifdef MATERIAL_ENABLE_IRIDESCENCE + vec3 iridescenceSpecularColor; + #endif }; @@ -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; @@ -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 \ No newline at end of file diff --git a/packages/shaderlab/src/shaders/shadingPBR/FragmentPBR.glsl b/packages/shaderlab/src/shaders/shadingPBR/FragmentPBR.glsl index 86286f81..3f630fc0 100644 --- a/packages/shaderlab/src/shaders/shadingPBR/FragmentPBR.glsl +++ b/packages/shaderlab/src/shaders/shadingPBR/FragmentPBR.glsl @@ -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; @@ -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; diff --git a/packages/shaderlab/src/shaders/shadingPBR/LightDirectPBR.glsl b/packages/shaderlab/src/shaders/shadingPBR/LightDirectPBR.glsl index 053b3b51..4fa3d201 100644 --- a/packages/shaderlab/src/shaders/shadingPBR/LightDirectPBR.glsl +++ b/packages/shaderlab/src/shaders/shadingPBR/LightDirectPBR.glsl @@ -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" @@ -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; } diff --git a/packages/shaderlab/src/shaders/shadingPBR/LightIndirectPBR.glsl b/packages/shaderlab/src/shaders/shadingPBR/LightIndirectPBR.glsl index 1cd46f1c..a9fe3e23 100644 --- a/packages/shaderlab/src/shaders/shadingPBR/LightIndirectPBR.glsl +++ b/packages/shaderlab/src/shaders/shadingPBR/LightIndirectPBR.glsl @@ -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" @@ -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 ); @@ -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; } diff --git a/packages/shaderlab/src/shaders/shadingPBR/ReflectionLobe.glsl b/packages/shaderlab/src/shaders/shadingPBR/ReflectionLobe.glsl index 31e448a4..d007a8f3 100644 --- a/packages/shaderlab/src/shaders/shadingPBR/ReflectionLobe.glsl +++ b/packages/shaderlab/src/shaders/shadingPBR/ReflectionLobe.glsl @@ -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 \ No newline at end of file From 2f0e91ab43cddc60225b1a5de9a18c197b206e54 Mon Sep 17 00:00:00 2001 From: hhhhkrx Date: Wed, 6 Nov 2024 17:23:01 +0800 Subject: [PATCH 2/2] fix: merge --- packages/shaderlab/src/index.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/shaderlab/src/index.ts b/packages/shaderlab/src/index.ts index df47aed1..d144ddf4 100644 --- a/packages/shaderlab/src/index.ts +++ b/packages/shaderlab/src/index.ts @@ -21,3 +21,8 @@ export function registerShader() { shaderRegistered = true; } + +/** + * @internal + */ +export { fragmentList };