Skip to content

Commit 12526b3

Browse files
author
Babylon.js Platform
committed
Merge remote-tracking branch 'origin/master'
2 parents d374fb4 + 1d41ccb commit 12526b3

39 files changed

+455
-217
lines changed

packages/dev/core/src/Engines/constants.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,33 @@ export class Constants {
484484
*/
485485
public static readonly MATERIAL_DIFFUSE_MODEL_LAMBERT = 2;
486486

487+
/**
488+
* Specular lighting for dielectric materials follows the logic
489+
* in the glTF specification and KHR_materials_specular extension.
490+
* Specular colour is applied only at normal incidence (i.e. F0) while
491+
* glancing angles (i.e. F90) tend towards white.
492+
*/
493+
public static readonly MATERIAL_DIELECTRIC_SPECULAR_MODEL_GLTF = 0;
494+
495+
/**
496+
* Specular lighting for dielectric materials follows the logic
497+
* in the OpenPBR specification. Specular colour is applied to all
498+
* dielectric reflection, not just at normal incidence (i.e. F0).
499+
*/
500+
public static readonly MATERIAL_DIELECTRIC_SPECULAR_MODEL_OPENPBR = 1;
501+
502+
/**
503+
* Specular lighting for metals follows the logic in the glTF specification.
504+
* Base colour is applied at F0 while glancing angles tend towards white.
505+
*/
506+
public static readonly MATERIAL_CONDUCTOR_SPECULAR_MODEL_GLTF = 0;
507+
508+
/**
509+
* Specular lighting for metals follows the logic in the OpenPBR specification.
510+
* Specular colour is applied to glancing angles using the F82 spec.
511+
*/
512+
public static readonly MATERIAL_CONDUCTOR_SPECULAR_MODEL_OPENPBR = 1;
513+
487514
/**
488515
* Nothing
489516
* @see https://doc.babylonjs.com/features/featuresDeepDive/events/actions#triggers

packages/dev/core/src/Materials/Node/Blocks/PBR/pbrMetallicRoughnessBlock.ts

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import { SubSurfaceBlock } from "./subSurfaceBlock";
2929
import type { RefractionBlock } from "./refractionBlock";
3030
import type { PerturbNormalBlock } from "../Fragment/perturbNormalBlock";
3131
import { Constants } from "../../../../Engines/constants";
32-
import { Color3, TmpColors } from "../../../../Maths/math.color";
32+
import { Color3 } from "../../../../Maths/math.color";
3333
import { Logger } from "core/Misc/logger";
3434
import {
3535
BindLight,
@@ -889,19 +889,9 @@ export class PBRMetallicRoughnessBlock extends NodeMaterialBlock {
889889
effect.setFloat4("vLightingIntensity", this.directIntensity, 1, this.environmentIntensity * this._scene.environmentIntensity, this.specularIntensity);
890890

891891
// reflectivity bindings
892-
const outsideIOR = 1; // consider air as clear coat and other layers would remap in the shader.
893-
const ior = this.indexOfRefraction.connectInputBlock?.value ?? 1.5;
894-
895-
// We are here deriving our default reflectance from a common value for none metallic surface.
896-
// Based of the schlick fresnel approximation model
897-
// for dielectrics.
898-
const f0 = Math.pow((ior - outsideIOR) / (ior + outsideIOR), 2);
899-
900-
// Tweak the default F0 and F90 based on our given setup
901-
this._metallicReflectanceColor.scaleToRef(f0 * this._metallicF0Factor, TmpColors.Color3[0]);
902892
const metallicF90 = this._metallicF0Factor;
903893

904-
effect.setColor4(this._vMetallicReflectanceFactorsName, TmpColors.Color3[0], metallicF90);
894+
effect.setColor4(this._vMetallicReflectanceFactorsName, this._metallicReflectanceColor, metallicF90);
905895

906896
if (nodeMaterial.imageProcessingConfiguration) {
907897
nodeMaterial.imageProcessingConfiguration.bind(effect);
@@ -1034,8 +1024,14 @@ export class PBRMetallicRoughnessBlock extends NodeMaterialBlock {
10341024
this._baseDiffuseRoughnessName = state._getFreeVariableName("baseDiffuseRoughness");
10351025
state._emitUniformFromString(this._baseDiffuseRoughnessName, NodeMaterialBlockConnectionPointTypes.Float);
10361026

1027+
const outsideIOR = 1; // consider air as clear coat and other layers would remap in the shader.
1028+
const ior = this.indexOfRefraction.connectInputBlock?.value ?? 1.5;
1029+
// Based of the schlick fresnel approximation model
1030+
// for dielectrics.
1031+
const f0 = Math.pow((ior - outsideIOR) / (ior + outsideIOR), 2);
1032+
10371033
code += `${state._declareLocalVar("baseColor", NodeMaterialBlockConnectionPointTypes.Vector3)} = surfaceAlbedo;
1038-
${isWebGPU ? "let" : `vec4${state.fSuffix}`} vReflectivityColor = vec4${state.fSuffix}(${this.metallic.associatedVariableName}, ${this.roughness.associatedVariableName}, ${this.indexOfRefraction.associatedVariableName || "1.5"}, 1.0);
1034+
${isWebGPU ? "let" : `vec4${state.fSuffix}`} vReflectivityColor = vec4${state.fSuffix}(${this.metallic.associatedVariableName}, ${this.roughness.associatedVariableName}, ${this.indexOfRefraction.associatedVariableName || "1.5"}, ${f0});
10391035
reflectivityOut = reflectivityBlock(
10401036
vReflectivityColor
10411037
#ifdef METALLICWORKFLOW

packages/dev/core/src/Materials/Node/Blocks/PBR/sheenBlock.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ export class SheenBlock extends NodeMaterialBlock {
141141
${isWebGPU ? `, ${texture}Sampler` : ""}
142142
, 1.0
143143
#endif
144-
, reflectance
144+
, reflectanceF0
145145
#ifdef SHEEN_LINKWITHALBEDO
146146
, baseColor
147147
, surfaceAlbedo

packages/dev/core/src/Materials/Node/Blocks/PBR/subSurfaceBlock.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,11 @@ export class SubSurfaceBlock extends NodeMaterialBlock {
188188
, vThicknessParam
189189
, vTintColor
190190
, normalW
191-
, specularEnvironmentReflectance
191+
#ifdef LEGACY_SPECULAR_ENERGY_CONSERVATION
192+
, vec3(max(cumulativeSpecularEnvironmentReflectance.r, max(cumulativeSpecularEnvironmentReflectance.g, cumulativeSpecularEnvironmentReflectance.b)))
193+
#else
194+
, baseSpecularEnvironmentReflectance
195+
#endif
192196
#ifdef SS_THICKNESSANDMASK_TEXTURE
193197
, vec4${state.fSuffix}(0.)
194198
#endif
@@ -282,7 +286,7 @@ export class SubSurfaceBlock extends NodeMaterialBlock {
282286
#endif
283287
#endif
284288
#else
285-
subSurfaceOut.specularEnvironmentReflectance = specularEnvironmentReflectance;
289+
subSurfaceOut.specularEnvironmentReflectance = cumulativeSpecularEnvironmentReflectance;
286290
#endif\n`;
287291

288292
return code;

packages/dev/core/src/Materials/PBR/pbrBRDFConfiguration.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export class MaterialBRDFDefines extends MaterialDefines {
1414
SPHERICAL_HARMONICS = false;
1515
SPECULAR_GLOSSINESS_ENERGY_CONSERVATION = false;
1616
MIX_IBL_RADIANCE_WITH_IRRADIANCE = true;
17+
LEGACY_SPECULAR_ENERGY_CONSERVATION = false;
1718
}
1819

1920
/**
@@ -53,6 +54,11 @@ export class PBRBRDFConfiguration extends MaterialPluginBase {
5354
*/
5455
public static DEFAULT_MIX_IBL_RADIANCE_WITH_IRRADIANCE = true;
5556

57+
/**
58+
* Default value for whether the legacy specular energy conservation is used.
59+
*/
60+
public static DEFAULT_USE_LEGACY_SPECULAR_ENERGY_CONSERVATION = true;
61+
5662
private _useEnergyConservation = PBRBRDFConfiguration.DEFAULT_USE_ENERGY_CONSERVATION;
5763
/**
5864
* Defines if the material uses energy conservation.
@@ -107,6 +113,15 @@ export class PBRBRDFConfiguration extends MaterialPluginBase {
107113
@expandToProperty("_markAllSubMeshesAsMiscDirty")
108114
public mixIblRadianceWithIrradiance = PBRBRDFConfiguration.DEFAULT_MIX_IBL_RADIANCE_WITH_IRRADIANCE;
109115

116+
private _useLegacySpecularEnergyConservation = PBRBRDFConfiguration.DEFAULT_USE_LEGACY_SPECULAR_ENERGY_CONSERVATION;
117+
/**
118+
* Defines if the legacy specular energy conservation is used.
119+
* If activated, the specular color is multiplied with (1. - maxChannel(albedo color)).
120+
*/
121+
@serialize()
122+
@expandToProperty("_markAllSubMeshesAsMiscDirty")
123+
public useLegacySpecularEnergyConservation = PBRBRDFConfiguration.DEFAULT_USE_LEGACY_SPECULAR_ENERGY_CONSERVATION;
124+
110125
/** @internal */
111126
private _internalMarkAllSubMeshesAsMiscDirty: () => void;
112127

@@ -136,6 +151,7 @@ export class PBRBRDFConfiguration extends MaterialPluginBase {
136151
defines.SPHERICAL_HARMONICS = this._useSphericalHarmonics;
137152
defines.SPECULAR_GLOSSINESS_ENERGY_CONSERVATION = this._useSpecularGlossinessInputEnergyConservation;
138153
defines.MIX_IBL_RADIANCE_WITH_IRRADIANCE = this._mixIblRadianceWithIrradiance;
154+
defines.LEGACY_SPECULAR_ENERGY_CONSERVATION = this._useLegacySpecularEnergyConservation;
139155
}
140156

141157
public override getClassName(): string {

packages/dev/core/src/Materials/PBR/pbrBaseMaterial.ts

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,8 @@ export class PBRMaterialDefines extends MaterialDefines implements IImageProcess
197197
public LINEARSPECULARREFLECTION = false;
198198
public RADIANCEOCCLUSION = false;
199199
public HORIZONOCCLUSION = false;
200+
public DIELECTRIC_SPECULAR_MODEL = 0;
201+
public CONDUCTOR_SPECULAR_MODEL = 0;
200202

201203
public INSTANCES = false;
202204
public THIN_INSTANCES = false;
@@ -827,6 +829,9 @@ export abstract class PBRBaseMaterial extends PushMaterial {
827829

828830
private _baseDiffuseModel: number = PBRBaseMaterial.DEFAULT_DIFFUSE_MODEL;
829831

832+
private _dielectricSpecularModel: number = Constants.MATERIAL_DIELECTRIC_SPECULAR_MODEL_GLTF;
833+
private _conductorSpecularModel: number = Constants.MATERIAL_CONDUCTOR_SPECULAR_MODEL_GLTF;
834+
830835
/**
831836
* Can this material render to several textures at once
832837
*/
@@ -2001,6 +2006,10 @@ export abstract class PBRBaseMaterial extends PushMaterial {
20012006

20022007
defines.HORIZONOCCLUSION = this._useHorizonOcclusion;
20032008

2009+
defines.DIELECTRIC_SPECULAR_MODEL = this._dielectricSpecularModel;
2010+
2011+
defines.CONDUCTOR_SPECULAR_MODEL = this._conductorSpecularModel;
2012+
20042013
// Misc.
20052014
if (defines._areMiscDirty) {
20062015
PrepareDefinesForMisc(
@@ -2363,22 +2372,18 @@ export abstract class PBRBaseMaterial extends PushMaterial {
23632372

23642373
// Colors
23652374
if (defines.METALLICWORKFLOW) {
2366-
TmpColors.Color3[0].r = this._metallic === undefined || this._metallic === null ? 1 : this._metallic;
2367-
TmpColors.Color3[0].g = this._roughness === undefined || this._roughness === null ? 1 : this._roughness;
2375+
TmpColors.Color4[0].r = this._metallic === undefined || this._metallic === null ? 1 : this._metallic;
2376+
TmpColors.Color4[0].g = this._roughness === undefined || this._roughness === null ? 1 : this._roughness;
23682377
const ior = this.subSurface?._indexOfRefraction ?? 1.5;
23692378
const outsideIOR = 1; // consider air as clear coat and other layers would remap in the shader.
2370-
TmpColors.Color3[0].b = ior;
2371-
ubo.updateColor4("vReflectivityColor", TmpColors.Color3[0], 1);
2379+
TmpColors.Color4[0].b = ior;
23722380
// We are here deriving our default reflectance from a common value for none metallic surface.
23732381
// Based of the schlick fresnel approximation model
23742382
// for dielectrics.
23752383
const f0 = Math.pow((ior - outsideIOR) / (ior + outsideIOR), 2);
2376-
2377-
// Tweak the default F0 and F90 based on our given setup
2378-
this._metallicReflectanceColor.scaleToRef(f0 * this._metallicF0Factor, TmpColors.Color3[0]);
2379-
const metallicF90 = this._metallicF0Factor;
2380-
2381-
ubo.updateColor4("vMetallicReflectanceFactors", TmpColors.Color3[0], metallicF90);
2384+
TmpColors.Color4[0].a = f0;
2385+
ubo.updateDirectColor4("vReflectivityColor", TmpColors.Color4[0]);
2386+
ubo.updateColor4("vMetallicReflectanceFactors", this._metallicReflectanceColor, this._metallicF0Factor);
23822387
} else {
23832388
ubo.updateColor4("vReflectivityColor", this._reflectivityColor, this._microSurface);
23842389
}

packages/dev/core/src/Materials/PBR/pbrMaterial.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,20 @@ export class PBRMaterial extends PBRBaseMaterial {
325325
@expandToProperty("_markAllSubMeshesAsTexturesDirty")
326326
public reflectionColor = new Color3(1.0, 1.0, 1.0);
327327

328+
/**
329+
* The material model to use for specular lighting of dielectric materials.
330+
*/
331+
@serialize("dielectricSpecularModel")
332+
@expandToProperty("_markAllSubMeshesAsMiscDirty")
333+
public dielectricSpecularModel: number = Constants.MATERIAL_DIELECTRIC_SPECULAR_MODEL_GLTF;
334+
335+
/**
336+
* The material model to use for specular lighting.
337+
*/
338+
@serialize("conductorSpecularModel")
339+
@expandToProperty("_markAllSubMeshesAsMiscDirty")
340+
public conductorSpecularModel: number = Constants.MATERIAL_CONDUCTOR_SPECULAR_MODEL_GLTF;
341+
328342
/**
329343
* The color emitted from the material.
330344
*/

packages/dev/core/src/Shaders/ShadersInclude/lightFragment.fx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,15 @@
103103
#if AREALIGHT{X}
104104
info.specular = computeAreaSpecularLighting(preInfo, light{X}.vLightSpecular.rgb);
105105
#else
106+
fresnel = fresnelSchlickGGX(preInfo.VdotH, vec3(reflectanceF0), specularEnvironmentR90);
107+
coloredFresnel = fresnelSchlickGGX(preInfo.VdotH, clearcoatOut.specularEnvironmentR0, specularEnvironmentR90);
108+
#ifndef LEGACY_SPECULAR_ENERGY_CONSERVATION
109+
info.diffuse *= (1.0 - fresnel);
110+
#endif
106111
#ifdef ANISOTROPIC
107112
info.specular = computeAnisotropicSpecularLighting(preInfo, viewDirectionW, normalW, anisotropicOut.anisotropicTangent, anisotropicOut.anisotropicBitangent, anisotropicOut.anisotropy, clearcoatOut.specularEnvironmentR0, specularEnvironmentR90, AARoughnessFactors.x, diffuse{X}.rgb);
108113
#else
109-
info.specular = computeSpecularLighting(preInfo, normalW, clearcoatOut.specularEnvironmentR0, specularEnvironmentR90, AARoughnessFactors.x, diffuse{X}.rgb, vReflectivityColor.b);
114+
info.specular = computeSpecularLighting(preInfo, normalW, clearcoatOut.specularEnvironmentR0, coloredFresnel, AARoughnessFactors.x, diffuse{X}.rgb);
110115
#endif
111116
#endif
112117
#endif

packages/dev/core/src/Shaders/ShadersInclude/pbrBRDFFunctions.fx

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
#define BRDF_DIFFUSE_MODEL_EON 0
44
#define BRDF_DIFFUSE_MODEL_BURLEY 1
55
#define BRDF_DIFFUSE_MODEL_LAMBERT 2
6+
#define DIELECTRIC_SPECULAR_MODEL_GLTF 0
7+
#define DIELECTRIC_SPECULAR_MODEL_OPENPBR 1
8+
#define CONDUCTOR_SPECULAR_MODEL_GLTF 0
9+
#define CONDUCTOR_SPECULAR_MODEL_OPENPBR 1
610

711
// ______________________________________________________________________
812
//
@@ -17,6 +21,27 @@
1721
}
1822
#endif
1923

24+
#if CONDUCTOR_SPECULAR_MODEL == CONDUCTOR_SPECULAR_MODEL_OPENPBR
25+
vec3 getF82Specular(float NdotV, vec3 F0, vec3 edgeTint, float roughness) {
26+
// F82 specular model for metals
27+
// https://www.slideshare.net/PeterKutz/fresnel-equations-considered-harmful
28+
// Peter Kutz, 2020
29+
const float cos_theta_max = 0.142857143; // 1 / 7
30+
31+
const float one_minus_cos_theta_max_to_the_fifth = 0.462664366; // (1 - cos_theta_max)^5
32+
const float one_minus_cos_theta_max_to_the_sixth = 0.396569457; // (1 - cos_theta_max)^6
33+
vec3 white_minus_F0 = vec3(1.0) - F0;
34+
vec3 b_numerator = (F0 + white_minus_F0 * one_minus_cos_theta_max_to_the_fifth) * (vec3(1.0) - edgeTint);
35+
const float b_denominator = cos_theta_max * one_minus_cos_theta_max_to_the_sixth;
36+
const float b_denominator_reciprocal = 1.0 / b_denominator;
37+
vec3 b = b_numerator * b_denominator_reciprocal; // analogous to "a" in the "Fresnel Equations Considered Harmful" slides
38+
float cos_theta = max(roughness, NdotV);
39+
float one_minus_cos_theta = 1.0 - cos_theta;
40+
vec3 offset_from_F0 = (white_minus_F0 - b * cos_theta * one_minus_cos_theta) * pow(one_minus_cos_theta, 5.0);
41+
return clamp(F0 + offset_from_F0, 0.0, 1.0);
42+
}
43+
#endif
44+
2045
#ifdef ENVIRONMENTBRDF
2146
vec3 getBRDFLookup(float NdotV, float perceptualRoughness) {
2247
// Indexed on cos(theta) and roughness
@@ -32,14 +57,9 @@
3257
return brdfLookup.rgb;
3358
}
3459

35-
vec3 getReflectanceFromBRDFLookup(const vec3 specularEnvironmentR0, const vec3 specularEnvironmentR90, const float ior, const vec3 environmentBrdf) {
60+
vec3 getReflectanceFromBRDFLookup(const vec3 specularEnvironmentR0, const vec3 specularEnvironmentR90, const vec3 environmentBrdf) {
3661
#ifdef BRDF_V_HEIGHT_CORRELATED
37-
#ifdef METALLICWORKFLOW
38-
// Scale the reflectance by the IOR for values less than 1.5
39-
vec3 reflectance = (specularEnvironmentR90 - specularEnvironmentR0) * clamp(environmentBrdf.x * 2.0 * (ior - 1.0), 0.0, 1.0) + specularEnvironmentR0 * environmentBrdf.y;
40-
#else
41-
vec3 reflectance = (specularEnvironmentR90 - specularEnvironmentR0) * environmentBrdf.x + specularEnvironmentR0 * environmentBrdf.y;
42-
#endif
62+
vec3 reflectance = (specularEnvironmentR90 - specularEnvironmentR0) * environmentBrdf.x + specularEnvironmentR0 * environmentBrdf.y;
4363
// Simplification if F90 = 1 vec3 reflectance = (specularEnvironmentR90 - specularEnvironmentR0) * environmentBrdf.xxx + specularEnvironmentR0 * environmentBrdf.yyy;
4464
#else
4565
vec3 reflectance = specularEnvironmentR0 * environmentBrdf.x + specularEnvironmentR90 * environmentBrdf.y;

packages/dev/core/src/Shaders/ShadersInclude/pbrBlockDirectLighting.fx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ vec3 diffuseBase = vec3(0., 0., 0.);
1212
vec3 sheenBase = vec3(0., 0., 0.);
1313
#endif
1414

15+
#if defined(SPECULARTERM) && defined(LIGHT0)
16+
vec3 fresnel;
17+
vec3 coloredFresnel;
18+
#endif
1519
// Direct Lighting Variables
1620
preLightingInfo preInfo;
1721
lightingInfo info;

0 commit comments

Comments
 (0)