diff --git a/packages/core/src/material/PBRBaseMaterial.ts b/packages/core/src/material/PBRBaseMaterial.ts index f9a2a38848..ddb60a3333 100644 --- a/packages/core/src/material/PBRBaseMaterial.ts +++ b/packages/core/src/material/PBRBaseMaterial.ts @@ -15,10 +15,22 @@ export abstract class PBRBaseMaterial extends BaseMaterial { private static _normalTextureProp = Shader.getPropertyByName("u_normalTexture"); private static _normalTextureIntensityProp = Shader.getPropertyByName("u_normalIntensity"); private static _occlusionTextureIntensityProp = Shader.getPropertyByName("u_occlusionStrength"); - private static _emissiveTextureProp = Shader.getPropertyByName("u_emissiveSampler"); private static _occlusionTextureProp = Shader.getPropertyByName("u_occlusionSampler"); + private static _clearcoatProp = Shader.getPropertyByName("u_clearcoat"); + private static _clearcoatTextureProp = Shader.getPropertyByName("u_clearcoatTexture"); + private static _clearcoatRoughnessProp = Shader.getPropertyByName("u_clearcoatRoughness"); + private static _clearcoatRoughnessTextureProp = Shader.getPropertyByName("u_clearcoatRoughnessTexture"); + private static _clearcoatNormalTextureProp = Shader.getPropertyByName("u_clearcoatNormalTexture"); + + private static _sheenColor = Shader.getPropertyByName("u_sheenColor"); + private static _sheenColorTexture = Shader.getPropertyByName("u_sheenColorTexture"); + private static _sheenRoughness = Shader.getPropertyByName("u_sheenRoughness"); + private static _sheenRoughnessTexture = Shader.getPropertyByName("u_sheenRoughnessTexture"); + + private _sheenEnabled: boolean = false; + /** * Base color. */ @@ -148,6 +160,163 @@ export abstract class PBRBaseMaterial extends BaseMaterial { } } + /** + * The clearcoat layer intensity, default 0. + */ + get clearcoat(): number { + return this.shaderData.getFloat(PBRBaseMaterial._clearcoatProp); + } + + set clearcoat(value: number) { + this.shaderData.setFloat(PBRBaseMaterial._clearcoatProp, value); + + if (value === 0) { + this.shaderData.disableMacro("CLEARCOAT"); + } else { + this.shaderData.enableMacro("CLEARCOAT"); + } + } + + /** + * The clearcoat layer intensity texture. + */ + get clearcoatTexture(): Texture2D { + return this.shaderData.getTexture(PBRBaseMaterial._clearcoatTextureProp); + } + + set clearcoatTexture(value: Texture2D) { + this.shaderData.setTexture(PBRBaseMaterial._clearcoatTextureProp, value); + + if (value) { + this.shaderData.enableMacro("HAS_CLEARCOATTEXTURE"); + } else { + this.shaderData.disableMacro("HAS_CLEARCOATTEXTURE"); + } + } + + /** + * The clearcoat layer roughness, default 0. + */ + get clearcoatRoughness(): number { + return this.shaderData.getFloat(PBRBaseMaterial._clearcoatRoughnessProp); + } + + set clearcoatRoughness(value: number) { + this.shaderData.setFloat(PBRBaseMaterial._clearcoatRoughnessProp, value); + } + + /** + * The clearcoat layer roughness texture. + */ + get clearcoatRoughnessTexture(): Texture2D { + return this.shaderData.getTexture(PBRBaseMaterial._clearcoatRoughnessTextureProp); + } + + set clearcoatRoughnessTexture(value: Texture2D) { + this.shaderData.setTexture(PBRBaseMaterial._clearcoatRoughnessTextureProp, value); + + if (value) { + this.shaderData.enableMacro("HAS_CLEARCOATROUGHNESSTEXTURE"); + } else { + this.shaderData.disableMacro("HAS_CLEARCOATROUGHNESSTEXTURE"); + } + } + + /** + * The clearcoat normal map texture. + */ + get clearcoatNormalTexture(): Texture2D { + return this.shaderData.getTexture(PBRBaseMaterial._clearcoatNormalTextureProp); + } + + set clearcoatNormalTexture(value: Texture2D) { + this.shaderData.setTexture(PBRBaseMaterial._clearcoatNormalTextureProp, value); + + if (value) { + this.shaderData.enableMacro("HAS_CLEARCOATNORMALTEXTURE"); + } else { + this.shaderData.disableMacro("HAS_CLEARCOATNORMALTEXTURE"); + } + } + + /** + * Sheen enabled. + * @remark + */ + get sheenEnabled(): boolean { + return this._sheenEnabled; + } + + set sheenEnabled(value: boolean) { + this._sheenEnabled = value; + + if (value) { + this.shaderData.enableMacro("SHEEN"); + } else { + this.shaderData.disableMacro("SHEEN"); + } + } + + /** + * Sheen color, default [0,0,0]. + */ + get sheenColor(): Color { + return this.shaderData.getColor(PBRBaseMaterial._sheenColor); + } + + set sheenColor(value: Color) { + const baseColor = this.shaderData.getColor(PBRBaseMaterial._sheenColor); + + if (value !== baseColor) { + value.cloneTo(baseColor); + } + } + + /** + * The sheen color texture. + */ + get sheenColorTexture(): Texture2D { + return this.shaderData.getTexture(PBRBaseMaterial._sheenColorTexture); + } + + set sheenColorTexture(value: Texture2D) { + this.shaderData.setTexture(PBRBaseMaterial._sheenColorTexture, value); + + if (value) { + this.shaderData.enableMacro("HAS_SHEENCOLORTEXTURE"); + } else { + this.shaderData.disableMacro("HAS_SHEENCOLORTEXTURE"); + } + } + + /** + * Sheen roughness, default 0. + */ + get sheenRoughness(): number { + return this.shaderData.getFloat(PBRBaseMaterial._sheenRoughness); + } + + set sheenRoughness(value: number) { + this.shaderData.setFloat(PBRBaseMaterial._sheenRoughness, value); + } + + /** + * The sheen roughness texture. + */ + get sheenRoughnessTexture(): Texture2D { + return this.shaderData.getTexture(PBRBaseMaterial._sheenRoughnessTexture); + } + + set sheenRoughnessTexture(value: Texture2D) { + this.shaderData.setTexture(PBRBaseMaterial._sheenRoughnessTexture, value); + + if (value) { + this.shaderData.enableMacro("HAS_SHEENROUGHNESSTEXTURE"); + } else { + this.shaderData.disableMacro("HAS_SHEENROUGHNESSTEXTURE"); + } + } + /** * Create a pbr base material instance. * @param engine - Engine to which the material belongs @@ -167,5 +336,21 @@ export abstract class PBRBaseMaterial extends BaseMaterial { shaderData.setFloat(PBRBaseMaterial._normalTextureIntensityProp, 1); shaderData.setFloat(PBRBaseMaterial._occlusionTextureIntensityProp, 1); + + shaderData.setFloat(PBRBaseMaterial._clearcoatProp, 0); + shaderData.setFloat(PBRBaseMaterial._clearcoatRoughnessProp, 0); + + shaderData.setColor(PBRBaseMaterial._sheenColor, new Color(0, 0, 0, 1)); + shaderData.setFloat(PBRBaseMaterial._sheenRoughness, 0); + } + + /** + * @override + * Clone to the target material. + * @param target - target material + */ + cloneTo(target: PBRBaseMaterial): void { + super.cloneTo(target); + target._sheenEnabled = this._sheenEnabled; } } diff --git a/packages/core/src/material/PBRMaterial.ts b/packages/core/src/material/PBRMaterial.ts index 3074529797..e1ddc11e25 100644 --- a/packages/core/src/material/PBRMaterial.ts +++ b/packages/core/src/material/PBRMaterial.ts @@ -10,9 +10,10 @@ export class PBRMaterial extends PBRBaseMaterial { private static _metallicProp = Shader.getPropertyByName("u_metal"); private static _roughnessProp = Shader.getPropertyByName("u_roughness"); private static _metallicRoughnessTextureProp = Shader.getPropertyByName("u_metallicRoughnessSampler"); + private static _iorProp = Shader.getPropertyByName("u_ior"); /** - * Metallic. + * Metallic, default 1. */ get metallic(): number { return this.shaderData.getFloat(PBRMaterial._metallicProp); @@ -23,7 +24,7 @@ export class PBRMaterial extends PBRBaseMaterial { } /** - * Roughness. + * Roughness, default 1. */ get roughness(): number { return this.shaderData.getFloat(PBRMaterial._roughnessProp); @@ -50,14 +51,30 @@ export class PBRMaterial extends PBRBaseMaterial { } } + /** + * Index of refraction of the material, default 1.5 . + * @remarks It influence the F0 of dielectric materials. + */ + get ior(): number { + return this.shaderData.getFloat(PBRMaterial._iorProp); + } + + set ior(value: number) { + this.shaderData.setFloat(PBRMaterial._iorProp, value); + } + /** * Create a pbr metallic-roughness workflow material instance. * @param engine - Engine to which the material belongs */ constructor(engine: Engine) { super(engine, Shader.find("pbr")); - this.shaderData.setFloat(PBRMaterial._metallicProp, 1.0); - this.shaderData.setFloat(PBRMaterial._roughnessProp, 1.0); + + const shaderData = this.shaderData; + + shaderData.setFloat(PBRMaterial._metallicProp, 1); + shaderData.setFloat(PBRMaterial._roughnessProp, 1); + shaderData.setFloat(PBRMaterial._iorProp, 1.5); } /** diff --git a/packages/core/src/shaderlib/begin_normal_vert.glsl b/packages/core/src/shaderlib/begin_normal_vert.glsl index 1f0b8d165b..c7d1a3553b 100644 --- a/packages/core/src/shaderlib/begin_normal_vert.glsl +++ b/packages/core/src/shaderlib/begin_normal_vert.glsl @@ -1,11 +1,9 @@ +#ifndef OMIT_NORMAL #ifdef O3_HAS_NORMAL + vec3 normal = vec3( NORMAL ); + #endif - vec3 normal = vec3( NORMAL ); - - #if defined( O3_HAS_TANGENT ) && defined( O3_NORMAL_TEXTURE ) - + #ifdef O3_HAS_TANGENT vec4 tangent = vec4( TANGENT ); - - #endif - #endif +#endif \ No newline at end of file diff --git a/packages/core/src/shaderlib/common.glsl b/packages/core/src/shaderlib/common.glsl index 026ff80b34..615638debf 100644 --- a/packages/core/src/shaderlib/common.glsl +++ b/packages/core/src/shaderlib/common.glsl @@ -6,6 +6,10 @@ #define saturate( a ) clamp( a, 0.0, 1.0 ) #define whiteCompliment(a) ( 1.0 - saturate( a ) ) +float pow2(float x ) { + return x * x; +} + vec4 RGBMToLinear(vec4 value, float maxRange ) { return vec4( value.rgb * value.a * maxRange, 1.0 ); } diff --git a/packages/core/src/shaderlib/mobile_blinnphong_frag.glsl b/packages/core/src/shaderlib/mobile_blinnphong_frag.glsl index 3b2b08c78f..d0d9de454b 100644 --- a/packages/core/src/shaderlib/mobile_blinnphong_frag.glsl +++ b/packages/core/src/shaderlib/mobile_blinnphong_frag.glsl @@ -1,4 +1,9 @@ - vec3 N = getNormal(); + vec3 N = getNormal( + #ifdef O3_NORMAL_TEXTURE + u_normalTexture, + u_normalIntensity + #endif + ); vec3 lightDiffuse = vec3( 0.0, 0.0, 0.0 ); vec3 lightSpecular = vec3( 0.0, 0.0, 0.0 ); diff --git a/packages/core/src/shaderlib/normal_get.glsl b/packages/core/src/shaderlib/normal_get.glsl index b7b2223d71..e6fe635d07 100644 --- a/packages/core/src/shaderlib/normal_get.glsl +++ b/packages/core/src/shaderlib/normal_get.glsl @@ -1,7 +1,24 @@ -vec3 getNormal() +vec3 getNormal(){ + vec3 normal = vec3(0, 0, 1); + + #ifdef O3_HAS_NORMAL + normal = normalize(v_normal); + #elif defined(HAS_DERIVATIVES) + vec3 pos_dx = dFdx(v_pos); + vec3 pos_dy = dFdy(v_pos); + normal = normalize( cross(pos_dx, pos_dy) ); + #endif + + normal *= float( gl_FrontFacing ) * 2.0 - 1.0; + return normal; +} + +vec3 getNormal(sampler2D normalTexture, float normalIntensity) { - #ifdef O3_NORMAL_TEXTURE - #ifndef O3_HAS_TANGENT + + #if defined(O3_HAS_NORMAL) && defined(O3_HAS_TANGENT) && defined( O3_NORMAL_TEXTURE ) + mat3 tbn = v_TBN; + #else #ifdef HAS_DERIVATIVES vec3 pos_dx = dFdx(v_pos); vec3 pos_dy = dFdy(v_pos); @@ -24,24 +41,11 @@ vec3 getNormal() #endif mat3 tbn = mat3(vec3(0.0), vec3(0.0), ng); #endif - #else - mat3 tbn = v_TBN; - #endif - vec3 n = texture2D(u_normalTexture, v_uv ).rgb; - n = normalize(tbn * ((2.0 * n - 1.0) * vec3(u_normalIntensity, u_normalIntensity, 1.0))); - #else - #ifdef O3_HAS_NORMAL - vec3 n = normalize(v_normal); - #elif defined(HAS_DERIVATIVES) - vec3 pos_dx = dFdx(v_pos); - vec3 pos_dy = dFdy(v_pos); - vec3 n = normalize( cross(pos_dx, pos_dy) ); - #else - vec3 n= vec3(0.0,0.0,1.0); #endif - #endif - n *= float( gl_FrontFacing ) * 2.0 - 1.0; + vec3 normal = texture2D(normalTexture, v_uv ).rgb; + normal = normalize(tbn * ((2.0 * normal - 1.0) * vec3(normalIntensity, normalIntensity, 1.0))); + normal *= float( gl_FrontFacing ) * 2.0 - 1.0; - return n; + return normal; } diff --git a/packages/core/src/shaderlib/normal_share.glsl b/packages/core/src/shaderlib/normal_share.glsl index d21d9322bc..094be54d11 100644 --- a/packages/core/src/shaderlib/normal_share.glsl +++ b/packages/core/src/shaderlib/normal_share.glsl @@ -1,13 +1,8 @@ -#ifdef O3_HAS_NORMAL - - #if defined( O3_HAS_TANGENT ) && defined( O3_NORMAL_TEXTURE ) - - varying mat3 v_TBN; - - #else - - varying vec3 v_normal; - +#ifndef OMIT_NORMAL + #ifdef O3_HAS_NORMAL + varying vec3 v_normal; + #if defined( O3_HAS_TANGENT ) && defined( O3_NORMAL_TEXTURE ) + varying mat3 v_TBN; + #endif #endif - -#endif +#endif \ No newline at end of file diff --git a/packages/core/src/shaderlib/normal_vert.glsl b/packages/core/src/shaderlib/normal_vert.glsl index 938ce04cac..03a42c72dc 100644 --- a/packages/core/src/shaderlib/normal_vert.glsl +++ b/packages/core/src/shaderlib/normal_vert.glsl @@ -1,16 +1,13 @@ +#ifndef OMIT_NORMAL #ifdef O3_HAS_NORMAL + v_normal = normalize( mat3(u_normalMat) * normal ); #if defined( O3_HAS_TANGENT ) && defined( O3_NORMAL_TEXTURE ) + vec3 normalW = normalize( mat3(u_normalMat) * normal.xyz ); + vec3 tangentW = normalize( mat3(u_normalMat) * tangent.xyz ); + vec3 bitangentW = cross( normalW, tangentW ) * tangent.w; - vec3 normalW = normalize( mat3(u_normalMat) * normal.xyz ); - vec3 tangentW = normalize( mat3(u_normalMat) * tangent.xyz ); - vec3 bitangentW = cross( normalW, tangentW ) * tangent.w; - v_TBN = mat3( tangentW, bitangentW, normalW ); - - #else - - v_normal = normalize( mat3(u_normalMat) * normal ); - + v_TBN = mat3( tangentW, bitangentW, normalW ); #endif - #endif +#endif \ No newline at end of file diff --git a/packages/core/src/shaderlib/pbr/brdf.glsl b/packages/core/src/shaderlib/pbr/brdf.glsl index 4026207888..ba5bb7dbf2 100644 --- a/packages/core/src/shaderlib/pbr/brdf.glsl +++ b/packages/core/src/shaderlib/pbr/brdf.glsl @@ -1,3 +1,7 @@ +float F_Schlick(float dotLH) { + return 0.04 + 0.96 * (pow(1.0 - dotLH, 5.0)); +} + vec3 F_Schlick(vec3 specularColor, float dotLH ) { // Original approximation by Christophe Schlick '94 @@ -39,15 +43,15 @@ float D_GGX(float alpha, float dotNH ) { } // GGX Distribution, Schlick Fresnel, GGX-Smith Visibility -vec3 BRDF_Specular_GGX(vec3 incidentDirection, GeometricContext geometry, vec3 specularColor, float roughness ) { +vec3 BRDF_Specular_GGX(vec3 incidentDirection, vec3 viewDir, vec3 normal, vec3 specularColor, float roughness ) { float alpha = pow2( roughness ); // UE4's roughness - vec3 halfDir = normalize( incidentDirection + geometry.viewDir ); + vec3 halfDir = normalize( incidentDirection + viewDir ); - float dotNL = saturate( dot( geometry.normal, incidentDirection ) ); - float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) ); - float dotNH = saturate( dot( geometry.normal, halfDir ) ); + float dotNL = saturate( dot( normal, incidentDirection ) ); + float dotNV = saturate( dot( normal, viewDir ) ); + float dotNH = saturate( dot( normal, halfDir ) ); float dotLH = saturate( dot( incidentDirection, halfDir ) ); vec3 F = F_Schlick( specularColor, dotLH ); @@ -59,3 +63,35 @@ vec3 BRDF_Specular_GGX(vec3 incidentDirection, GeometricContext geometry, vec3 s return F * ( G * D ); } + +vec3 BRDF_Diffuse_Lambert(vec3 diffuseColor ) { + return RECIPROCAL_PI * diffuseColor; +} + + +#ifdef SHEEN + float D_Charlie(float roughness, float NoH) { + float alpha = pow2( roughness ); + // Estevez and Kulla 2017, "Production Friendly Microfacet Sheen BRDF" + float invAlpha = 1.0 / roughness; + float cos2h = NoH * NoH; + float sin2h = max(1.0 - cos2h, 0.0078125); // 2^(-14/2), so sin2h^2 > 0 in fp16 + + return (2.0 + invAlpha) * pow(sin2h, invAlpha * 0.5) / (2.0 * PI); + } + + float V_Neubelt(float NoV, float NoL) { + // Neubelt and Pettineo 2013, "Crafting a Next-gen Material Pipeline for The Order: 1886" + return saturate(1.0 / (4.0 * (NoL + NoV - NoL * NoV))); + } + + vec3 BRDF_Specular_Sheen(vec3 lightDir, Geometry geometry, vec3 sheenColor, float sheenRoughness ) { + vec3 halfDir = normalize( lightDir + geometry.viewDir ); + float dotNL = saturate( dot( geometry.normal, lightDir ) ); + float dotNH = saturate( dot( geometry.normal, halfDir ) ); + float D = D_Charlie( sheenRoughness, dotNH ); + float V = V_Neubelt( geometry.dotNV, dotNL ); + + return sheenColor * ( D * V ); + } +#endif \ No newline at end of file diff --git a/packages/core/src/shaderlib/pbr/direct_irradiance_frag_define.glsl b/packages/core/src/shaderlib/pbr/direct_irradiance_frag_define.glsl index 152150dd30..17fee6919c 100644 --- a/packages/core/src/shaderlib/pbr/direct_irradiance_frag_define.glsl +++ b/packages/core/src/shaderlib/pbr/direct_irradiance_frag_define.glsl @@ -1,18 +1,28 @@ -void addDirectRadiance(vec3 incidentDirection, vec3 color, GeometricContext geometry, PhysicalMaterial material, inout ReflectedLight reflectedLight) { +void addDirectRadiance(vec3 incidentDirection, vec3 color, Geometry geometry, Material material, inout ReflectedLight reflectedLight) { float dotNL = saturate( dot( geometry.normal, incidentDirection ) ); + vec3 irradiance = dotNL * color * PI; - vec3 irradiance = dotNL * color; - irradiance *= PI; + #ifdef CLEARCOAT + float clearcoatDotNL = saturate( dot( geometry.clearcoatNormal, incidentDirection ) ); + vec3 clearcoatIrradiance = clearcoatDotNL * color; + + reflectedLight.directSpecular += material.clearcoat * clearcoatIrradiance * BRDF_Specular_GGX( incidentDirection, geometry.viewDir, geometry.clearcoatNormal, vec3( 0.04 ), material.clearcoatRoughness ); + #endif + - reflectedLight.directSpecular += irradiance * BRDF_Specular_GGX( incidentDirection, geometry, material.specularColor, material.roughness); + #ifdef SHEEN + reflectedLight.directSpecular += material.clearcoatAttenuation * irradiance * BRDF_Specular_Sheen(incidentDirection, geometry, material.sheenColor, material.sheenRoughness); + #endif - reflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor ); + float attenuation = material.clearcoatAttenuation * material.sheenAttenuation; + reflectedLight.directSpecular += attenuation * irradiance * BRDF_Specular_GGX( incidentDirection, geometry.viewDir, geometry.normal, material.specularColor, material.roughness); + reflectedLight.directDiffuse += attenuation * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor ); } #ifdef O3_DIRECT_LIGHT_COUNT - void addDirectionalDirectLightRadiance(DirectLight directionalLight, GeometricContext geometry, PhysicalMaterial material, inout ReflectedLight reflectedLight) { + void addDirectionalDirectLightRadiance(DirectLight directionalLight, Geometry geometry, Material material, inout ReflectedLight reflectedLight) { vec3 color = directionalLight.color; vec3 direction = -directionalLight.direction; @@ -24,7 +34,7 @@ void addDirectRadiance(vec3 incidentDirection, vec3 color, GeometricContext geom #ifdef O3_POINT_LIGHT_COUNT - void addPointDirectLightRadiance(PointLight pointLight, GeometricContext geometry, PhysicalMaterial material, inout ReflectedLight reflectedLight) { + void addPointDirectLightRadiance(PointLight pointLight, Geometry geometry, Material material, inout ReflectedLight reflectedLight) { vec3 lVector = pointLight.position - geometry.position; vec3 direction = normalize( lVector ); @@ -42,7 +52,7 @@ void addDirectRadiance(vec3 incidentDirection, vec3 color, GeometricContext geom #ifdef O3_SPOT_LIGHT_COUNT - void addSpotDirectLightRadiance(SpotLight spotLight, GeometricContext geometry, PhysicalMaterial material, inout ReflectedLight reflectedLight) { + void addSpotDirectLightRadiance(SpotLight spotLight, Geometry geometry, Material material, inout ReflectedLight reflectedLight) { vec3 lVector = spotLight.position - geometry.position; vec3 direction = normalize( lVector ); @@ -63,7 +73,7 @@ void addDirectRadiance(vec3 incidentDirection, vec3 color, GeometricContext geom #endif -void addTotalDirectRadiance(GeometricContext geometry, PhysicalMaterial material, inout ReflectedLight reflectedLight){ +void addTotalDirectRadiance(Geometry geometry, Material material, inout ReflectedLight reflectedLight){ #ifdef O3_DIRECT_LIGHT_COUNT DirectLight directionalLight; diff --git a/packages/core/src/shaderlib/pbr/ibl_frag_define.glsl b/packages/core/src/shaderlib/pbr/ibl_frag_define.glsl index 1a4a3ff8ff..84f37e7921 100644 --- a/packages/core/src/shaderlib/pbr/ibl_frag_define.glsl +++ b/packages/core/src/shaderlib/pbr/ibl_frag_define.glsl @@ -42,7 +42,7 @@ float getSpecularMIPLevel(float roughness, int maxMIPLevel ) { return roughness * float(maxMIPLevel); } -vec3 getLightProbeRadiance(GeometricContext geometry, float roughness, int maxMIPLevel, float specularIntensity) { +vec3 getLightProbeRadiance(vec3 viewDir, vec3 normal, float roughness, int maxMIPLevel, float specularIntensity) { #ifndef O3_USE_SPECULAR_ENV @@ -50,7 +50,7 @@ vec3 getLightProbeRadiance(GeometricContext geometry, float roughness, int maxMI #else - vec3 reflectVec = reflect( -geometry.viewDir, geometry.normal ); + vec3 reflectVec = reflect( -viewDir, normal ); float specularMIPLevel = getSpecularMIPLevel(roughness, maxMIPLevel ); @@ -75,4 +75,15 @@ vec3 getLightProbeRadiance(GeometricContext geometry, float roughness, int maxMI #endif -} \ No newline at end of file +} + +#ifdef SHEEN + // https://drive.google.com/file/d/1T0D1VSyR4AllqIJTQAraEIzjlb5h4FKH + float envBRDFApprox_Sheen( float dotNV, float roughness) { + float r2 = pow2(roughness); + float a = roughness < 0.25 ? -339.2 * r2 + 161.4 * roughness - 25.9 : -8.48 * r2 + 14.3 * roughness - 9.95; + float b = roughness < 0.25 ? 44.0 * r2 - 23.7 * roughness + 3.26 : 1.97 * r2 - 3.27 * roughness + 0.72; + float DG = exp( a * dotNV + b ) + ( roughness < 0.25 ? 0.0 : 0.1 * ( roughness - 0.25 ) ); + return saturate( DG ); + } +#endif \ No newline at end of file diff --git a/packages/core/src/shaderlib/pbr/pbr_frag.glsl b/packages/core/src/shaderlib/pbr/pbr_frag.glsl index be129ebba1..f0899dbeab 100644 --- a/packages/core/src/shaderlib/pbr/pbr_frag.glsl +++ b/packages/core/src/shaderlib/pbr/pbr_frag.glsl @@ -1,12 +1,14 @@ -GeometricContext geometry = GeometricContext(v_pos, getNormal(), normalize(u_cameraPos - v_pos)); -PhysicalMaterial material = getPhysicalMaterial(u_baseColor, u_metal, u_roughness, u_specularColor, u_glossiness, u_alphaCutoff); -ReflectedLight reflectedLight = ReflectedLight( vec3( 0 ), vec3( 0 ), vec3( 0 ), vec3( 0 ) ); -float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) ); +Geometry geometry; +Material material; +ReflectedLight reflectedLight; -// Direct Light +initGeometry(geometry); +initMaterial(material, geometry); + +/** ------ Direct Light ------ */ addTotalDirectRadiance(geometry, material, reflectedLight); -// IBL diffuse +/** ------ IBL irradiance ------ */ #ifdef O3_USE_SH vec3 irradiance = getLightProbeIrradiance(u_env_sh, geometry.normal); #ifdef OASIS_COLORSPACE_GAMMA @@ -20,30 +22,44 @@ addTotalDirectRadiance(geometry, material, reflectedLight); reflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor ); -// IBL specular -vec3 radiance = getLightProbeRadiance( geometry, material.roughness, int(u_envMapLight.mipMapLevel), u_envMapLight.specularIntensity); -reflectedLight.indirectSpecular += radiance * envBRDFApprox(material.specularColor, material.roughness, dotNV ); +/** ------ IBL radiance ------ */ +vec3 radiance = getLightProbeRadiance(geometry.viewDir, geometry.normal, material.roughness, int(u_envMapLight.mipMapLevel), u_envMapLight.specularIntensity); + +#ifdef CLEARCOAT + vec3 clearcoatRadiance = getLightProbeRadiance( geometry.viewDir, geometry.clearcoatNormal, material.clearcoatRoughness, int(u_envMapLight.mipMapLevel), u_envMapLight.specularIntensity ); + reflectedLight.indirectSpecular += clearcoatRadiance * material.clearcoat * envBRDFApprox(vec3( 0.04 ), material.clearcoatRoughness, geometry.clearcoatDotNV); +#endif + +#ifdef SHEEN + vec3 sheenRadiance = getLightProbeRadiance(geometry.viewDir, geometry.normal, material.sheenRoughness, int(u_envMapLight.mipMapLevel), u_envMapLight.specularIntensity ); + reflectedLight.indirectSpecular += material.clearcoatAttenuation * sheenRadiance * material.sheenColor * material.sheenEnvBRDF; +#endif + +// attenuation(total) = attenuation(clearcoat + sheen) +float attenuation = material.clearcoatAttenuation * material.sheenAttenuation; +reflectedLight.indirectSpecular += attenuation * radiance * envBRDFApprox(material.specularColor, material.roughness, geometry.dotNV ); + -// Occlusion +/** ------ occlusion ------ */ #ifdef HAS_OCCLUSIONMAP float ambientOcclusion = (texture2D(u_occlusionSampler, v_uv).r - 1.0) * u_occlusionStrength + 1.0; reflectedLight.indirectDiffuse *= ambientOcclusion; #ifdef O3_USE_SPECULAR_ENV - reflectedLight.indirectSpecular *= computeSpecularOcclusion(ambientOcclusion, material.roughness, dotNV); + reflectedLight.indirectSpecular *= attenuation * computeSpecularOcclusion(ambientOcclusion, material.roughness, geometry.dotNV); #endif #endif -// Emissive +/** ------ emissive ------ */ vec3 emissiveRadiance = u_emissiveColor; #ifdef HAS_EMISSIVEMAP vec4 emissiveColor = texture2D(u_emissiveSampler, v_uv); #ifndef OASIS_COLORSPACE_GAMMA emissiveColor = gammaToLinear(emissiveColor); #endif - emissiveRadiance *= emissiveColor.rgb; + emissiveRadiance *= attenuation * emissiveColor.rgb; #endif -// Total +/** ------ Total ------ */ vec3 totalRadiance = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + diff --git a/packages/core/src/shaderlib/pbr/pbr_frag_define.glsl b/packages/core/src/shaderlib/pbr/pbr_frag_define.glsl index 6ddb7d742c..3b45da3ac7 100644 --- a/packages/core/src/shaderlib/pbr/pbr_frag_define.glsl +++ b/packages/core/src/shaderlib/pbr/pbr_frag_define.glsl @@ -1,5 +1,5 @@ uniform float u_alphaCutoff; - +uniform float u_ior; uniform vec4 u_baseColor; uniform float u_metal; uniform float u_roughness; @@ -7,10 +7,21 @@ uniform vec3 u_specularColor; uniform float u_glossiness; uniform vec3 u_emissiveColor; +#ifdef CLEARCOAT + uniform float u_clearcoat; + uniform float u_clearcoatRoughness; +#endif + +#ifdef SHEEN + uniform vec3 u_sheenColor; + uniform float u_sheenRoughness; +#endif + uniform float u_normalIntensity; uniform float u_occlusionStrength; +// Texture #ifdef HAS_BASECOLORMAP uniform sampler2D u_baseColorSampler; #endif @@ -36,21 +47,65 @@ uniform float u_occlusionStrength; uniform sampler2D u_occlusionSampler; #endif +#ifdef HAS_CLEARCOATTEXTURE + uniform sampler2D u_clearcoatTexture; +#endif + +#ifdef HAS_CLEARCOATROUGHNESSTEXTURE + uniform sampler2D u_clearcoatRoughnessTexture; +#endif + +#ifdef HAS_CLEARCOATNORMALTEXTURE + uniform sampler2D u_clearcoatNormalTexture; +#endif + +#ifdef HAS_SHEENCOLORTEXTURE + uniform sampler2D u_sheenColorTexture; +#endif + +#ifdef HAS_SHEENROUGHNESSTEXTURE + uniform sampler2D u_sheenRoughnessTexture; +#endif + +// Runtime struct ReflectedLight { vec3 directDiffuse; vec3 directSpecular; vec3 indirectDiffuse; vec3 indirectSpecular; }; -struct GeometricContext { + +struct Geometry { vec3 position; vec3 normal; vec3 viewDir; + float dotNV; + + #ifdef CLEARCOAT + vec3 clearcoatNormal; + float clearcoatDotNV; + #endif + }; -struct PhysicalMaterial { + +struct Material { vec3 diffuseColor; float roughness; vec3 specularColor; float opacity; + float clearcoatAttenuation; + float sheenAttenuation; + + #ifdef CLEARCOAT + float clearcoat; + float clearcoatRoughness; + #endif + + #ifdef SHEEN + vec3 sheenColor; + float sheenRoughness; + float sheenEnvBRDF; + #endif + }; diff --git a/packages/core/src/shaderlib/pbr/pbr_helper.glsl b/packages/core/src/shaderlib/pbr/pbr_helper.glsl index 5d626fbc8f..d79a226c38 100644 --- a/packages/core/src/shaderlib/pbr/pbr_helper.glsl +++ b/packages/core/src/shaderlib/pbr/pbr_helper.glsl @@ -1,44 +1,79 @@ #include +// direct + indirect +#include +#include +#include -float pow2(float x ) { - return x * x; +float computeSpecularOcclusion(float ambientOcclusion, float roughness, float dotNV ) { + return saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion ); } -vec3 BRDF_Diffuse_Lambert(vec3 diffuseColor ) { - return RECIPROCAL_PI * diffuseColor; +float getAARoughnessFactor(vec3 normal) { + #ifdef HAS_DERIVATIVES + vec3 dxy = max( abs(dFdx(normal)), abs(dFdy(normal)) ); + return max( max(dxy.x, dxy.y), dxy.z ); + #else + return 0.0; + #endif } +float getSheenAlbedoScaling(vec3 sheenColor, float sheenEnvBRDF){ + return 1.0 - sheenEnvBRDF * max( max(sheenColor.r, sheenColor.g), sheenColor.b ); +} -float computeSpecularOcclusion(float ambientOcclusion, float roughness, float dotNV ) { - return saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion ); +float getF0(float ior, float outsideIor){ + return pow2( (ior - outsideIor) / (ior + outsideIor) ); } -PhysicalMaterial getPhysicalMaterial( - vec4 diffuseColor, - float metal, - float roughness, - vec3 specularColor, - float glossiness, - float alphaCutoff - ){ - PhysicalMaterial material; +void initGeometry(out Geometry geometry){ + geometry.position = v_pos; + geometry.viewDir = normalize(u_cameraPos - v_pos); + + geometry.normal = getNormal( + #ifdef O3_NORMAL_TEXTURE + u_normalTexture, + u_normalIntensity + #endif + ); + + #ifdef CLEARCOAT + geometry.clearcoatNormal = getNormal( + #ifdef HAS_CLEARCOATNORMALTEXTURE + u_clearcoatNormalTexture, + u_normalIntensity + #endif + ); + geometry.clearcoatDotNV = saturate( dot(geometry.clearcoatNormal, geometry.viewDir) ); + #endif + + geometry.dotNV = saturate( dot(geometry.normal, geometry.viewDir) ); +} + +void initMaterial(out Material material, const in Geometry geometry){ + vec4 baseColor = u_baseColor; + float metal = u_metal; + float roughness = u_roughness; + vec3 specularColor = u_specularColor; + float glossiness = u_glossiness; + float alphaCutoff = u_alphaCutoff; + float F0 = getF0(u_ior, 1.0); #ifdef HAS_BASECOLORMAP - vec4 baseColor = texture2D(u_baseColorSampler, v_uv); + vec4 baseTextureColor = texture2D(u_baseColorSampler, v_uv); #ifndef OASIS_COLORSPACE_GAMMA - baseColor = gammaToLinear(baseColor); + baseTextureColor = gammaToLinear(baseTextureColor); #endif - diffuseColor *= baseColor; + baseColor *= baseTextureColor; #endif #ifdef O3_HAS_VERTEXCOLOR - diffuseColor *= v_color; + baseColor *= v_color; #endif #ifdef ALPHA_CUTOFF - if( diffuseColor.a < alphaCutoff ) { + if( baseColor.a < alphaCutoff ) { discard; } #endif @@ -51,28 +86,65 @@ PhysicalMaterial getPhysicalMaterial( #ifdef HAS_SPECULARGLOSSINESSMAP vec4 specularGlossinessColor = texture2D(u_specularGlossinessSampler, v_uv ); + #ifndef OASIS_COLORSPACE_GAMMA + specularGlossinessColor = gammaToLinear(specularGlossinessColor); + #endif specularColor *= specularGlossinessColor.rgb; glossiness *= specularGlossinessColor.a; #endif #ifdef IS_METALLIC_WORKFLOW - material.diffuseColor = diffuseColor.rgb * ( 1.0 - metal ); - material.specularColor = mix( vec3( 0.04), diffuseColor.rgb, metal ); - material.roughness = clamp( roughness, 0.04, 1.0 ); + material.diffuseColor = baseColor.rgb * ( 1.0 - metal ); + material.specularColor = mix( vec3(F0), baseColor.rgb, metal ); + material.roughness = roughness; #else float specularStrength = max( max( specularColor.r, specularColor.g ), specularColor.b ); - material.diffuseColor = diffuseColor.rgb * ( 1.0 - specularStrength ); + material.diffuseColor = baseColor.rgb * ( 1.0 - specularStrength ); material.specularColor = specularColor; - material.roughness = clamp( 1.0 - glossiness, 0.04, 1.0 ); + material.roughness = 1.0 - glossiness; #endif - material.opacity = diffuseColor.a; - return material; + material.roughness = max(material.roughness, getAARoughnessFactor(geometry.normal)); -} + #ifdef CLEARCOAT + material.clearcoat = u_clearcoat; + material.clearcoatRoughness = u_clearcoatRoughness; + #ifdef HAS_CLEARCOATTEXTURE + material.clearcoat *= texture2D( u_clearcoatTexture, v_uv ).r; + #endif + #ifdef HAS_CLEARCOATROUGHNESSTEXTURE + material.clearcoatRoughness *= texture2D( u_clearcoatRoughnessTexture, v_uv ).g; + #endif + material.clearcoat = saturate( material.clearcoat ); + material.clearcoatRoughness = max(material.clearcoatRoughness, getAARoughnessFactor(geometry.clearcoatNormal)); + material.clearcoatAttenuation = 1.0 - material.clearcoat * F_Schlick(geometry.clearcoatDotNV); + #else + material.clearcoatAttenuation = 1.0; + #endif -// direct + indirect -#include -#include -#include \ No newline at end of file + #ifdef SHEEN + material.sheenColor = u_sheenColor; + + #ifdef HAS_SHEENCOLORTEXTURE + vec4 sheenColorTextureValue = texture2D( u_sheenColorTexture, v_uv ); + #ifndef OASIS_COLORSPACE_GAMMA + sheenColorTextureValue = gammaToLinear(sheenColorTextureValue); + #endif + material.sheenColor *= sheenColorTextureValue.rgb; + #endif + + material.sheenRoughness = max( u_sheenRoughness, 0.01 ); + + #ifdef HAS_SHEENROUGHNESSTEXTURE + material.sheenRoughness *= texture2D( u_sheenRoughnessTexture, v_uv ).a; + #endif + + material.sheenEnvBRDF = envBRDFApprox_Sheen(geometry.dotNV, material.sheenRoughness ); + material.sheenAttenuation = getSheenAlbedoScaling(material.sheenColor, material.sheenEnvBRDF); + #else + material.sheenAttenuation = 1.0; + #endif + + material.opacity = baseColor.a; +} \ No newline at end of file diff --git a/packages/loader/src/gltf/extensions/KHR_materials_clearcoat.ts b/packages/loader/src/gltf/extensions/KHR_materials_clearcoat.ts index e69de29bb2..4d92337e6c 100644 --- a/packages/loader/src/gltf/extensions/KHR_materials_clearcoat.ts +++ b/packages/loader/src/gltf/extensions/KHR_materials_clearcoat.ts @@ -0,0 +1,36 @@ +import { PBRMaterial } from "@oasis-engine/core"; +import { GLTFResource } from "../GLTFResource"; +import { MaterialParser } from "../parser/MaterialParser"; +import { registerExtension } from "../parser/Parser"; +import { ExtensionParser } from "./ExtensionParser"; +import { IKHRMaterialsClearcoat } from "./Schema"; + +@registerExtension("KHR_materials_clearcoat") +class KHR_materials_clearcoat extends ExtensionParser { + parseEngineResource(schema: IKHRMaterialsClearcoat, material: PBRMaterial, context: GLTFResource): void { + const { textures } = context; + const { + clearcoatFactor = 0, + clearcoatTexture, + clearcoatRoughnessFactor = 0, + clearcoatRoughnessTexture, + clearcoatNormalTexture + } = schema; + + material.clearcoat = clearcoatFactor; + material.clearcoatRoughness = clearcoatRoughnessFactor; + + if (clearcoatTexture) { + material.clearcoatTexture = textures[clearcoatTexture.index]; + MaterialParser._parseTextureTransform(material, clearcoatTexture.extensions, context); + } + if (clearcoatRoughnessTexture) { + material.clearcoatRoughnessTexture = textures[clearcoatRoughnessTexture.index]; + MaterialParser._parseTextureTransform(material, clearcoatRoughnessTexture.extensions, context); + } + if (clearcoatNormalTexture) { + material.clearcoatNormalTexture = textures[clearcoatNormalTexture.index]; + MaterialParser._parseTextureTransform(material, clearcoatNormalTexture.extensions, context); + } + } +} diff --git a/packages/loader/src/gltf/extensions/KHR_materials_ior.ts b/packages/loader/src/gltf/extensions/KHR_materials_ior.ts index e69de29bb2..d81e0f1731 100644 --- a/packages/loader/src/gltf/extensions/KHR_materials_ior.ts +++ b/packages/loader/src/gltf/extensions/KHR_materials_ior.ts @@ -0,0 +1,14 @@ +import { PBRMaterial } from "@oasis-engine/core"; +import { GLTFResource } from "../GLTFResource"; +import { registerExtension } from "../parser/Parser"; +import { ExtensionParser } from "./ExtensionParser"; +import { IKHRMaterialsIor } from "./Schema"; + +@registerExtension("KHR_materials_ior") +class KHR_materials_ior extends ExtensionParser { + parseEngineResource(schema: IKHRMaterialsIor, material: PBRMaterial, context: GLTFResource): void { + const { ior = 1.5 } = schema; + + material.ior = ior; + } +} diff --git a/packages/loader/src/gltf/extensions/KHR_materials_pbrSpecularGlossiness.ts b/packages/loader/src/gltf/extensions/KHR_materials_pbrSpecularGlossiness.ts index fd78e27579..6faeef7983 100644 --- a/packages/loader/src/gltf/extensions/KHR_materials_pbrSpecularGlossiness.ts +++ b/packages/loader/src/gltf/extensions/KHR_materials_pbrSpecularGlossiness.ts @@ -14,7 +14,12 @@ class KHR_materials_pbrSpecularGlossiness extends ExtensionParser { const { diffuseFactor, diffuseTexture, specularFactor, glossinessFactor, specularGlossinessTexture } = schema; if (diffuseFactor) { - material.baseColor = new Color(...diffuseFactor); + material.baseColor = new Color( + Color.linearToGammaSpace(diffuseFactor[0]), + Color.linearToGammaSpace(diffuseFactor[1]), + Color.linearToGammaSpace(diffuseFactor[2]), + diffuseFactor[3] + ); } if (diffuseTexture) { @@ -23,7 +28,11 @@ class KHR_materials_pbrSpecularGlossiness extends ExtensionParser { } if (specularFactor) { - material.specularColor = new Color(...specularFactor); + material.specularColor = new Color( + Color.linearToGammaSpace(specularFactor[0]), + Color.linearToGammaSpace(specularFactor[1]), + Color.linearToGammaSpace(specularFactor[2]) + ); } if (glossinessFactor !== undefined) { diff --git a/packages/loader/src/gltf/extensions/KHR_materials_sheen.ts b/packages/loader/src/gltf/extensions/KHR_materials_sheen.ts index e69de29bb2..10cc8cb09f 100644 --- a/packages/loader/src/gltf/extensions/KHR_materials_sheen.ts +++ b/packages/loader/src/gltf/extensions/KHR_materials_sheen.ts @@ -0,0 +1,36 @@ +import { PBRMaterial } from "@oasis-engine/core"; +import { Color } from "@oasis-engine/math"; +import { GLTFResource } from "../GLTFResource"; +import { MaterialParser } from "../parser/MaterialParser"; +import { registerExtension } from "../parser/Parser"; +import { ExtensionParser } from "./ExtensionParser"; +import { IKHRMaterialsSheen } from "./Schema"; + +@registerExtension("KHR_materials_sheen") +class KHR_materials_sheen extends ExtensionParser { + parseEngineResource(schema: IKHRMaterialsSheen, material: PBRMaterial, context: GLTFResource): void { + const { textures } = context; + const { sheenColorFactor, sheenColorTexture, sheenRoughnessFactor = 0, sheenRoughnessTexture } = schema; + + if (sheenColorFactor) { + material.sheenEnabled = true; + material.sheenColor = new Color( + Color.linearToGammaSpace(sheenColorFactor[0]), + Color.linearToGammaSpace(sheenColorFactor[1]), + Color.linearToGammaSpace(sheenColorFactor[2]) + ); + } + + if (sheenColorTexture) { + material.sheenColorTexture = textures[sheenColorTexture.index]; + MaterialParser._parseTextureTransform(material, sheenColorTexture.extensions, context); + } + + material.sheenRoughness = sheenRoughnessFactor; + + if (sheenRoughnessTexture) { + material.sheenRoughnessTexture = textures[sheenRoughnessTexture.index]; + MaterialParser._parseTextureTransform(material, sheenRoughnessTexture.extensions, context); + } + } +} diff --git a/packages/loader/src/gltf/parser/MaterialParser.ts b/packages/loader/src/gltf/parser/MaterialParser.ts index 6ab5d02737..1ce9ac7e3a 100644 --- a/packages/loader/src/gltf/parser/MaterialParser.ts +++ b/packages/loader/src/gltf/parser/MaterialParser.ts @@ -33,7 +33,13 @@ export class MaterialParser extends Parser { name = "" } = gltf.materials[i]; - const { KHR_materials_unlit, KHR_materials_pbrSpecularGlossiness } = extensions; + const { + KHR_materials_unlit, + KHR_materials_pbrSpecularGlossiness, + KHR_materials_clearcoat, + KHR_materials_sheen, + KHR_materials_ior + } = extensions; let material: UnlitMaterial | PBRMaterial | PBRSpecularMaterial = null; @@ -53,12 +59,29 @@ export class MaterialParser extends Parser { material.name = name; + if (KHR_materials_clearcoat) { + Parser.parseEngineResource("KHR_materials_clearcoat", KHR_materials_clearcoat, material, context); + } + + if (KHR_materials_sheen) { + Parser.parseEngineResource("KHR_materials_sheen", KHR_materials_sheen, material, context); + } + + if (KHR_materials_ior) { + Parser.parseEngineResource("KHR_materials_ior", KHR_materials_ior, material, context); + } + if (pbrMetallicRoughness) { const { baseColorFactor, baseColorTexture, metallicFactor, roughnessFactor, metallicRoughnessTexture } = pbrMetallicRoughness; if (baseColorFactor) { - material.baseColor = new Color(...baseColorFactor); + material.baseColor = new Color( + Color.linearToGammaSpace(baseColorFactor[0]), + Color.linearToGammaSpace(baseColorFactor[1]), + Color.linearToGammaSpace(baseColorFactor[2]), + baseColorFactor[3] + ); } if (baseColorTexture) { material.baseTexture = textures[baseColorTexture.index]; @@ -85,7 +108,11 @@ export class MaterialParser extends Parser { } if (emissiveFactor) { - m.emissiveColor = new Color(...emissiveFactor); + m.emissiveColor = new Color( + Color.linearToGammaSpace(emissiveFactor[0]), + Color.linearToGammaSpace(emissiveFactor[1]), + Color.linearToGammaSpace(emissiveFactor[2]) + ); } if (normalTexture) {