diff --git a/packages/dev/core/src/Engines/constants.ts b/packages/dev/core/src/Engines/constants.ts index b782804e296..85a3c40155a 100644 --- a/packages/dev/core/src/Engines/constants.ts +++ b/packages/dev/core/src/Engines/constants.ts @@ -447,6 +447,21 @@ export class Constants { */ public static readonly MATERIAL_CounterClockWiseSideOrientation = 1; + /** + * Energy-conserving Oren Nayar diffuse model type. + */ + public static readonly MATERIAL_DIFFUSE_MODEL_E_OREN_NAYAR = 0; + + /** + * Burley diffuse model type. + */ + public static readonly MATERIAL_DIFFUSE_MODEL_BURLEY = 1; + + /** + * Lambertian diffuse model type. + */ + public static readonly MATERIAL_DIFFUSE_MODEL_LAMBERT = 2; + /** * Nothing * @see https://doc.babylonjs.com/features/featuresDeepDive/events/actions#triggers diff --git a/packages/dev/core/src/Materials/Node/Blocks/PBR/pbrMetallicRoughnessBlock.ts b/packages/dev/core/src/Materials/Node/Blocks/PBR/pbrMetallicRoughnessBlock.ts index 2f32f31bc91..aadcf9f182b 100644 --- a/packages/dev/core/src/Materials/Node/Blocks/PBR/pbrMetallicRoughnessBlock.ts +++ b/packages/dev/core/src/Materials/Node/Blocks/PBR/pbrMetallicRoughnessBlock.ts @@ -96,6 +96,7 @@ export class PBRMetallicRoughnessBlock extends NodeMaterialBlock { private _metallicReflectanceColor: Color3 = Color3.White(); private _metallicF0Factor = 1; private _vMetallicReflectanceFactorsName: string; + private _baseDiffuseRoughnessName: string; /** * Create a new ReflectionBlock @@ -270,6 +271,19 @@ export class PBRMetallicRoughnessBlock extends NodeMaterialBlock { }) public realTimeFilteringQuality = Constants.TEXTURE_FILTERING_QUALITY_LOW; + /** + * Base Diffuse Model + */ + @editableInPropertyPage("Diffuse Model", PropertyTypeForEdition.List, "RENDERING", { + notifiers: { update: true }, + options: [ + { label: "Lambert", value: Constants.MATERIAL_DIFFUSE_MODEL_LAMBERT }, + { label: "Burley", value: Constants.MATERIAL_DIFFUSE_MODEL_BURLEY }, + { label: "Oren-Nayar", value: Constants.MATERIAL_DIFFUSE_MODEL_E_OREN_NAYAR }, + ], + }) + public baseDiffuseModel = Constants.MATERIAL_DIFFUSE_MODEL_E_OREN_NAYAR; + /** * Defines if the material uses energy conservation. */ @@ -761,6 +775,8 @@ export class PBRMetallicRoughnessBlock extends NodeMaterialBlock { defines.setValue("NUM_SAMPLES", "" + this.realTimeFilteringQuality, true); } + defines.setValue("BASE_DIFFUSE_MODEL", this.baseDiffuseModel, true); + // Advanced defines.setValue("BRDF_V_HEIGHT_CORRELATED", true); defines.setValue("MS_BRDF_ENERGY_CONSERVATION", this.useEnergyConservation, true); @@ -1013,6 +1029,9 @@ export class PBRMetallicRoughnessBlock extends NodeMaterialBlock { this._vMetallicReflectanceFactorsName = state._getFreeVariableName("vMetallicReflectanceFactors"); state._emitUniformFromString(this._vMetallicReflectanceFactorsName, NodeMaterialBlockConnectionPointTypes.Vector4); + this._baseDiffuseRoughnessName = state._getFreeVariableName("baseDiffuseRoughness"); + state._emitUniformFromString(this._baseDiffuseRoughnessName, NodeMaterialBlockConnectionPointTypes.Float); + code += `${state._declareLocalVar("baseColor", NodeMaterialBlockConnectionPointTypes.Vector3)} = surfaceAlbedo; ${isWebGPU ? "let" : `vec4${state.fSuffix}`} vReflectivityColor = vec4${state.fSuffix}(${this.metallic.associatedVariableName}, ${this.roughness.associatedVariableName}, ${this.indexOfRefraction.associatedVariableName || "1.5"}, 1.0); reflectivityOut = reflectivityBlock( @@ -1020,6 +1039,11 @@ export class PBRMetallicRoughnessBlock extends NodeMaterialBlock { #ifdef METALLICWORKFLOW , surfaceAlbedo , ${(isWebGPU ? "uniforms." : "") + this._vMetallicReflectanceFactorsName} + #endif + , ${(isWebGPU ? "uniforms." : "") + this._baseDiffuseRoughnessName} + #ifdef BASE_DIFFUSE_ROUGHNESS + , 0. + , vec2${state.fSuffix}(0., 0.) #endif #ifdef REFLECTIVITY , vec3${state.fSuffix}(0., 0., ${aoIntensity}) @@ -1035,6 +1059,7 @@ export class PBRMetallicRoughnessBlock extends NodeMaterialBlock { ${state._declareLocalVar("microSurface", NodeMaterialBlockConnectionPointTypes.Float)} = reflectivityOut.microSurface; ${state._declareLocalVar("roughness", NodeMaterialBlockConnectionPointTypes.Float)} = reflectivityOut.roughness; + ${state._declareLocalVar("diffuseRoughness", NodeMaterialBlockConnectionPointTypes.Float)} = reflectivityOut.diffuseRoughness; #ifdef METALLICWORKFLOW surfaceAlbedo = reflectivityOut.surfaceAlbedo; diff --git a/packages/dev/core/src/Materials/Node/Blocks/PBR/reflectionBlock.ts b/packages/dev/core/src/Materials/Node/Blocks/PBR/reflectionBlock.ts index 3a79a31c990..b9056c78024 100644 --- a/packages/dev/core/src/Materials/Node/Blocks/PBR/reflectionBlock.ts +++ b/packages/dev/core/src/Materials/Node/Blocks/PBR/reflectionBlock.ts @@ -438,6 +438,9 @@ export class ReflectionBlock extends ReflectionTextureBaseBlock { #ifdef USEIRRADIANCEMAP , irradianceSampler // ** not handled ** ${isWebGPU ? `, irradianceSamplerSampler` : ""} + #ifdef USE_IRRADIANCE_DOMINANT_DIRECTION + , vReflectionDominantDirection + #endif #endif #ifndef LODBASEDMICROSFURACE #ifdef ${this._define3DName} @@ -459,6 +462,9 @@ export class ReflectionBlock extends ReflectionTextureBaseBlock { ${isWebGPU ? `, icdfSamplerSampler` : ""} #endif #endif + , viewDirectionW + , diffuseRoughness + , surfaceAlbedo ); #endif\n`; diff --git a/packages/dev/core/src/Materials/PBR/pbrBaseMaterial.ts b/packages/dev/core/src/Materials/PBR/pbrBaseMaterial.ts index a1ac46b3daa..91f9bd2872b 100644 --- a/packages/dev/core/src/Materials/PBR/pbrBaseMaterial.ts +++ b/packages/dev/core/src/Materials/PBR/pbrBaseMaterial.ts @@ -7,7 +7,7 @@ import { GetEnvironmentBRDFTexture } from "../../Misc/brdfTextureTools"; import type { Nullable } from "../../types"; import { Scene } from "../../scene"; import type { Matrix } from "../../Maths/math.vector"; -import { Vector4 } from "../../Maths/math.vector"; +import { Vector3, Vector4 } from "../../Maths/math.vector"; import { VertexBuffer } from "../../Buffers/buffer"; import type { SubMesh } from "../../Meshes/subMesh"; import type { AbstractMesh } from "../../Meshes/abstractMesh"; @@ -81,7 +81,7 @@ export class PBRMaterialDefines extends MaterialDefines implements IImageProcess public NUM_SAMPLES = "0"; public REALTIME_FILTERING = false; public IBL_CDF_FILTERING = false; - + public BASE_DIFFUSE_MODEL = 0; public MAINUV1 = false; public MAINUV2 = false; public MAINUV3 = false; @@ -100,8 +100,10 @@ export class PBRMaterialDefines extends MaterialDefines implements IImageProcess public ALBEDODIRECTUV = 0; public VERTEXCOLOR = false; - public BASEWEIGHT = false; - public BASEWEIGHTDIRECTUV = 0; + public BASE_WEIGHT = false; + public BASE_WEIGHTDIRECTUV = 0; + public BASE_DIFFUSE_ROUGHNESS = false; + public BASE_DIFFUSE_ROUGHNESSDIRECTUV = 0; public BAKED_VERTEX_ANIMATION_TEXTURE = false; @@ -186,6 +188,7 @@ export class PBRMaterialDefines extends MaterialDefines implements IImageProcess public INVERTCUBICMAP = false; public USESPHERICALFROMREFLECTIONMAP = false; public USEIRRADIANCEMAP = false; + public USE_IRRADIANCE_DOMINANT_DIRECTION = false; public USESPHERICALINVERTEX = false; public REFLECTIONMAP_OPPOSITEZ = false; public LODINREFLECTIONALPHA = false; @@ -356,6 +359,11 @@ export abstract class PBRBaseMaterial extends PushMaterial { */ public static DEFAULT_AO_ON_ANALYTICAL_LIGHTS = 0; + /** + * Defines the default diffuse model used by the material. + */ + public static DEFAULT_DIFFUSE_MODEL = Constants.MATERIAL_DIFFUSE_MODEL_E_OREN_NAYAR; + /** * PBRMaterialLightFalloff Physical: light is falling off following the inverse squared distance law. */ @@ -425,11 +433,17 @@ export abstract class PBRBaseMaterial extends PushMaterial { public _albedoTexture: Nullable = null; /** - * OpenPBR Base Weight (multiplier to the diffuse and metal lobes). + * Base Weight texture (multiplier to the diffuse and metal lobes). * @internal */ public _baseWeightTexture: Nullable = null; + /** + * Base Diffuse Roughness texture (roughness of the diffuse lobe). + * @internal + */ + public _baseDiffuseRoughnessTexture: Nullable = null; + /** * AKA Occlusion Texture in other nomenclature. * @internal @@ -573,11 +587,18 @@ export abstract class PBRBaseMaterial extends PushMaterial { public _albedoColor = new Color3(1, 1, 1); /** - * OpenPBR Base Weight (multiplier to the diffuse and metal lobes). + * Base Weight (multiplier to the diffuse and metal lobes). * @internal */ public _baseWeight = 1; + /** + * Base Diffuse Roughness (roughness of the diffuse lobe). + * Can also be used to scale the corresponding texture. + * @internal + */ + public _baseDiffuseRoughness: Nullable = null; + /** * AKA Specular Color in other nomenclature. * @internal @@ -804,6 +825,8 @@ export abstract class PBRBaseMaterial extends PushMaterial { this.markAsDirty(Constants.MATERIAL_TextureDirtyFlag); } + private _baseDiffuseModel: number = PBRBaseMaterial.DEFAULT_DIFFUSE_MODEL; + /** * Can this material render to several textures at once */ @@ -1134,6 +1157,12 @@ export abstract class PBRBaseMaterial extends PushMaterial { } } + if (this._baseDiffuseRoughnessTexture && MaterialFlags.BaseDiffuseRoughnessTextureEnabled) { + if (!this._baseDiffuseRoughnessTexture.isReadyOrNotBlocking()) { + return false; + } + } + if (this._ambientTexture && MaterialFlags.AmbientTextureEnabled) { if (!this._ambientTexture.isReadyOrNotBlocking()) { return false; @@ -1446,6 +1475,7 @@ export abstract class PBRBaseMaterial extends PushMaterial { "vAmbientColor", "vAlbedoColor", "baseWeight", + "baseDiffuseRoughness", "vReflectivityColor", "vMetallicReflectanceFactors", "vEmissiveColor", @@ -1456,6 +1486,7 @@ export abstract class PBRBaseMaterial extends PushMaterial { "pointSize", "vAlbedoInfos", "vBaseWeightInfos", + "vBaseDiffuseRoughnessInfos", "vAmbientInfos", "vOpacityInfos", "vReflectionInfos", @@ -1472,6 +1503,7 @@ export abstract class PBRBaseMaterial extends PushMaterial { "mBones", "albedoMatrix", "baseWeightMatrix", + "baseDiffuseRoughnessMatrix", "ambientMatrix", "opacityMatrix", "reflectionMatrix", @@ -1504,6 +1536,7 @@ export abstract class PBRBaseMaterial extends PushMaterial { "vSphericalL21", "vSphericalL22", "vReflectionMicrosurfaceInfos", + "vReflectionDominantDirection", "vTangentSpaceParams", "boneTextureWidth", "vDebugMode", @@ -1514,6 +1547,7 @@ export abstract class PBRBaseMaterial extends PushMaterial { const samplers = [ "albedoSampler", "baseWeightSampler", + "baseDiffuseRoughnessSampler", "reflectivitySampler", "ambientSampler", "emissiveSampler", @@ -1628,6 +1662,7 @@ export abstract class PBRBaseMaterial extends PushMaterial { // Lights PrepareDefinesForLights(scene, mesh, defines, true, this._maxSimultaneousLights, this._disableLighting); defines._needNormals = true; + defines.BASE_DIFFUSE_MODEL = this._baseDiffuseModel; // Multiview PrepareDefinesForMultiview(scene, defines); @@ -1650,7 +1685,8 @@ export abstract class PBRBaseMaterial extends PushMaterial { } if (scene.texturesEnabled) { defines.ALBEDODIRECTUV = 0; - defines.BASEWEIGHTDIRECTUV = 0; + defines.BASE_WEIGHTDIRECTUV = 0; + defines.BASE_DIFFUSE_ROUGHNESSDIRECTUV = 0; defines.AMBIENTDIRECTUV = 0; defines.OPACITYDIRECTUV = 0; defines.EMISSIVEDIRECTUV = 0; @@ -1673,9 +1709,15 @@ export abstract class PBRBaseMaterial extends PushMaterial { } if (this._baseWeightTexture && MaterialFlags.BaseWeightTextureEnabled) { - PrepareDefinesForMergedUV(this._baseWeightTexture, defines, "BASEWEIGHT"); + PrepareDefinesForMergedUV(this._baseWeightTexture, defines, "BASE_WEIGHT"); } else { - defines.BASEWEIGHT = false; + defines.BASE_WEIGHT = false; + } + + if (this._baseDiffuseRoughnessTexture && MaterialFlags.BaseDiffuseRoughnessTextureEnabled) { + PrepareDefinesForMergedUV(this._baseDiffuseRoughnessTexture, defines, "BASE_DIFFUSE_ROUGHNESS"); + } else { + defines.BASE_DIFFUSE_ROUGHNESS = false; } if (this._ambientTexture && MaterialFlags.AmbientTextureEnabled) { @@ -1767,12 +1809,22 @@ export abstract class PBRBaseMaterial extends PushMaterial { defines.USEIRRADIANCEMAP = true; defines.USESPHERICALFROMREFLECTIONMAP = false; defines.USESPHERICALINVERTEX = false; + if (reflectionTexture.irradianceTexture._dominantDirection) { + defines.USE_IRRADIANCE_DOMINANT_DIRECTION = true; + } } // Assume using spherical polynomial if the reflection texture is a cube map else if (reflectionTexture.isCube) { defines.USESPHERICALFROMREFLECTIONMAP = true; defines.USEIRRADIANCEMAP = false; - if (this._forceIrradianceInFragment || this.realTimeFiltering || this._twoSidedLighting || engine.getCaps().maxVaryingVectors <= 8) { + defines.USE_IRRADIANCE_DOMINANT_DIRECTION = false; + if ( + this._forceIrradianceInFragment || + this.realTimeFiltering || + this._twoSidedLighting || + engine.getCaps().maxVaryingVectors <= 8 || + this._baseDiffuseRoughnessTexture + ) { defines.USESPHERICALINVERTEX = false; } else { defines.USESPHERICALINVERTEX = true; @@ -1795,6 +1847,7 @@ export abstract class PBRBaseMaterial extends PushMaterial { defines.INVERTCUBICMAP = false; defines.USESPHERICALFROMREFLECTIONMAP = false; defines.USEIRRADIANCEMAP = false; + defines.USE_IRRADIANCE_DOMINANT_DIRECTION = false; defines.USESPHERICALINVERTEX = false; defines.REFLECTIONMAP_OPPOSITEZ = false; defines.LODINREFLECTIONALPHA = false; @@ -2031,6 +2084,7 @@ export abstract class PBRBaseMaterial extends PushMaterial { const ubo = this._uniformBuffer; ubo.addUniform("vAlbedoInfos", 2); ubo.addUniform("vBaseWeightInfos", 2); + ubo.addUniform("vBaseDiffuseRoughnessInfos", 2); ubo.addUniform("vAmbientInfos", 4); ubo.addUniform("vOpacityInfos", 2); ubo.addUniform("vEmissiveInfos", 2); @@ -2044,6 +2098,7 @@ export abstract class PBRBaseMaterial extends PushMaterial { ubo.addUniform("vBumpInfos", 3); ubo.addUniform("albedoMatrix", 16); ubo.addUniform("baseWeightMatrix", 16); + ubo.addUniform("baseDiffuseRoughnessMatrix", 16); ubo.addUniform("ambientMatrix", 16); ubo.addUniform("opacityMatrix", 16); ubo.addUniform("emissiveMatrix", 16); @@ -2057,9 +2112,11 @@ export abstract class PBRBaseMaterial extends PushMaterial { ubo.addUniform("vReflectionColor", 3); ubo.addUniform("vAlbedoColor", 4); ubo.addUniform("baseWeight", 1); + ubo.addUniform("baseDiffuseRoughness", 1); ubo.addUniform("vLightingIntensity", 4); ubo.addUniform("vReflectionMicrosurfaceInfos", 3); + ubo.addUniform("vReflectionDominantDirection", 3); ubo.addUniform("pointSize", 1); ubo.addUniform("vReflectivityColor", 4); ubo.addUniform("vEmissiveColor", 3); @@ -2164,6 +2221,11 @@ export abstract class PBRBaseMaterial extends PushMaterial { BindTextureMatrix(this._baseWeightTexture, ubo, "baseWeight"); } + if (this._baseDiffuseRoughnessTexture && MaterialFlags.BaseDiffuseRoughnessTextureEnabled) { + ubo.updateFloat2("vBaseDiffuseRoughnessInfos", this._baseDiffuseRoughnessTexture.coordinatesIndex, this._baseDiffuseRoughnessTexture.level); + BindTextureMatrix(this._baseDiffuseRoughnessTexture, ubo, "baseDiffuseRoughness"); + } + if (this._ambientTexture && MaterialFlags.AmbientTextureEnabled) { ubo.updateFloat4( "vAmbientInfos", @@ -2232,6 +2294,18 @@ export abstract class PBRBaseMaterial extends PushMaterial { ubo.updateFloat3("vSphericalZX", polynomials.zx.x, polynomials.zx.y, polynomials.zx.z); } } + } else { + // If we're using an irradiance map with a dominant direction assigned, set it. + if (defines.USEIRRADIANCEMAP && reflectionTexture.irradianceTexture) { + const irradianceTexture = reflectionTexture.irradianceTexture; + if (irradianceTexture._dominantDirection) { + ubo.updateVector3("vReflectionDominantDirection", irradianceTexture._dominantDirection); + } else { + ubo.updateVector3("vReflectionDominantDirection", Vector3.Up()); + } + } else { + ubo.updateVector3("vReflectionDominantDirection", Vector3.Up()); + } } ubo.updateFloat3( @@ -2325,6 +2399,7 @@ export abstract class PBRBaseMaterial extends PushMaterial { } ubo.updateFloat("baseWeight", this._baseWeight); + ubo.updateFloat("baseDiffuseRoughness", this._baseDiffuseRoughness || 0.0); // Misc this._lightingInfos.x = this._directIntensity; @@ -2352,6 +2427,10 @@ export abstract class PBRBaseMaterial extends PushMaterial { ubo.setTexture("baseWeightSampler", this._baseWeightTexture); } + if (this._baseDiffuseRoughnessTexture && MaterialFlags.BaseDiffuseRoughnessTextureEnabled) { + ubo.setTexture("baseDiffuseRoughnessSampler", this._baseDiffuseRoughnessTexture); + } + if (this._ambientTexture && MaterialFlags.AmbientTextureEnabled) { ubo.setTexture("ambientSampler", this._ambientTexture); } @@ -2490,6 +2569,10 @@ export abstract class PBRBaseMaterial extends PushMaterial { results.push(this._baseWeightTexture); } + if (this._baseDiffuseRoughnessTexture && this._baseDiffuseRoughnessTexture.animations && this._baseDiffuseRoughnessTexture.animations.length > 0) { + results.push(this._baseDiffuseRoughnessTexture); + } + if (this._ambientTexture && this._ambientTexture.animations && this._ambientTexture.animations.length > 0) { results.push(this._ambientTexture); } @@ -2562,6 +2645,10 @@ export abstract class PBRBaseMaterial extends PushMaterial { activeTextures.push(this._baseWeightTexture); } + if (this._baseDiffuseRoughnessTexture) { + activeTextures.push(this._baseDiffuseRoughnessTexture); + } + if (this._ambientTexture) { activeTextures.push(this._ambientTexture); } @@ -2627,6 +2714,10 @@ export abstract class PBRBaseMaterial extends PushMaterial { return true; } + if (this._baseDiffuseRoughnessTexture === texture) { + return true; + } + if (this._ambientTexture === texture) { return true; } @@ -2707,6 +2798,7 @@ export abstract class PBRBaseMaterial extends PushMaterial { this._albedoTexture?.dispose(); this._baseWeightTexture?.dispose(); + this._baseDiffuseRoughnessTexture?.dispose(); this._ambientTexture?.dispose(); this._opacityTexture?.dispose(); this._reflectionTexture?.dispose(); diff --git a/packages/dev/core/src/Materials/PBR/pbrMaterial.ts b/packages/dev/core/src/Materials/PBR/pbrMaterial.ts index df311aa1d02..e381dba2b48 100644 --- a/packages/dev/core/src/Materials/PBR/pbrMaterial.ts +++ b/packages/dev/core/src/Materials/PBR/pbrMaterial.ts @@ -3,6 +3,7 @@ import { GetEnvironmentBRDFTexture } from "../../Misc/brdfTextureTools"; import type { Nullable } from "../../types"; import type { Scene } from "../../scene"; import { Color3 } from "../../Maths/math.color"; +import { Constants } from "../../Engines/constants"; import type { ImageProcessingConfiguration } from "../../Materials/imageProcessingConfiguration"; import type { ColorCurves } from "../../Materials/colorCurves"; import type { BaseTexture } from "../../Materials/Textures/baseTexture"; @@ -46,6 +47,11 @@ export class PBRMaterial extends PBRBaseMaterial { */ public static override DEFAULT_AO_ON_ANALYTICAL_LIGHTS = PBRBaseMaterial.DEFAULT_AO_ON_ANALYTICAL_LIGHTS; + /** + * Defines the default diffuse model used by the material. + */ + public static override DEFAULT_DIFFUSE_MODEL = Constants.MATERIAL_DIFFUSE_MODEL_E_OREN_NAYAR; + /** * Intensity of the direct lights e.g. the four lights available in your scene. * This impacts both the direct diffuse and specular highlights. @@ -93,12 +99,19 @@ export class PBRMaterial extends PBRBaseMaterial { public albedoTexture: Nullable; /** - * OpenPBR Base Weight (multiplier to the diffuse and metal lobes). + * OpenPBR Base Weight texture (multiplier to the diffuse and metal lobes). */ @serializeAsTexture() @expandToProperty("_markAllSubMeshesAsTexturesDirty") public baseWeightTexture: Nullable; + /** + * OpenPBR Base Diffuse Roughness texture (roughness of the diffuse lobe). + */ + @serializeAsTexture() + @expandToProperty("_markAllSubMeshesAsTexturesDirty") + public baseDiffuseRoughnessTexture: Nullable; + /** * AKA Occlusion Texture in other nomenclature. */ @@ -284,6 +297,20 @@ export class PBRMaterial extends PBRBaseMaterial { @expandToProperty("_markAllSubMeshesAsTexturesDirty") public baseWeight = 1; + /** + * OpenPBR Base Diffuse Roughness (roughness of the diffuse lobe). + */ + @serialize("baseDiffuseRoughness") + @expandToProperty("_markAllSubMeshesAsTexturesDirty") + public baseDiffuseRoughness: Nullable; + + /** + * Defines the base diffuse roughness model of the material. + */ + @serialize("baseDiffuseModel") + @expandToProperty("_markAllSubMeshesAsMiscDirty") + public baseDiffuseModel: number = PBRMaterial.DEFAULT_DIFFUSE_MODEL; + /** * AKA Specular Color in other nomenclature. */ diff --git a/packages/dev/core/src/Materials/Textures/Filtering/hdrIrradianceFiltering.ts b/packages/dev/core/src/Materials/Textures/Filtering/hdrIrradianceFiltering.ts index a774a587189..5a51c91df5d 100644 --- a/packages/dev/core/src/Materials/Textures/Filtering/hdrIrradianceFiltering.ts +++ b/packages/dev/core/src/Materials/Textures/Filtering/hdrIrradianceFiltering.ts @@ -217,6 +217,12 @@ export class HDRIrradianceFiltering { await this._effectWrapper.effect.whenCompiledAsync(); const irradianceTexture = this._prefilterInternal(texture); + + if (this.useCdf) { + await this._cdfGenerator.findDominantDirection().then((dir) => { + irradianceTexture._dominantDirection = dir; + }); + } this._effectRenderer.dispose(); this._effectWrapper.dispose(); this._cdfGenerator?.dispose(); diff --git a/packages/dev/core/src/Materials/Textures/baseTexture.ts b/packages/dev/core/src/Materials/Textures/baseTexture.ts index c24d5f466bc..61397e60000 100644 --- a/packages/dev/core/src/Materials/Textures/baseTexture.ts +++ b/packages/dev/core/src/Materials/Textures/baseTexture.ts @@ -4,6 +4,7 @@ import { Observable } from "../../Misc/observable"; import type { Nullable } from "../../types"; import type { Scene } from "../../scene"; import { Matrix } from "../../Maths/math.vector"; +import type { Vector3 } from "../../Maths/math.vector"; import { EngineStore } from "../../Engines/engineStore"; import type { InternalTexture } from "../../Materials/Textures/internalTexture"; import { Constants } from "../../Engines/constants"; @@ -436,6 +437,14 @@ export class BaseTexture extends ThinTexture implements IAnimatable { } } + /** + * Indicates the average direction of light in an environment map. This + * can be treated as the most dominant direction but it's magnitude also + * tells you something about how dominant that direction is. + */ + /** @internal */ + public _dominantDirection: Nullable = null; + /** * Define if the texture is a render target. */ diff --git a/packages/dev/core/src/Materials/materialFlags.ts b/packages/dev/core/src/Materials/materialFlags.ts index af013512b86..8f9dde396b6 100644 --- a/packages/dev/core/src/Materials/materialFlags.ts +++ b/packages/dev/core/src/Materials/materialFlags.ts @@ -38,6 +38,22 @@ export class MaterialFlags { AbstractEngine.MarkAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag); } + private static _BaseDiffuseRoughnessTextureEnabled = true; + /** + * Is the OpenPBR Base Diffuse Roughness texture enabled in the application. + */ + public static get BaseDiffuseRoughnessTextureEnabled(): boolean { + return this._BaseDiffuseRoughnessTextureEnabled; + } + public static set BaseDiffuseRoughnessTextureEnabled(value: boolean) { + if (this._BaseDiffuseRoughnessTextureEnabled === value) { + return; + } + + this._BaseDiffuseRoughnessTextureEnabled = value; + AbstractEngine.MarkAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag); + } + private static _DetailTextureEnabled = true; /** * Are detail textures enabled in the application. diff --git a/packages/dev/core/src/Rendering/IBLShadows/iblShadowsAccumulationPass.ts b/packages/dev/core/src/Rendering/IBLShadows/iblShadowsAccumulationPass.ts index bb9d975c242..0b2978ed3f9 100644 --- a/packages/dev/core/src/Rendering/IBLShadows/iblShadowsAccumulationPass.ts +++ b/packages/dev/core/src/Rendering/IBLShadows/iblShadowsAccumulationPass.ts @@ -198,7 +198,7 @@ export class _IblShadowsAccumulationPass { this._scene, outputTextureOptions ); - this._outputTexture.refreshRate = -1; + this._outputTexture.refreshRate = 1; this._outputTexture.autoClear = false; this._outputTexture.onGeneratedObservable.addOnce(() => { this.onReadyObservable.notifyObservers(); diff --git a/packages/dev/core/src/Rendering/iblCdfGenerator.ts b/packages/dev/core/src/Rendering/iblCdfGenerator.ts index 454d2daefad..74747ed767a 100644 --- a/packages/dev/core/src/Rendering/iblCdfGenerator.ts +++ b/packages/dev/core/src/Rendering/iblCdfGenerator.ts @@ -8,7 +8,7 @@ import { ProceduralTexture } from "../Materials/Textures/Procedurals/proceduralT import type { IProceduralTextureCreationOptions } from "../Materials/Textures/Procedurals/proceduralTexture"; import { PostProcess } from "../PostProcesses/postProcess"; import type { PostProcessOptions } from "../PostProcesses/postProcess"; -import { Vector4 } from "../Maths/math.vector"; +import { Vector3, Vector4 } from "../Maths/math.vector"; import { RawTexture } from "../Materials/Textures/rawTexture"; import type { BaseTexture } from "../Materials/Textures/baseTexture"; import { Observable } from "../Misc/observable"; @@ -18,6 +18,7 @@ import { Engine } from "../Engines/engine"; import { _WarnImport } from "../Misc/devTools"; import type { Nullable } from "../types"; import { EngineStore } from "../Engines/engineStore"; +import { Logger } from "../Misc/logger"; /** * Build cdf maps to be used for IBL importance sampling. @@ -30,8 +31,23 @@ export class IblCdfGenerator { private _cdfxPT: ProceduralTexture; private _icdfPT: ProceduralTexture; private _scaledLuminancePT: ProceduralTexture; + private _dominantDirectionPT: ProceduralTexture; private _iblSource: Nullable; private _dummyTexture: RawTexture; + + private _cachedDominantDirection: Nullable = null; + + /** + * Returns whether the CDF renderer is supported by the current engine + */ + public get isSupported(): boolean { + const engine = EngineStore.LastCreatedEngine; + if (!engine) { + return false; + } + return engine.getCaps().texelFetch; + } + /** * Gets the IBL source texture being used by the CDF renderer */ @@ -148,6 +164,10 @@ export class IblCdfGenerator { if (this._scene) { this._engine = this._scene.getEngine(); } + if (!this.isSupported) { + Logger.Warn("CDF renderer is not supported by the current engine."); + return; + } const blackPixels = new Uint16Array([0, 0, 0, 255]); this._dummyTexture = new RawTexture(blackPixels, 1, 1, Engine.TEXTUREFORMAT_RGBA, sceneOrEngine, false, false, undefined, Constants.TEXTURETYPE_HALF_FLOAT); if (this._scene) { @@ -214,9 +234,9 @@ export class IblCdfGenerator { gammaSpace: false, extraInitializationsAsync: async () => { if (isWebGPU) { - await Promise.all([import("../ShadersWGSL/iblIcdf.fragment")]); + await Promise.all([import("../ShadersWGSL/iblIcdf.fragment"), import("../ShadersWGSL/iblDominantDirection.fragment")]); } else { - await Promise.all([import("../Shaders/iblIcdf.fragment")]); + await Promise.all([import("../Shaders/iblIcdf.fragment"), import("../Shaders/iblDominantDirection.fragment")]); } }, }; @@ -269,6 +289,12 @@ export class IblCdfGenerator { this._icdfPT.onGeneratedObservable.addOnce(() => { this.onGeneratedObservable.notifyObservers(); }); + + this._dominantDirectionPT = new ProceduralTexture("iblDominantDirection", { width: 1, height: 1 }, "iblDominantDirection", this._scene, icdfOptions, false, false); + this._dominantDirectionPT.autoClear = false; + this._dominantDirectionPT.setTexture("icdfSampler", this._icdfPT); + this._dominantDirectionPT.refreshRate = 0; + this._dominantDirectionPT.defines = "#define NUM_SAMPLES 32u\n"; } private _disposeTextures() { @@ -276,6 +302,7 @@ export class IblCdfGenerator { this._cdfxPT?.dispose(); this._icdfPT?.dispose(); this._scaledLuminancePT?.dispose(); + this._dominantDirectionPT?.dispose(); } private _createDebugPass() { @@ -343,6 +370,7 @@ export class IblCdfGenerator { * @returns Promise that resolves when the CDF maps are rendered. */ public renderWhenReady(): Promise { + this._cachedDominantDirection = null; // Once the textures are generated, notify that they are ready to use. this._icdfPT.onGeneratedObservable.addOnce(() => { this.onGeneratedObservable.notifyObservers(); @@ -369,6 +397,45 @@ export class IblCdfGenerator { }); } + /** + * Finds the average direction of the highest intensity areas of the IBL source + * @returns Async promise that resolves to the dominant direction of the IBL source + */ + public findDominantDirection(): Promise { + if (this._cachedDominantDirection) { + return Promise.resolve(this._cachedDominantDirection); + } + return new Promise((resolve) => { + this._dominantDirectionPT.onGeneratedObservable.addOnce(() => { + const data = new Float32Array(4); + this._dominantDirectionPT.readPixels(0, 0, data, true)!.then(() => { + const dominantDirection = new Vector3(data[0], data[1], data[2]); + this._cachedDominantDirection = dominantDirection; + resolve(dominantDirection); + }); + }); + if (this.isReady()) { + if (this._dominantDirectionPT.isReady()) { + this._dominantDirectionPT.render(); + } else { + this._dominantDirectionPT.getEffect().executeWhenCompiled(() => { + this._dominantDirectionPT.render(); + }); + } + } else { + this.onGeneratedObservable.addOnce(() => { + if (this._dominantDirectionPT.isReady()) { + this._dominantDirectionPT.render(); + } else { + this._dominantDirectionPT.getEffect().executeWhenCompiled(() => { + this._dominantDirectionPT.render(); + }); + } + }); + } + }); + } + /** * Disposes the CDF renderer and associated resources */ diff --git a/packages/dev/core/src/Rendering/iblCdfGeneratorSceneComponent.ts b/packages/dev/core/src/Rendering/iblCdfGeneratorSceneComponent.ts index 6861f98dbd4..bfd25053f67 100644 --- a/packages/dev/core/src/Rendering/iblCdfGeneratorSceneComponent.ts +++ b/packages/dev/core/src/Rendering/iblCdfGeneratorSceneComponent.ts @@ -51,6 +51,10 @@ Scene.prototype.enableIblCdfGenerator = function (): Nullable { } this._iblCdfGenerator = new IblCdfGenerator(this); + if (!this._iblCdfGenerator.isSupported) { + this._iblCdfGenerator = null; + return null; + } if (this.environmentTexture) { this._iblCdfGenerator.iblSource = this.environmentTexture; } diff --git a/packages/dev/core/src/Rendering/index.ts b/packages/dev/core/src/Rendering/index.ts index 01ac0285226..f00e93f6f88 100644 --- a/packages/dev/core/src/Rendering/index.ts +++ b/packages/dev/core/src/Rendering/index.ts @@ -81,6 +81,8 @@ export * from "../ShadersWGSL/iblCdfDebug.fragment"; export * from "../Shaders/iblCdfDebug.fragment"; export * from "../ShadersWGSL/iblScaledLuminance.fragment"; export * from "../Shaders/iblScaledLuminance.fragment"; +export * from "../ShadersWGSL/iblDominantDirection.fragment"; +export * from "../Shaders/iblDominantDirection.fragment"; export * from "../Shaders/iblVoxelGrid2dArrayDebug.fragment"; export * from "../ShadersWGSL/iblVoxelGrid2dArrayDebug.fragment"; export * from "../Shaders/iblVoxelGrid.fragment"; diff --git a/packages/dev/core/src/Shaders/ShadersInclude/hdrFilteringFunctions.fx b/packages/dev/core/src/Shaders/ShadersInclude/hdrFilteringFunctions.fx index d803f164f8e..d788a4d0acd 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/hdrFilteringFunctions.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/hdrFilteringFunctions.fx @@ -184,7 +184,7 @@ // #define inline - vec3 irradiance(samplerCube inputTexture, vec3 inputN, vec2 filteringInfo + vec3 irradiance(samplerCube inputTexture, vec3 inputN, vec2 filteringInfo, float diffuseRoughness, vec3 surfaceAlbedo, vec3 inputV #ifdef IBL_CDF_FILTERING , sampler2D icdfSampler #endif @@ -198,12 +198,15 @@ tangent = normalize(cross(tangent, n)); vec3 bitangent = cross(n, tangent); mat3 tbn = mat3(tangent, bitangent, n); + // The inverse is just the transpose of the TBN matrix. However, WebGL 1.0 doesn't support mat3 transpose. + // So, we have to calculate it manually. + mat3 tbnInverse = mat3(tangent.x, bitangent.x, n.x, tangent.y, bitangent.y, n.y, tangent.z, bitangent.z, n.z); #endif float maxLevel = filteringInfo.y; float dim0 = filteringInfo.x; float omegaP = (4. * PI) / (6. * dim0 * dim0); - + vec3 clampedAlbedo = clamp(surfaceAlbedo, vec3(0.1), vec3(1.0)); #if defined(WEBGL2) || defined(WEBGPU) || defined(NATIVE) for(uint i = 0u; i < NUM_SAMPLES; ++i) #else @@ -214,20 +217,36 @@ #ifdef IBL_CDF_FILTERING vec2 T; - T.x = textureLod(icdfSampler, vec2(Xi.x, 0.0), 0.0).x; - T.y = textureLod(icdfSampler, vec2(T.x, Xi.y), 0.0).y; + T.x = texture2D(icdfSampler, vec2(Xi.x, 0.0)).x; + T.y = texture2D(icdfSampler, vec2(T.x, Xi.y)).y; vec3 Ls = uv_to_normal(vec2(1.0 - fract(T.x + 0.25), T.y)); float NoL = dot(n, Ls); + float NoV = dot(n, inputV); + #if BASE_DIFFUSE_MODEL == BRDF_DIFFUSE_MODEL_EON + float LoV = dot (Ls, inputV); + #elif BASE_DIFFUSE_MODEL == BRDF_DIFFUSE_MODEL_BURLEY + vec3 H = (inputV + Ls) * 0.5; + float VoH = dot(inputV, H); + #endif #else vec3 Ls = hemisphereCosSample(Xi); Ls = normalize(Ls); vec3 Ns = vec3(0., 0., 1.); float NoL = dot(Ns, Ls); + vec3 V = tbnInverse * inputV; + float NoV = dot(Ns, V); + #if BASE_DIFFUSE_MODEL == BRDF_DIFFUSE_MODEL_EON + float LoV = dot (Ls, V); + #elif BASE_DIFFUSE_MODEL == BRDF_DIFFUSE_MODEL_BURLEY + vec3 H = (V + Ls) * 0.5; + float VoH = dot(V, H); + #endif + #endif if (NoL > 0.) { #ifdef IBL_CDF_FILTERING - float pdf = textureLod(icdfSampler, T, 0.0).z; + float pdf = texture2D(icdfSampler, T).z; vec3 c = textureCubeLodEXT(inputTexture, Ls, 0.0).rgb; #else float pdf_inversed = PI / NoL; @@ -240,17 +259,28 @@ c = toLinearSpace(c); #endif + vec3 diffuseRoughnessTerm = vec3(1.0); + #if BASE_DIFFUSE_MODEL == BRDF_DIFFUSE_MODEL_EON + diffuseRoughnessTerm = diffuseBRDF_EON(clampedAlbedo, diffuseRoughness, NoL, NoV, LoV) * PI; + #elif BASE_DIFFUSE_MODEL == BRDF_DIFFUSE_MODEL_BURLEY + diffuseRoughnessTerm = vec3(diffuseBRDF_Burley(NoL, NoV, VoH, diffuseRoughness) * PI); + #endif + #ifdef IBL_CDF_FILTERING vec3 light = pdf < 1e-6 ? vec3(0.0) : vec3(1.0) / vec3(pdf) * c; - result += NoL * light; + result += NoL * diffuseRoughnessTerm * light; #else - result += c; + result += c * diffuseRoughnessTerm; #endif } } result = result * NUM_SAMPLES_FLOAT_INVERSED; + #if BASE_DIFFUSE_MODEL == BRDF_DIFFUSE_MODEL_EON + result = result / clampedAlbedo; + #endif + return result; } diff --git a/packages/dev/core/src/Shaders/ShadersInclude/lightFragment.fx b/packages/dev/core/src/Shaders/ShadersInclude/lightFragment.fx index 8a36a7474b3..181fb06ff4e 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/lightFragment.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/lightFragment.fx @@ -51,7 +51,7 @@ preInfo.attenuation *= computeDirectionalLightFalloff_IES(light{X}.vLightDirection.xyz, preInfo.L, iesLightTexture{X}); #else preInfo.attenuation *= computeDirectionalLightFalloff(light{X}.vLightDirection.xyz, preInfo.L, light{X}.vLightDirection.w, light{X}.vLightData.w, light{X}.vLightFalloff.z, light{X}.vLightFalloff.w); - #endif + #endif #endif #elif defined(POINTLIGHT{X}) #ifdef LIGHT_FALLOFF_GLTF{X} @@ -74,6 +74,8 @@ #else preInfo.roughness = adjustRoughnessFromLightProperties(roughness, light{X}.vLightSpecular.a, preInfo.lightDistance); #endif + preInfo.diffuseRoughness = diffuseRoughness; + preInfo.surfaceAlbedo = surfaceAlbedo; #ifdef IRIDESCENCE preInfo.iridescenceIntensity = iridescenceIntensity; @@ -135,7 +137,7 @@ #endif info.clearCoat = computeClearCoatLighting(preInfo, clearcoatOut.clearCoatNormalW, clearcoatOut.clearCoatAARoughnessFactors.x, clearcoatOut.clearCoatIntensity, diffuse{X}.rgb); - + #ifdef CLEARCOAT_TINT // Absorption absorption = computeClearCoatLightingAbsorption(clearcoatOut.clearCoatNdotVRefract, preInfo.L, clearcoatOut.clearCoatNormalW, clearcoatOut.clearCoatColor, clearcoatOut.clearCoatThickness, clearcoatOut.clearCoatIntensity); @@ -190,7 +192,7 @@ #ifdef SHADOW{X} #ifdef SHADOWCSM{X} - for (int i = 0; i < SHADOWCSMNUM_CASCADES{X}; i++) + for (int i = 0; i < SHADOWCSMNUM_CASCADES{X}; i++) { #ifdef SHADOWCSM_RIGHTHANDED{X} diff{X} = viewFrustumZ{X}[i] + vPositionFromCamera{X}.z; @@ -347,7 +349,7 @@ #else #ifdef SHADOWCSMDEBUG{X} diffuseBase += info.diffuse * shadowDebug{X}; - #else + #else diffuseBase += info.diffuse * shadow; #endif #ifdef SS_TRANSLUCENCY diff --git a/packages/dev/core/src/Shaders/ShadersInclude/pbrBRDFFunctions.fx b/packages/dev/core/src/Shaders/ShadersInclude/pbrBRDFFunctions.fx index 3c5653c559c..91a6cbcf54c 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/pbrBRDFFunctions.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/pbrBRDFFunctions.fx @@ -1,5 +1,8 @@ // Constants #define FRESNEL_MAXIMUM_ON_ROUGH 0.25 +#define BRDF_DIFFUSE_MODEL_EON 0 +#define BRDF_DIFFUSE_MODEL_BURLEY 1 +#define BRDF_DIFFUSE_MODEL_LAMBERT 2 // ______________________________________________________________________ // @@ -429,6 +432,42 @@ float diffuseBRDF_Burley(float NdotL, float NdotV, float VdotH, float roughness) return fresnel / PI; } +const float constant1_FON = 0.5 - 2.0 / (3.0 * PI); +const float constant2_FON = 2.0 / 3.0 - 28.0 / (15.0 * PI); + +// Fujii Oren-Nayar (FON) directional albedo (approximate). +float E_FON_approx(float mu, float roughness) +{ + float sigma = roughness; // FON sigma prime + float mucomp = 1.0 - mu; + float mucomp2 = mucomp * mucomp; + const mat2 Gcoeffs = mat2(0.0571085289, -0.332181442, + 0.491881867, 0.0714429953); + float GoverPi = dot(Gcoeffs * vec2(mucomp, mucomp2), vec2(1.0, mucomp2)); + return (1.0 + sigma * GoverPi) / (1.0 + constant1_FON * sigma); +} + +vec3 diffuseBRDF_EON(vec3 albedo, float roughness, float NdotL, float NdotV, float LdotV) +{ + vec3 rho = albedo; + float sigma = roughness; // FON sigma prime + float mu_i = NdotL; // input angle cos + float mu_o = NdotV; // output angle cos + float s = LdotV - mu_i * mu_o; // QON s term + float sovertF = s > 0.0 ? s / max(mu_i, mu_o) : s; // FON s/t + float AF = 1.0 / (1.0 + constant1_FON * sigma); // FON A coeff. + vec3 f_ss = (rho * RECIPROCAL_PI) * AF * (1.0 + sigma * sovertF); // single-scatter + float EFo = E_FON_approx(mu_o, sigma); // FON wo albedo (approx) + float EFi = E_FON_approx(mu_i, sigma); // FON wi albedo (approx) + float avgEF = AF * (1.0 + constant2_FON * sigma); // avg. albedo + vec3 rho_ms = (rho * rho) * avgEF / (vec3(1.0) - rho * (1.0 - avgEF)); + const float eps = 1.0e-7; + vec3 f_ms = (rho_ms * RECIPROCAL_PI) * max(eps, 1.0 - EFo) // multi-scatter lobe + * max(eps, 1.0 - EFi) + / max(eps, 1.0 - avgEF); + return (f_ss + f_ms); +} + #ifdef SS_TRANSLUCENCY // Pixar diffusion profile // http://graphics.pixar.com/library/ApproxBSSRDF/paper.pdf diff --git a/packages/dev/core/src/Shaders/ShadersInclude/pbrBlockAlbedoOpacity.fx b/packages/dev/core/src/Shaders/ShadersInclude/pbrBlockAlbedoOpacity.fx index 30732fdc922..e0e51184fa5 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/pbrBlockAlbedoOpacity.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/pbrBlockAlbedoOpacity.fx @@ -12,7 +12,7 @@ albedoOpacityOutParams albedoOpacityBlock( ,in vec2 albedoInfos #endif , in float baseWeight -#ifdef BASEWEIGHT +#ifdef BASE_WEIGHT , in vec4 baseWeightTexture , in vec2 vBaseWeightInfos #endif @@ -75,7 +75,7 @@ albedoOpacityOutParams albedoOpacityBlock( // applied in computeDiffuseLighting), but with the diffuse model *currently* used // in Babylon.js, factoring it into the surfaceAlbedo is equivalent. surfaceAlbedo *= baseWeight; - #ifdef BASEWEIGHT + #ifdef BASE_WEIGHT surfaceAlbedo *= baseWeightTexture.r; #endif diff --git a/packages/dev/core/src/Shaders/ShadersInclude/pbrBlockReflection.fx b/packages/dev/core/src/Shaders/ShadersInclude/pbrBlockReflection.fx index f31fa0724d3..cec01643afa 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/pbrBlockReflection.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/pbrBlockReflection.fx @@ -190,6 +190,9 @@ #else , in sampler2D irradianceSampler #endif + #ifdef USE_IRRADIANCE_DOMINANT_DIRECTION + , in vec3 reflectionDominantDirection + #endif #endif #ifndef LODBASEDMICROSFURACE #ifdef REFLECTIONMAP_3D @@ -206,6 +209,9 @@ , in sampler2D icdfSampler #endif #endif + , in vec3 viewDirectionW + , in float diffuseRoughness + , in vec3 surfaceAlbedo ) { reflectionOutParams outParams; @@ -264,7 +270,15 @@ #else vec3 irradianceVector = vec3(reflectionMatrix * vec4(normalW, 0)).xyz; #endif - + vec3 irradianceView = vec3(reflectionMatrix * vec4(viewDirectionW, 0)).xyz; + #if !defined(USE_IRRADIANCE_DOMINANT_DIRECTION) && !defined(REALTIME_FILTERING) + // Approximate diffuse roughness by bending the surface normal away from the view. + #if BASE_DIFFUSE_MODEL != BRDF_DIFFUSE_MODEL_LAMBERT + float NdotV = max(dot(normalW, viewDirectionW), 0.0); + irradianceVector = mix(irradianceVector, irradianceView, (0.5 * (1.0 - NdotV)) * diffuseRoughness); + #endif + #endif + #ifdef REFLECTIONMAP_OPPOSITEZ irradianceVector.z *= -1.0; #endif @@ -278,7 +292,7 @@ environmentIrradiance = vEnvironmentIrradiance; #else #if defined(REALTIME_FILTERING) - environmentIrradiance = irradiance(reflectionSampler, irradianceVector, vReflectionFilteringInfo + environmentIrradiance = irradiance(reflectionSampler, irradianceVector, vReflectionFilteringInfo, diffuseRoughness, surfaceAlbedo, irradianceView #ifdef IBL_CDF_FILTERING , icdfSampler #endif @@ -297,6 +311,7 @@ #else vec4 environmentIrradiance4 = sampleReflection(irradianceSampler, reflectionCoords); #endif + environmentIrradiance = environmentIrradiance4.rgb; #ifdef RGBDREFLECTION environmentIrradiance.rgb = fromRGBD(environmentIrradiance4); @@ -305,6 +320,28 @@ #ifdef GAMMAREFLECTION environmentIrradiance.rgb = toLinearSpace(environmentIrradiance.rgb); #endif + // If we have a predominant light direction, use it to compute the diffuse roughness term.abort + // Otherwise, bend the irradiance vector to simulate retro-reflectivity of diffuse roughness. + #ifdef USE_IRRADIANCE_DOMINANT_DIRECTION + vec3 Ls = normalize(reflectionDominantDirection); + float NoL = dot(irradianceVector, Ls); + float NoV = dot(irradianceVector, irradianceView); + + vec3 diffuseRoughnessTerm = vec3(1.0); + #if BASE_DIFFUSE_MODEL == BRDF_DIFFUSE_MODEL_EON + float LoV = dot (Ls, irradianceView); + float mag = length(reflectionDominantDirection) * 2.0; + vec3 clampedAlbedo = clamp(surfaceAlbedo, vec3(0.1), vec3(1.0)); + diffuseRoughnessTerm = diffuseBRDF_EON(clampedAlbedo, diffuseRoughness, NoL, NoV, LoV) * PI; + diffuseRoughnessTerm = diffuseRoughnessTerm / clampedAlbedo; + diffuseRoughnessTerm = mix(vec3(1.0), diffuseRoughnessTerm, sqrt(min(mag * NoV, 1.0))); + #elif BASE_DIFFUSE_MODEL == BRDF_DIFFUSE_MODEL_BURLEY + vec3 H = (irradianceView + Ls)*0.5; + float VoH = dot(irradianceView, H); + diffuseRoughnessTerm = vec3(diffuseBRDF_Burley(NoL, NoV, VoH, diffuseRoughness) * PI); + #endif + environmentIrradiance = environmentIrradiance.rgb * diffuseRoughnessTerm; + #endif #endif environmentIrradiance *= vReflectionColor.rgb * vReflectionInfos.x; diff --git a/packages/dev/core/src/Shaders/ShadersInclude/pbrBlockReflectivity.fx b/packages/dev/core/src/Shaders/ShadersInclude/pbrBlockReflectivity.fx index 8fed9824237..6aaba22f259 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/pbrBlockReflectivity.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/pbrBlockReflectivity.fx @@ -2,6 +2,7 @@ struct reflectivityOutParams { float microSurface; float roughness; + float diffuseRoughness; vec3 surfaceReflectivityColor; #ifdef METALLICWORKFLOW vec3 surfaceAlbedo; @@ -32,6 +33,11 @@ reflectivityOutParams reflectivityBlock( #ifdef METALLICWORKFLOW , in vec3 surfaceAlbedo , in vec4 metallicReflectanceFactors +#endif + , in float baseDiffuseRoughness +#ifdef BASE_DIFFUSE_ROUGHNESS + , in float baseDiffuseRoughnessTexture + , in vec2 baseDiffuseRoughnessInfos #endif #ifdef REFLECTIVITY , in vec3 reflectivityInfos @@ -97,7 +103,7 @@ reflectivityOutParams reflectivityBlock( #endif #define CUSTOM_FRAGMENT_UPDATE_METALLICROUGHNESS - + // Compute microsurface from roughness. microSurface = 1.0 - metallicRoughness.g; @@ -148,9 +154,9 @@ reflectivityOutParams reflectivityBlock( #ifdef MICROSURFACEMAP microSurface *= microSurfaceTexel.r; #endif - + #define CUSTOM_FRAGMENT_UPDATE_MICROSURFACE - + #endif #endif #endif @@ -160,8 +166,14 @@ reflectivityOutParams reflectivityBlock( // Compute roughness. float roughness = 1. - microSurface; + float diffuseRoughness = baseDiffuseRoughness; +#ifdef BASE_DIFFUSE_ROUGHNESS + diffuseRoughness *= baseDiffuseRoughnessTexture * baseDiffuseRoughnessInfos.y; +#endif + outParams.microSurface = microSurface; outParams.roughness = roughness; + outParams.diffuseRoughness = diffuseRoughness; outParams.surfaceReflectivityColor = surfaceReflectivityColor; return outParams; diff --git a/packages/dev/core/src/Shaders/ShadersInclude/pbrBlockSubSurface.fx b/packages/dev/core/src/Shaders/ShadersInclude/pbrBlockSubSurface.fx index 9c9e1e42041..2c34872d586 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/pbrBlockSubSurface.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/pbrBlockSubSurface.fx @@ -493,7 +493,7 @@ struct subSurfaceOutParams #if defined(USESPHERICALFROMREFLECTIONMAP) #if defined(REALTIME_FILTERING) - vec3 refractionIrradiance = irradiance(reflectionSampler, -irradianceVector, vReflectionFilteringInfo + vec3 refractionIrradiance = irradiance(reflectionSampler, -irradianceVector, vReflectionFilteringInfo, 0.0, surfaceAlbedo, irradianceVector #ifdef IBL_CDF_FILTERING , icdfSampler #endif diff --git a/packages/dev/core/src/Shaders/ShadersInclude/pbrDirectLightingFunctions.fx b/packages/dev/core/src/Shaders/ShadersInclude/pbrDirectLightingFunctions.fx index ab5075a4927..97f79e02b0c 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/pbrDirectLightingFunctions.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/pbrDirectLightingFunctions.fx @@ -23,7 +23,7 @@ struct lightingInfo // Simulate area (small) lights by increasing roughness float adjustRoughnessFromLightProperties(float roughness, float lightRadius, float lightDistance) { #if defined(USEPHYSICALLIGHTFALLOFF) || defined(USEGLTFLIGHTFALLOFF) - // At small angle this approximation works. + // At small angle this approximation works. float lightRoughness = lightRadius / lightDistance; // Distribution can sum. float totalRoughness = saturate(lightRoughness + roughness); @@ -44,7 +44,14 @@ vec3 computeHemisphericDiffuseLighting(preLightingInfo info, vec3 lightColor, ve #endif vec3 computeDiffuseLighting(preLightingInfo info, vec3 lightColor) { - float diffuseTerm = diffuseBRDF_Burley(info.NdotL, info.NdotV, info.VdotH, info.roughness); + vec3 diffuseTerm = vec3(1.0 / PI); + #if BASE_DIFFUSE_MODEL == BRDF_DIFFUSE_MODEL_BURLEY + diffuseTerm = vec3(diffuseBRDF_Burley(info.NdotL, info.NdotV, info.VdotH, info.diffuseRoughness)); + #elif BASE_DIFFUSE_MODEL == BRDF_DIFFUSE_MODEL_EON + vec3 clampedAlbedo = clamp(info.surfaceAlbedo, vec3(0.1), vec3(1.0)); + diffuseTerm = diffuseBRDF_EON(clampedAlbedo, info.diffuseRoughness, info.NdotL, info.NdotV, info.LdotV); + diffuseTerm /= clampedAlbedo; + #endif return diffuseTerm * info.attenuation * info.NdotL * lightColor; } @@ -71,11 +78,17 @@ vec3 computeProjectionTextureDiffuseLighting(sampler2D projectionLightSampler, m transmittanceNdotL = mix(transmittance * wrapNdotL, vec3(wrapNdotL), trAdapt); #ifndef SS_TRANSLUCENCY_LEGACY } - - return (transmittanceNdotL / PI) * info.attenuation * lightColor; - #endif - + vec3 diffuseTerm = vec3(1.0 / PI); + #if BASE_DIFFUSE_MODEL == BRDF_DIFFUSE_MODEL_BURLEY + diffuseTerm = vec3(diffuseBRDF_Burley(info.NdotL, info.NdotV, info.VdotH, info.diffuseRoughness)); + #elif BASE_DIFFUSE_MODEL == BRDF_DIFFUSE_MODEL_EON + vec3 clampedAlbedo = clamp(info.surfaceAlbedo, vec3(0.1), vec3(1.0)); + diffuseTerm = diffuseBRDF_EON(clampedAlbedo, info.diffuseRoughness, info.NdotL, info.NdotV, info.LdotV); + diffuseTerm /= clampedAlbedo; + #endif + #else float diffuseTerm = diffuseBRDF_Burley(NdotL, info.NdotV, info.VdotH, info.roughness); + #endif return diffuseTerm * transmittanceNdotL * info.attenuation * lightColor; } #endif diff --git a/packages/dev/core/src/Shaders/ShadersInclude/pbrDirectLightingSetupFunctions.fx b/packages/dev/core/src/Shaders/ShadersInclude/pbrDirectLightingSetupFunctions.fx index 1113cb5aecb..c0179add6a2 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/pbrDirectLightingSetupFunctions.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/pbrDirectLightingSetupFunctions.fx @@ -16,7 +16,12 @@ struct preLightingInfo float NdotLUnclamped; float NdotL; float VdotH; + float LdotV; + + // TODO: the code would probably be leaner with material properties out of the structure. float roughness; + float diffuseRoughness; + vec3 surfaceAlbedo; #ifdef IRIDESCENCE float iridescenceIntensity; @@ -66,7 +71,7 @@ preLightingInfo computeDirectionalPreLightingInfo(vec4 lightData, vec3 V, vec3 N result.NdotLUnclamped = dot(N, result.L); result.NdotL = saturateEps(result.NdotLUnclamped); - + result.LdotV = dot(result.L, V); return result; } @@ -91,7 +96,7 @@ preLightingInfo computeHemisphericPreLightingInfo(vec4 lightData, vec3 V, vec3 N #if defined(AREALIGHTUSED) && defined(AREALIGHTSUPPORTED) #include -preLightingInfo computeAreaPreLightingInfo(sampler2D ltc1, sampler2D ltc2, vec3 viewDirectionW, vec3 vNormal, vec3 vPosition, vec4 lightData, vec3 halfWidth, vec3 halfHeight, float roughness ) +preLightingInfo computeAreaPreLightingInfo(sampler2D ltc1, sampler2D ltc2, vec3 viewDirectionW, vec3 vNormal, vec3 vPosition, vec4 lightData, vec3 halfWidth, vec3 halfHeight, float roughness ) { preLightingInfo result; result.lightOffset = lightData.xyz - vPosition; @@ -103,7 +108,7 @@ preLightingInfo computeAreaPreLightingInfo(sampler2D ltc1, sampler2D ltc2, vec3 #ifdef SPECULARTERM result.areaLightFresnel = data.Fresnel; - result.areaLightSpecular = data.Specular; + result.areaLightSpecular = data.Specular; #endif result.areaLightDiffuse = data.Diffuse; return result; diff --git a/packages/dev/core/src/Shaders/ShadersInclude/pbrFragmentDeclaration.fx b/packages/dev/core/src/Shaders/ShadersInclude/pbrFragmentDeclaration.fx index 8d0aa34965d..a12485a4607 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/pbrFragmentDeclaration.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/pbrFragmentDeclaration.fx @@ -3,6 +3,7 @@ uniform vec4 vEyePosition; uniform vec3 vReflectionColor; uniform vec4 vAlbedoColor; uniform float baseWeight; +uniform float baseDiffuseRoughness; // CUSTOM CONTROLS uniform vec4 vLightingIntensity; @@ -20,10 +21,14 @@ uniform vec3 vAmbientColor; uniform vec2 vAlbedoInfos; #endif -#ifdef BASEWEIGHT +#ifdef BASE_WEIGHT uniform vec2 vBaseWeightInfos; #endif +#ifdef BASE_DIFFUSE_ROUGHNESS +uniform vec2 vBaseDiffuseRoughnessInfos; +#endif + #ifdef AMBIENT uniform vec4 vAmbientInfos; #endif @@ -67,6 +72,9 @@ uniform mat4 view; #endif uniform mat4 reflectionMatrix; uniform vec3 vReflectionMicrosurfaceInfos; + #if defined(USEIRRADIANCEMAP) && defined(USE_IRRADIANCE_DOMINANT_DIRECTION) + uniform vec3 vReflectionDominantDirection; + #endif #if defined(USE_LOCAL_REFLECTIONMAP_CUBIC) && defined(REFLECTIONMAP_CUBIC) uniform vec3 vReflectionPosition; diff --git a/packages/dev/core/src/Shaders/ShadersInclude/pbrFragmentSamplersDeclaration.fx b/packages/dev/core/src/Shaders/ShadersInclude/pbrFragmentSamplersDeclaration.fx index c2d49f788fa..d55dc95bac8 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/pbrFragmentSamplersDeclaration.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/pbrFragmentSamplersDeclaration.fx @@ -1,5 +1,6 @@ #include(_DEFINENAME_,ALBEDO,_VARYINGNAME_,Albedo,_SAMPLERNAME_,albedo) -#include(_DEFINENAME_,BASEWEIGHT,_VARYINGNAME_,BaseWeight,_SAMPLERNAME_,baseWeight) +#include(_DEFINENAME_,BASE_WEIGHT,_VARYINGNAME_,BaseWeight,_SAMPLERNAME_,baseWeight) +#include(_DEFINENAME_,BASE_DIFFUSE_ROUGHNESS,_VARYINGNAME_,BaseDiffuseRoughness,_SAMPLERNAME_,baseDiffuseRoughness) #include(_DEFINENAME_,AMBIENT,_VARYINGNAME_,Ambient,_SAMPLERNAME_,ambient) #include(_DEFINENAME_,OPACITY,_VARYINGNAME_,Opacity,_SAMPLERNAME_,opacity) #include(_DEFINENAME_,EMISSIVE,_VARYINGNAME_,Emissive,_SAMPLERNAME_,emissive) diff --git a/packages/dev/core/src/Shaders/ShadersInclude/pbrUboDeclaration.fx b/packages/dev/core/src/Shaders/ShadersInclude/pbrUboDeclaration.fx index dbef7e7c0a4..0845a60e489 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/pbrUboDeclaration.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/pbrUboDeclaration.fx @@ -25,6 +25,7 @@ layout(std140, column_major) uniform; uniform Material { vec2 vAlbedoInfos; vec2 vBaseWeightInfos; + vec2 vBaseDiffuseRoughnessInfos; vec4 vAmbientInfos; vec2 vOpacityInfos; vec2 vEmissiveInfos; @@ -38,6 +39,7 @@ uniform Material { vec3 vBumpInfos; mat4 albedoMatrix; mat4 baseWeightMatrix; + mat4 baseDiffuseRoughnessMatrix; mat4 ambientMatrix; mat4 opacityMatrix; mat4 emissiveMatrix; @@ -50,8 +52,10 @@ uniform Material { vec3 vReflectionColor; vec4 vAlbedoColor; float baseWeight; + float baseDiffuseRoughness; vec4 vLightingIntensity; vec3 vReflectionMicrosurfaceInfos; + vec3 vReflectionDominantDirection; float pointSize; vec4 vReflectivityColor; vec3 vEmissiveColor; diff --git a/packages/dev/core/src/Shaders/ShadersInclude/pbrVertexDeclaration.fx b/packages/dev/core/src/Shaders/ShadersInclude/pbrVertexDeclaration.fx index d9142aaa417..943f913a2ae 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/pbrVertexDeclaration.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/pbrVertexDeclaration.fx @@ -1,5 +1,6 @@ uniform mat4 view; uniform mat4 viewProjection; +uniform vec4 vEyePosition; #ifdef MULTIVIEW mat4 viewProjectionR; #endif @@ -9,11 +10,17 @@ uniform mat4 albedoMatrix; uniform vec2 vAlbedoInfos; #endif -#ifdef BASEWEIGHT +#ifdef BASE_WEIGHT uniform mat4 baseWeightMatrix; uniform vec2 vBaseWeightInfos; #endif +uniform float baseDiffuseRoughness; +#ifdef BASE_DIFFUSE_ROUGHNESS +uniform mat4 baseDiffuseRoughnessMatrix; +uniform vec2 vBaseDiffuseRoughnessInfos; +#endif + #ifdef AMBIENT uniform mat4 ambientMatrix; uniform vec4 vAmbientInfos; diff --git a/packages/dev/core/src/Shaders/hdrIrradianceFiltering.fragment.fx b/packages/dev/core/src/Shaders/hdrIrradianceFiltering.fragment.fx index 49a44d9d44b..b2aa4b64cab 100644 --- a/packages/dev/core/src/Shaders/hdrIrradianceFiltering.fragment.fx +++ b/packages/dev/core/src/Shaders/hdrIrradianceFiltering.fragment.fx @@ -13,7 +13,7 @@ uniform float hdrScale; varying vec3 direction; void main() { - vec3 color = irradiance(inputTexture, direction, vFilteringInfo + vec3 color = irradiance(inputTexture, direction, vFilteringInfo, 0.0, vec3(1.0), direction #ifdef IBL_CDF_FILTERING , icdfTexture #endif diff --git a/packages/dev/core/src/Shaders/iblDominantDirection.fragment.fx b/packages/dev/core/src/Shaders/iblDominantDirection.fragment.fx new file mode 100644 index 00000000000..28ac908c778 --- /dev/null +++ b/packages/dev/core/src/Shaders/iblDominantDirection.fragment.fx @@ -0,0 +1,27 @@ +precision highp sampler2D; +precision highp samplerCube; + +#include +#include +#include +#include + +varying vec2 vUV; + +uniform sampler2D icdfSampler; + + +void main(void) { + vec3 lightDir = vec3(0.0, 0.0, 0.0); + for(uint i = 0u; i < NUM_SAMPLES; ++i) + { + vec2 Xi = hammersley(i, NUM_SAMPLES); + vec2 T; + T.x = texture2D(icdfSampler, vec2(Xi.x, 0.0)).x; + T.y = texture2D(icdfSampler, vec2(T.x, Xi.y)).y; + vec3 Ls = uv_to_normal(vec2(1.0 - fract(T.x + 0.25), T.y)); + lightDir += Ls; + } + lightDir /= float(NUM_SAMPLES); + gl_FragColor = vec4(lightDir, 1.0); +} \ No newline at end of file diff --git a/packages/dev/core/src/Shaders/pbr.fragment.fx b/packages/dev/core/src/Shaders/pbr.fragment.fx index d28c5e43547..5f2efc02382 100644 --- a/packages/dev/core/src/Shaders/pbr.fragment.fx +++ b/packages/dev/core/src/Shaders/pbr.fragment.fx @@ -90,7 +90,7 @@ void main(void) { vec4 albedoTexture = texture2D(albedoSampler, vAlbedoUV + uvOffset); #endif -#ifdef BASEWEIGHT +#ifdef BASE_WEIGHT vec4 baseWeightTexture = texture2D(baseWeightSampler, vBaseWeightUV + uvOffset); #endif @@ -109,7 +109,7 @@ void main(void) { , vAlbedoInfos #endif , baseWeight - #ifdef BASEWEIGHT + #ifdef BASE_WEIGHT , baseWeightTexture , vBaseWeightInfos #endif @@ -199,11 +199,20 @@ void main(void) { #endif #endif +#ifdef BASE_DIFFUSE_ROUGHNESS + float baseDiffuseRoughnessTexture = texture2D(baseDiffuseRoughnessSampler, vBaseDiffuseRoughnessUV + uvOffset).r; +#endif + reflectivityOut = reflectivityBlock( vReflectivityColor #ifdef METALLICWORKFLOW , surfaceAlbedo , metallicReflectanceFactors + #endif + , baseDiffuseRoughness + #ifdef BASE_DIFFUSE_ROUGHNESS + , baseDiffuseRoughnessTexture + , vBaseDiffuseRoughnessInfos #endif #ifdef REFLECTIVITY , vReflectivityInfos @@ -223,6 +232,7 @@ void main(void) { float microSurface = reflectivityOut.microSurface; float roughness = reflectivityOut.roughness; + float diffuseRoughness = reflectivityOut.diffuseRoughness; #ifdef METALLICWORKFLOW surfaceAlbedo = reflectivityOut.surfaceAlbedo; @@ -300,6 +310,9 @@ void main(void) { #endif #ifdef USEIRRADIANCEMAP , irradianceSampler + #ifdef USE_IRRADIANCE_DOMINANT_DIRECTION + , vReflectionDominantDirection + #endif #endif #ifndef LODBASEDMICROSFURACE , reflectionSamplerLow @@ -311,6 +324,9 @@ void main(void) { , icdfSampler #endif #endif + , viewDirectionW + , diffuseRoughness + , baseColor ); #else #define CUSTOM_REFLECTION diff --git a/packages/dev/core/src/Shaders/pbr.vertex.fx b/packages/dev/core/src/Shaders/pbr.vertex.fx index 71ce434d3e2..21141bd90a4 100644 --- a/packages/dev/core/src/Shaders/pbr.vertex.fx +++ b/packages/dev/core/src/Shaders/pbr.vertex.fx @@ -31,7 +31,8 @@ attribute vec4 color; #include #include(_DEFINENAME_,ALBEDO,_VARYINGNAME_,Albedo) -#include(_DEFINENAME_,BASEWEIGHT,_VARYINGNAME_,BaseWeight) +#include(_DEFINENAME_,BASE_WEIGHT,_VARYINGNAME_,BaseWeight) +#include(_DEFINENAME_,BASE_DIFFUSE_ROUGHNESS,_VARYINGNAME_,BaseDiffuseRoughness) #include(_DEFINENAME_,DETAIL,_VARYINGNAME_,Detail) #include(_DEFINENAME_,AMBIENT,_VARYINGNAME_,Ambient) #include(_DEFINENAME_,OPACITY,_VARYINGNAME_,Opacity) @@ -175,7 +176,15 @@ void main(void) { #endif #if defined(USESPHERICALFROMREFLECTIONMAP) && defined(USESPHERICALINVERTEX) - vec3 reflectionVector = vec3(reflectionMatrix * vec4(vNormalW, 0)).xyz; + #if BASE_DIFFUSE_MODEL != BRDF_DIFFUSE_MODEL_LAMBERT + // Bend the normal towards the viewer based on the diffuse roughness + vec3 viewDirectionW = normalize(vEyePosition.xyz - vPositionW); + float NdotV = max(dot(vNormalW, viewDirectionW), 0.0); + vec3 roughNormal = mix(vNormalW, viewDirectionW, (0.5 * (1.0 - NdotV)) * baseDiffuseRoughness); + vec3 reflectionVector = vec3(reflectionMatrix * vec4(roughNormal, 0)).xyz; + #else + vec3 reflectionVector = vec3(reflectionMatrix * vec4(vNormalW, 0)).xyz; + #endif #ifdef REFLECTIONMAP_OPPOSITEZ reflectionVector.z *= -1.0; #endif @@ -220,7 +229,8 @@ void main(void) { #include[3..7] #include(_DEFINENAME_,ALBEDO,_VARYINGNAME_,Albedo,_MATRIXNAME_,albedo,_INFONAME_,AlbedoInfos.x) - #include(_DEFINENAME_,BASEWEIGHT,_VARYINGNAME_,BaseWeight,_MATRIXNAME_,baseWeight,_INFONAME_,BaseWeightInfos.x) + #include(_DEFINENAME_,BASE_WEIGHT,_VARYINGNAME_,BaseWeight,_MATRIXNAME_,baseWeight,_INFONAME_,BaseWeightInfos.x) + #include(_DEFINENAME_,BASE_DIFFUSE_ROUGHNESS,_VARYINGNAME_,BaseDiffuseRoughness,_MATRIXNAME_,baseDiffuseRoughness,_INFONAME_,BaseDiffuseRoughnessInfos.x) #include(_DEFINENAME_,DETAIL,_VARYINGNAME_,Detail,_MATRIXNAME_,detail,_INFONAME_,DetailInfos.x) #include(_DEFINENAME_,AMBIENT,_VARYINGNAME_,Ambient,_MATRIXNAME_,ambient,_INFONAME_,AmbientInfos.x) #include(_DEFINENAME_,OPACITY,_VARYINGNAME_,Opacity,_MATRIXNAME_,opacity,_INFONAME_,OpacityInfos.x) diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/hdrFilteringFunctions.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/hdrFilteringFunctions.fx index 66cc98f6fef..d750a717350 100644 --- a/packages/dev/core/src/ShadersWGSL/ShadersInclude/hdrFilteringFunctions.fx +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/hdrFilteringFunctions.fx @@ -157,7 +157,7 @@ // // - fn irradiance(inputTexture: texture_cube, inputSampler: sampler, inputN: vec3f, filteringInfo: vec2f + fn irradiance(inputTexture: texture_cube, inputSampler: sampler, inputN: vec3f, filteringInfo: vec2f, diffuseRoughness: f32, surfaceAlbedo: vec3f, inputV: vec3f #ifdef IBL_CDF_FILTERING , icdfSampler: texture_2d, icdfSamplerSampler: sampler #endif @@ -171,12 +171,13 @@ tangent = normalize(cross(tangent, n)); var bitangent: vec3f = cross(n, tangent); var tbn: mat3x3f = mat3x3f(tangent, bitangent, n); + var tbnInverse: mat3x3f = transpose(tbn); #endif var maxLevel: f32 = filteringInfo.y; var dim0: f32 = filteringInfo.x; var omegaP: f32 = (4. * PI) / (6. * dim0 * dim0); - + var clampedAlbedo: vec3f = clamp(surfaceAlbedo, vec3f(0.1), vec3f(1.0)); for(var i: u32 = 0u; i < NUM_SAMPLES; i++) { var Xi: vec2f = hammersley(i, NUM_SAMPLES); @@ -187,11 +188,26 @@ T.y = textureSampleLevel(icdfSampler, icdfSamplerSampler, vec2(T.x, Xi.y), 0.0).y; var Ls: vec3f = uv_to_normal(vec2f(1.0 - fract(T.x + 0.25), T.y)); var NoL: f32 = dot(n, Ls); + var NoV: f32 = dot(n, inputV); + #if BASE_DIFFUSE_MODEL == BRDF_DIFFUSE_MODEL_EON + var LoV: f32 = dot(Ls, inputV); + #elif BASE_DIFFUSE_MODEL == BRDF_DIFFUSE_MODEL_BURLEY + var H: vec3f = (inputV + Ls) * 0.5; + var VoH: f32 = dot(inputV, H); + #endif #else var Ls: vec3f = hemisphereCosSample(Xi); Ls = normalize(Ls); var Ns: vec3f = vec3f(0., 0., 1.); var NoL: f32 = dot(Ns, Ls); + var V: vec3f = tbnInverse * inputV; + var NoV: f32 = dot(Ns, V); + #if BASE_DIFFUSE_MODEL == BRDF_DIFFUSE_MODEL_EON + var LoV: f32 = dot(Ls, V); + #elif BASE_DIFFUSE_MODEL == BRDF_DIFFUSE_MODEL_BURLEY + var H: vec3f = (V + Ls) * 0.5; + var VoH: f32 = dot(V, H); + #endif #endif if (NoL > 0.) { @@ -211,20 +227,31 @@ c = toLinearSpaceVec3(c); #endif + var diffuseRoughnessTerm: vec3f = vec3f(1.0); + #if BASE_DIFFUSE_MODEL == BRDF_DIFFUSE_MODEL_EON + diffuseRoughnessTerm = diffuseBRDF_EON(clampedAlbedo, diffuseRoughness, NoL, NoV, LoV) * PI; + #elif BASE_DIFFUSE_MODEL == BRDF_DIFFUSE_MODEL_BURLEY + diffuseRoughnessTerm = vec3f(diffuseBRDF_Burley(NoL, NoV, VoH, diffuseRoughness) * PI); + #endif + #ifdef IBL_CDF_FILTERING var light: vec3f = vec3f(0.0); if (pdf > 1e-6) { light = vec3f(1.0) / vec3f(pdf) * c; } - result += NoL * light; + result += NoL * diffuseRoughnessTerm * light; #else - result += c; + result += c * diffuseRoughnessTerm; #endif } } result = result * NUM_SAMPLES_FLOAT_INVERSED; + #if BASE_DIFFUSE_MODEL == BRDF_DIFFUSE_MODEL_EON + result = result / clampedAlbedo; + #endif + return result; } diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/lightFragment.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/lightFragment.fx index ad18fd129e2..c9dcaba77aa 100644 --- a/packages/dev/core/src/ShadersWGSL/ShadersInclude/lightFragment.fx +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/lightFragment.fx @@ -51,8 +51,8 @@ preInfo.attenuation *= computeDirectionalLightFalloff_IES(light{X}.vLightDirection.xyz, preInfo.L, iesLightTexture{X}, iesLightTexture{X}Sampler); #else preInfo.attenuation *= computeDirectionalLightFalloff(light{X}.vLightDirection.xyz, preInfo.L, light{X}.vLightDirection.w, light{X}.vLightData.w, light{X}.vLightFalloff.z, light{X}.vLightFalloff.w); - #endif - #endif + #endif + #endif #elif defined(POINTLIGHT{X}) #ifdef LIGHT_FALLOFF_GLTF{X} preInfo.attenuation = computeDistanceLightFalloff_GLTF(preInfo.lightDistanceSquared, light{X}.vLightFalloff.y); @@ -74,6 +74,8 @@ #else preInfo.roughness = adjustRoughnessFromLightProperties(roughness, light{X}.vLightSpecular.a, preInfo.lightDistance); #endif + preInfo.diffuseRoughness = diffuseRoughness; + preInfo.surfaceAlbedo = surfaceAlbedo; #ifdef IRIDESCENCE preInfo.iridescenceIntensity = iridescenceIntensity; @@ -135,7 +137,7 @@ #endif info.clearCoat = computeClearCoatLighting(preInfo, clearcoatOut.clearCoatNormalW, clearcoatOut.clearCoatAARoughnessFactors.x, clearcoatOut.clearCoatIntensity, diffuse{X}.rgb); - + #ifdef CLEARCOAT_TINT // Absorption absorption = computeClearCoatLightingAbsorption(clearcoatOut.clearCoatNdotVRefract, preInfo.L, clearcoatOut.clearCoatNormalW, clearcoatOut.clearCoatColor, clearcoatOut.clearCoatThickness, clearcoatOut.clearCoatIntensity); @@ -167,7 +169,7 @@ info = computeIESSpotLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDirection, diffuse{X}.rgb, light{X}.vLightSpecular.rgb, diffuse{X}.a, glossiness, iesLightTexture{X}, iesLightTexture{X}Sampler); #else info = computeSpotLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDirection, diffuse{X}.rgb, light{X}.vLightSpecular.rgb, diffuse{X}.a, glossiness); - #endif + #endif #elif defined(HEMILIGHT{X}) info = computeHemisphericLighting(viewDirectionW, normalW, light{X}.vLightData, diffuse{X}.rgb, light{X}.vLightSpecular.rgb, light{X}.vLightGround, glossiness); #elif defined(POINTLIGHT{X}) || defined(DIRLIGHT{X}) @@ -200,18 +202,18 @@ #endif var diff{X}: f32 = 0.; - + vPositionFromLight{X}[0] = fragmentInputs.vPositionFromLight{X}_0; vPositionFromLight{X}[1] = fragmentInputs.vPositionFromLight{X}_1; vPositionFromLight{X}[2] = fragmentInputs.vPositionFromLight{X}_2; vPositionFromLight{X}[3] = fragmentInputs.vPositionFromLight{X}_3; - + vDepthMetric{X}[0] = fragmentInputs.vDepthMetric{X}_0; vDepthMetric{X}[1] = fragmentInputs.vDepthMetric{X}_1; vDepthMetric{X}[2] = fragmentInputs.vDepthMetric{X}_2; vDepthMetric{X}[3] = fragmentInputs.vDepthMetric{X}_3; - - for (var i:i32 = 0; i < SHADOWCSMNUM_CASCADES{X}; i++) + + for (var i:i32 = 0; i < SHADOWCSMNUM_CASCADES{X}; i++) { #ifdef SHADOWCSM_RIGHTHANDED{X} diff{X} = uniforms.viewFrustumZ{X}[i] + fragmentInputs.vPositionFromCamera{X}.z; @@ -367,7 +369,7 @@ #else #ifdef SHADOWCSMDEBUG{X} diffuseBase += info.diffuse * shadowDebug{X}; - #else + #else diffuseBase += info.diffuse * shadow; #endif #ifdef SS_TRANSLUCENCY diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBRDFFunctions.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBRDFFunctions.fx index 03b32cd1d82..126d33c3725 100644 --- a/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBRDFFunctions.fx +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBRDFFunctions.fx @@ -1,5 +1,8 @@ // Constants #define FRESNEL_MAXIMUM_ON_ROUGH 0.25 +#define BRDF_DIFFUSE_MODEL_EON 0 +#define BRDF_DIFFUSE_MODEL_BURLEY 1 +#define BRDF_DIFFUSE_MODEL_LAMBERT 2 // ______________________________________________________________________ // @@ -420,6 +423,42 @@ fn normalDistributionFunction_TrowbridgeReitzGGX(NdotH: f32, alphaG: f32) -> f32 // // DiffuseBRDF // ______________________________________________________________________ +const constant1_FON: f32 = 0.5f - 2.0f / (3.0f * PI); +const constant2_FON: f32 = 2.0f / 3.0f - 28.0f / (15.0f * PI); + +// Fujii Oren-Nayar (FON) directional albedo (approximate). +fn E_FON_approx(mu: f32, roughness: f32) -> f32 +{ + var sigma: f32 = roughness; // FON sigma prime + var mucomp: f32 = 1.0f - mu; + var mucomp2: f32 = mucomp * mucomp; + const Gcoeffs: mat2x2f = mat2x2f(0.0571085289f, -0.332181442f, + 0.491881867f, 0.0714429953f); + var GoverPi: f32 = dot(Gcoeffs * vec2f(mucomp, mucomp2), vec2f(1.0f, mucomp2)); + return (1.0f + sigma * GoverPi) / (1.0f + constant1_FON * sigma); +} + +fn diffuseBRDF_EON(albedo: vec3f, roughness: f32, NdotL: f32, NdotV: f32, LdotV: f32) -> vec3f +{ + var rho: vec3f = albedo; + var sigma: f32 = roughness; // FON sigma prime + var mu_i: f32 = NdotL; // input angle cos + var mu_o: f32 = NdotV; // output angle cos + var s: f32 = LdotV - mu_i * mu_o; // QON s term + var sovertF: f32 = select(s, s / max(mu_i, mu_o), s > 0.0f); // FON s/t + var AF: f32 = 1.0f / (1.0f + constant1_FON * sigma); // FON A coeff. + var f_ss: vec3f = (rho * RECIPROCAL_PI) * AF * (1.0f + sigma * sovertF); // single-scatter + var EFo: f32 = E_FON_approx(mu_o, sigma); // FON wo albedo (approx) + var EFi: f32 = E_FON_approx(mu_i, sigma); // FON wi albedo (approx) + var avgEF: f32 = AF * (1.0f + constant2_FON * sigma); // avg. albedo + var rho_ms: vec3f = (rho * rho) * avgEF / (vec3f(1.0f) - rho * (1.0f - avgEF)); + const eps: f32 = 1.0e-7f; + var f_ms: vec3f = (rho_ms * RECIPROCAL_PI) * max(eps, 1.0f - EFo) // multi-scatter lobe + * max(eps, 1.0f - EFi) + / max(eps, 1.0f - avgEF); + return (f_ss + f_ms); +} + // Disney diffuse term // https://blog.selfshadow.com/publications/s2012-shading-course/burley/s2012_pbs_disney_brdf_notes_v3.pdf diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBlockAlbedoOpacity.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBlockAlbedoOpacity.fx index 6a3ac9520a4..7321d2cd691 100644 --- a/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBlockAlbedoOpacity.fx +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBlockAlbedoOpacity.fx @@ -12,7 +12,7 @@ fn albedoOpacityBlock( ,albedoInfos: vec2f #endif , baseWeight: f32 -#ifdef BASEWEIGHT +#ifdef BASE_WEIGHT , baseWeightTexture: vec4f , vBaseWeightInfos: vec2f #endif @@ -75,7 +75,7 @@ fn albedoOpacityBlock( // applied in computeDiffuseLighting), but with the diffuse model *currently* used // in Babylon.js, factoring it into the surfaceAlbedo is equivalent. surfaceAlbedo *= baseWeight; - #ifdef BASEWEIGHT + #ifdef BASE_WEIGHT surfaceAlbedo *= baseWeightTexture.r; #endif diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBlockReflection.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBlockReflection.fx index fda627b0d05..364e33296f6 100644 --- a/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBlockReflection.fx +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBlockReflection.fx @@ -212,6 +212,9 @@ , irradianceSampler: texture_2d , irradianceSamplerSampler: sampler #endif + #ifdef USE_IRRADIANCE_DOMINANT_DIRECTION + , reflectionDominantDirection: vec3f + #endif #endif #ifndef LODBASEDMICROSFURACE #ifdef REFLECTIONMAP_3D @@ -233,6 +236,9 @@ , icdfSamplerSampler: sampler #endif #endif + , viewDirectionW: vec3f + , diffuseRoughness: f32 + , surfaceAlbedo: vec3f ) -> reflectionOutParams { var outParams: reflectionOutParams; @@ -293,6 +299,14 @@ #else var irradianceVector: vec3f = (reflectionMatrix * vec4f(normalW, 0)).xyz; #endif + var irradianceView: vec3f = (reflectionMatrix * vec4f(viewDirectionW, 0)).xyz; + #if !defined(USE_IRRADIANCE_DOMINANT_DIRECTION) && !defined(REALTIME_FILTERING) + // Approximate diffuse roughness by bending the surface normal away from the view. + #if BASE_DIFFUSE_MODEL != BRDF_DIFFUSE_MODEL_LAMBERT + var NdotV: f32 = max(dot(normalW, viewDirectionW), 0.0); + irradianceVector = mix(irradianceVector, irradianceView, (0.5 * (1.0 - NdotV)) * diffuseRoughness); + #endif + #endif #ifdef REFLECTIONMAP_OPPOSITEZ irradianceVector.z *= -1.0; @@ -307,7 +321,7 @@ environmentIrradiance = vEnvironmentIrradiance; #else #if defined(REALTIME_FILTERING) - environmentIrradiance = irradiance(reflectionSampler, reflectionSamplerSampler, irradianceVector, vReflectionFilteringInfo + environmentIrradiance = irradiance(reflectionSampler, reflectionSamplerSampler, irradianceVector, vReflectionFilteringInfo, diffuseRoughness, surfaceAlbedo, irradianceView #ifdef IBL_CDF_FILTERING , icdfSampler , icdfSamplerSampler @@ -327,7 +341,32 @@ #else var environmentIrradiance4: vec4f = textureSample(irradianceSampler, irradianceSamplerSampler, reflectionCoords); #endif - environmentIrradiance = environmentIrradiance4.rgb; + + // If we have a predominant light direction, use it to compute the diffuse roughness term.abort + // Otherwise, bend the irradiance vector to simulate retro-reflectivity of diffuse roughness. + #ifdef USE_IRRADIANCE_DOMINANT_DIRECTION + var Ls: vec3f = normalize(reflectionDominantDirection); + var NoL: f32 = dot(irradianceVector, Ls); + var NoV: f32 = dot(irradianceVector, irradianceView); + + var diffuseRoughnessTerm: vec3f = vec3f(1.0); + #if BASE_DIFFUSE_MODEL == BRDF_DIFFUSE_MODEL_EON + var LoV: f32 = dot(Ls, irradianceView); + var mag: f32 = length(reflectionDominantDirection) * 2.0f; + var clampedAlbedo: vec3f = clamp(surfaceAlbedo, vec3f(0.1), vec3f(1.0)); + diffuseRoughnessTerm = diffuseBRDF_EON(clampedAlbedo, diffuseRoughness, NoL, NoV, LoV) * PI; + diffuseRoughnessTerm = diffuseRoughnessTerm / clampedAlbedo; + diffuseRoughnessTerm = mix(vec3f(1.0), diffuseRoughnessTerm, sqrt(min(mag * NoV, 1.0f))); + #elif BASE_DIFFUSE_MODEL == BRDF_DIFFUSE_MODEL_BURLEY + var H: vec3f = (irradianceView + Ls) * 0.5f; + var VoH: f32 = dot(irradianceView, H); + diffuseRoughnessTerm = vec3f(diffuseBRDF_Burley(NoL, NoV, VoH, diffuseRoughness) * PI); + #endif + environmentIrradiance = environmentIrradiance4.rgb * diffuseRoughnessTerm; + #else + environmentIrradiance = environmentIrradiance4.rgb; + #endif + #ifdef RGBDREFLECTION environmentIrradiance = fromRGBD(environmentIrradiance4); #endif diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBlockReflectivity.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBlockReflectivity.fx index 906729b3bcd..3caf1283206 100644 --- a/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBlockReflectivity.fx +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBlockReflectivity.fx @@ -2,6 +2,7 @@ struct reflectivityOutParams { microSurface: f32, roughness: f32, + diffuseRoughness: f32, surfaceReflectivityColor: vec3f, #ifdef METALLICWORKFLOW surfaceAlbedo: vec3f, @@ -32,6 +33,11 @@ fn reflectivityBlock( #ifdef METALLICWORKFLOW , surfaceAlbedo: vec3f , metallicReflectanceFactors: vec4f +#endif + , baseDiffuseRoughness: f32 +#ifdef BASE_DIFFUSE_ROUGHNESS + , baseDiffuseRoughnessTexture: f32 + , baseDiffuseRoughnessInfos: vec2f #endif #ifdef REFLECTIVITY , reflectivityInfos: vec3f @@ -97,7 +103,7 @@ fn reflectivityBlock( #endif #define CUSTOM_FRAGMENT_UPDATE_METALLICROUGHNESS - + // Compute microsurface from roughness. microSurface = 1.0 - metallicRoughness.g; @@ -148,9 +154,9 @@ fn reflectivityBlock( #ifdef MICROSURFACEMAP microSurface *= microSurfaceTexel.r; #endif - + #define CUSTOM_FRAGMENT_UPDATE_MICROSURFACE - + #endif #endif #endif @@ -160,8 +166,14 @@ fn reflectivityBlock( // Compute roughness. var roughness: f32 = 1. - microSurface; + var diffuseRoughness: f32 = baseDiffuseRoughness; +#ifdef BASE_DIFFUSE_ROUGHNESS + diffuseRoughness *= baseDiffuseRoughnessTexture * baseDiffuseRoughnessInfos.y; +#endif + outParams.microSurface = microSurface; outParams.roughness = roughness; + outParams.diffuseRoughness = diffuseRoughness; outParams.surfaceReflectivityColor = surfaceReflectivityColor; return outParams; diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBlockSubSurface.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBlockSubSurface.fx index f2194020f35..77f93cc8998 100644 --- a/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBlockSubSurface.fx +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBlockSubSurface.fx @@ -517,7 +517,7 @@ struct subSurfaceOutParams #if defined(USESPHERICALFROMREFLECTIONMAP) #if defined(REALTIME_FILTERING) - var refractionIrradiance: vec3f = irradiance(reflectionSampler, reflectionSamplerSampler, -irradianceVector, vReflectionFilteringInfo + var refractionIrradiance: vec3f = irradiance(reflectionSampler, reflectionSamplerSampler, -irradianceVector, vReflectionFilteringInfo, 0.0, surfaceAlbedo, irradianceVector #ifdef IBL_CDF_FILTERING , icdfSampler , icdfSamplerSampler diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrDirectLightingFunctions.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrDirectLightingFunctions.fx index 1950285e06c..9d6a94a4853 100644 --- a/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrDirectLightingFunctions.fx +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrDirectLightingFunctions.fx @@ -23,7 +23,7 @@ struct lightingInfo // Simulate area (small) lights by increasing roughness fn adjustRoughnessFromLightProperties(roughness: f32, lightRadius: f32, lightDistance: f32) -> f32 { #if defined(USEPHYSICALLIGHTFALLOFF) || defined(USEGLTFLIGHTFALLOFF) - // At small angle this approximation works. + // At small angle this approximation works. var lightRoughness: f32 = lightRadius / lightDistance; // Distribution can sum. var totalRoughness: f32 = saturate(lightRoughness + roughness); @@ -44,7 +44,14 @@ fn computeHemisphericDiffuseLighting(info: preLightingInfo, lightColor: vec3f, g #endif fn computeDiffuseLighting(info: preLightingInfo, lightColor: vec3f) -> vec3f { - var diffuseTerm: f32 = diffuseBRDF_Burley(info.NdotL, info.NdotV, info.VdotH, info.roughness); + var diffuseTerm: vec3f = vec3f(1.0 / PI); + #if BASE_DIFFUSE_MODEL == BRDF_DIFFUSE_MODEL_BURLEY + diffuseTerm = vec3f(diffuseBRDF_Burley(info.NdotL, info.NdotV, info.VdotH, info.diffuseRoughness)); + #elif BASE_DIFFUSE_MODEL == BRDF_DIFFUSE_MODEL_EON + var clampedAlbedo: vec3f = clamp(info.surfaceAlbedo, vec3f(0.1), vec3f(1.0)); + diffuseTerm = diffuseBRDF_EON(clampedAlbedo, info.diffuseRoughness, info.NdotL, info.NdotV, info.LdotV); + diffuseTerm /= clampedAlbedo; + #endif return diffuseTerm * info.attenuation * info.NdotL * lightColor; } @@ -70,12 +77,21 @@ fn computeProjectionTextureDiffuseLighting(projectionLightTexture: texture_2d pr result.NdotLUnclamped = dot(N, result.L); result.NdotL = saturateEps(result.NdotLUnclamped); - + result.LdotV = dot(result.L, V); return result; } diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrFragmentSamplersDeclaration.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrFragmentSamplersDeclaration.fx index 8b6df840f7e..6f56c5372d4 100644 --- a/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrFragmentSamplersDeclaration.fx +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrFragmentSamplersDeclaration.fx @@ -1,5 +1,6 @@ #include(_DEFINENAME_,ALBEDO,_VARYINGNAME_,Albedo,_SAMPLERNAME_,albedo) -#include(_DEFINENAME_,BASEWEIGHT,_VARYINGNAME_,BaseWeight,_SAMPLERNAME_,baseWeight) +#include(_DEFINENAME_,BASE_WEIGHT,_VARYINGNAME_,BaseWeight,_SAMPLERNAME_,baseWeight) +#include(_DEFINENAME_,BASE_DIFFUSE_ROUGHNESS,_VARYINGNAME_,BaseDiffuseRoughness,_SAMPLERNAME_,baseDiffuseRoughness) #include(_DEFINENAME_,AMBIENT,_VARYINGNAME_,Ambient,_SAMPLERNAME_,ambient) #include(_DEFINENAME_,OPACITY,_VARYINGNAME_,Opacity,_SAMPLERNAME_,opacity) #include(_DEFINENAME_,EMISSIVE,_VARYINGNAME_,Emissive,_SAMPLERNAME_,emissive) diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrUboDeclaration.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrUboDeclaration.fx index b730d6beddf..3cb5d43a6cf 100644 --- a/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrUboDeclaration.fx +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrUboDeclaration.fx @@ -1,5 +1,6 @@ uniform vAlbedoInfos: vec2f; uniform vBaseWeightInfos: vec2f; +uniform vBaseDiffuseRoughnessInfos: vec2f; uniform vAmbientInfos: vec4f; uniform vOpacityInfos: vec2f; uniform vEmissiveInfos: vec2f; @@ -13,6 +14,7 @@ uniform vReflectionSize: vec3f; uniform vBumpInfos: vec3f; uniform albedoMatrix: mat4x4f; uniform baseWeightMatrix: mat4x4f; +uniform baseDiffuseRoughnessMatrix: mat4x4f; uniform ambientMatrix: mat4x4f; uniform opacityMatrix: mat4x4f; uniform emissiveMatrix: mat4x4f; @@ -25,8 +27,10 @@ uniform reflectionMatrix: mat4x4f; uniform vReflectionColor: vec3f; uniform vAlbedoColor: vec4f; uniform baseWeight: f32; +uniform baseDiffuseRoughness: f32; uniform vLightingIntensity: vec4f; uniform vReflectionMicrosurfaceInfos: vec3f; +uniform vReflectionDominantDirection: vec3f; uniform pointSize: f32; uniform vReflectivityColor: vec4f; uniform vEmissiveColor: vec3f; diff --git a/packages/dev/core/src/ShadersWGSL/hdrIrradianceFiltering.fragment.fx b/packages/dev/core/src/ShadersWGSL/hdrIrradianceFiltering.fragment.fx index 6a1ba090fca..cea8e9ed4c5 100644 --- a/packages/dev/core/src/ShadersWGSL/hdrIrradianceFiltering.fragment.fx +++ b/packages/dev/core/src/ShadersWGSL/hdrIrradianceFiltering.fragment.fx @@ -16,7 +16,7 @@ varying direction: vec3f; @fragment fn main(input: FragmentInputs) -> FragmentOutputs { - var color: vec3f = irradiance(inputTexture, inputTextureSampler, input.direction, uniforms.vFilteringInfo + var color: vec3f = irradiance(inputTexture, inputTextureSampler, input.direction, uniforms.vFilteringInfo, 0.0, vec3f(1.0), input.direction #ifdef IBL_CDF_FILTERING , icdfTexture, icdfTextureSampler #endif diff --git a/packages/dev/core/src/ShadersWGSL/iblDominantDirection.fragment.fx b/packages/dev/core/src/ShadersWGSL/iblDominantDirection.fragment.fx new file mode 100644 index 00000000000..1d04e7bda0c --- /dev/null +++ b/packages/dev/core/src/ShadersWGSL/iblDominantDirection.fragment.fx @@ -0,0 +1,23 @@ +#include +#include +#include +#include + +var icdfSamplerSampler: sampler; +var icdfSampler: texture_2d; + +@fragment +fn main(input: FragmentInputs) -> FragmentOutputs { + var lightDir: vec3f = vec3f(0.0, 0.0, 0.0); + for(var i: u32 = 0u; i < NUM_SAMPLES; i++) + { + var Xi: vec2f = hammersley(i, NUM_SAMPLES); + var T: vec2f; + T.x = textureSampleLevel(icdfSampler, icdfSamplerSampler, vec2(Xi.x, 0.0), 0.0).x; + T.y = textureSampleLevel(icdfSampler, icdfSamplerSampler, vec2(T.x, Xi.y), 0.0).y; + var Ls: vec3f = uv_to_normal(vec2f(1.0 - fract(T.x + 0.25), T.y)); + lightDir += Ls; + } + lightDir /= vec3f(f32(NUM_SAMPLES)); + fragmentOutputs.color = vec4f(lightDir, 1.0); +} \ No newline at end of file diff --git a/packages/dev/core/src/ShadersWGSL/pbr.fragment.fx b/packages/dev/core/src/ShadersWGSL/pbr.fragment.fx index d4f01ea3152..b7aa702a2ec 100644 --- a/packages/dev/core/src/ShadersWGSL/pbr.fragment.fx +++ b/packages/dev/core/src/ShadersWGSL/pbr.fragment.fx @@ -75,7 +75,7 @@ fn main(input: FragmentInputs) -> FragmentOutputs { var albedoTexture: vec4f = textureSample(albedoSampler, albedoSamplerSampler, fragmentInputs.vAlbedoUV + uvOffset); #endif -#ifdef BASEWEIGHT +#ifdef BASE_WEIGHT var baseWeightTexture: vec4f = textureSample(baseWeightSampler, baseWeightSamplerSampler, fragmentInputs.vBaseWeightUV + uvOffset); #endif @@ -94,7 +94,7 @@ fn main(input: FragmentInputs) -> FragmentOutputs { , uniforms.vAlbedoInfos #endif , uniforms.baseWeight - #ifdef BASEWEIGHT + #ifdef BASE_WEIGHT , baseWeightTexture , uniforms.vBaseWeightInfos #endif @@ -161,6 +161,10 @@ fn main(input: FragmentInputs) -> FragmentOutputs { var microSurfaceTexel: vec4f = textureSample(microSurfaceSampler, microSurfaceSamplerSampler, fragmentInputs.vMicroSurfaceSamplerUV + uvOffset) * uniforms.vMicroSurfaceSamplerInfos.y; #endif +#ifdef BASE_DIFFUSE_ROUGHNESS + var baseDiffuseRoughnessTexture: f32 = textureSample(baseDiffuseRoughnessSampler, baseDiffuseRoughnessSamplerSampler, fragmentInputs.vBaseDiffuseRoughnessUV + uvOffset).x; +#endif + #ifdef METALLICWORKFLOW var metallicReflectanceFactors: vec4f = uniforms.vMetallicReflectanceFactors; #ifdef REFLECTANCE @@ -189,6 +193,11 @@ fn main(input: FragmentInputs) -> FragmentOutputs { #ifdef METALLICWORKFLOW , surfaceAlbedo , metallicReflectanceFactors + #endif + , uniforms.baseDiffuseRoughness + #ifdef BASE_DIFFUSE_ROUGHNESS + , baseDiffuseRoughnessTexture + , uniforms.vBaseDiffuseRoughnessInfos #endif #ifdef REFLECTIVITY , uniforms.vReflectivityInfos @@ -208,6 +217,7 @@ fn main(input: FragmentInputs) -> FragmentOutputs { var microSurface: f32 = reflectivityOut.microSurface; var roughness: f32 = reflectivityOut.roughness; + var diffuseRoughness: f32 = reflectivityOut.diffuseRoughness; #ifdef METALLICWORKFLOW surfaceAlbedo = reflectivityOut.surfaceAlbedo; @@ -287,6 +297,9 @@ fn main(input: FragmentInputs) -> FragmentOutputs { #ifdef USEIRRADIANCEMAP , irradianceSampler , irradianceSamplerSampler + #ifdef USE_IRRADIANCE_DOMINANT_DIRECTION + , uniforms.vReflectionDominantDirection + #endif #endif #ifndef LODBASEDMICROSFURACE , reflectionLowSampler @@ -301,6 +314,9 @@ fn main(input: FragmentInputs) -> FragmentOutputs { , icdfSamplerSampler #endif #endif + , viewDirectionW + , diffuseRoughness + , surfaceAlbedo ); #else #define CUSTOM_REFLECTION @@ -360,7 +376,7 @@ fn main(input: FragmentInputs) -> FragmentOutputs { , reflectionHighSamplerSampler #endif #ifdef REALTIME_FILTERING - , vReflectionFilteringInfo + , uniforms.vReflectionFilteringInfo #endif #if !defined(REFLECTIONMAP_SKYBOX) && defined(RADIANCEOCCLUSION) , seo @@ -545,7 +561,7 @@ fn main(input: FragmentInputs) -> FragmentOutputs { #if defined(REALTIME_FILTERING) , reflectionSampler , reflectionSamplerSampler - , vReflectionFilteringInfo + , uniforms.vReflectionFilteringInfo #ifdef IBL_CDF_FILTERING , icdfSampler , icdfSamplerSampler diff --git a/packages/dev/core/src/ShadersWGSL/pbr.vertex.fx b/packages/dev/core/src/ShadersWGSL/pbr.vertex.fx index 93beef2fcb7..840335bf237 100644 --- a/packages/dev/core/src/ShadersWGSL/pbr.vertex.fx +++ b/packages/dev/core/src/ShadersWGSL/pbr.vertex.fx @@ -27,7 +27,8 @@ attribute color: vec4f; #include #include(_DEFINENAME_,ALBEDO,_VARYINGNAME_,Albedo) -#include(_DEFINENAME_,BASEWEIGHT,_VARYINGNAME_,BaseWeight) +#include(_DEFINENAME_,BASE_WEIGHT,_VARYINGNAME_,BaseWeight) +#include(_DEFINENAME_,BASE_DIFFUSE_ROUGHNESS,_VARYINGNAME_,BaseDiffuseRoughness) #include(_DEFINENAME_,DETAIL,_VARYINGNAME_,Detail) #include(_DEFINENAME_,AMBIENT,_VARYINGNAME_,Ambient) #include(_DEFINENAME_,OPACITY,_VARYINGNAME_,Opacity) @@ -171,10 +172,19 @@ fn main(input : VertexInputs) -> FragmentInputs { #endif #if defined(USESPHERICALFROMREFLECTIONMAP) && defined(USESPHERICALINVERTEX) - var reflectionVector: vec3f = (uniforms.reflectionMatrix * vec4f(vertexOutputs.vNormalW, 0)).xyz; + #if BASE_DIFFUSE_MODEL != BRDF_DIFFUSE_MODEL_LAMBERT + // Bend the normal towards the viewer based on the diffuse roughness + var viewDirectionW: vec3f = normalize(scene.vEyePosition.xyz - vertexOutputs.vPositionW); + var NdotV: f32 = max(dot(vertexOutputs.vNormalW, viewDirectionW), 0.0); + var roughNormal: vec3f = mix(vertexOutputs.vNormalW, viewDirectionW, (0.5 * (1.0 - NdotV)) * uniforms.baseDiffuseRoughness); + var reflectionVector: vec3f = (uniforms.reflectionMatrix * vec4f(roughNormal, 0)).xyz; + #else + var reflectionVector: vec3f = (uniforms.reflectionMatrix * vec4f(vertexOutputs.vNormalW, 0)).xyz; + #endif #ifdef REFLECTIONMAP_OPPOSITEZ reflectionVector.z *= -1.0; #endif + vertexOutputs.vEnvironmentIrradiance = computeEnvironmentIrradiance(reflectionVector); #endif #endif @@ -216,7 +226,8 @@ fn main(input : VertexInputs) -> FragmentInputs { #include[3..7] #include(_DEFINENAME_,ALBEDO,_VARYINGNAME_,Albedo,_MATRIXNAME_,albedo,_INFONAME_,AlbedoInfos.x) - #include(_DEFINENAME_,BASEWEIGHT,_VARYINGNAME_,BaseWeight,_MATRIXNAME_,baseWeight,_INFONAME_,BaseWeightInfos.x) + #include(_DEFINENAME_,BASE_WEIGHT,_VARYINGNAME_,BaseWeight,_MATRIXNAME_,baseWeight,_INFONAME_,BaseWeightInfos.x) + #include(_DEFINENAME_,BASE_DIFFUSE_ROUGHNESS,_VARYINGNAME_,BaseDiffuseRoughness,_MATRIXNAME_,baseDiffuseRoughness,_INFONAME_,BaseDiffuseRoughnessInfos.x) #include(_DEFINENAME_,DETAIL,_VARYINGNAME_,Detail,_MATRIXNAME_,detail,_INFONAME_,DetailInfos.x) #include(_DEFINENAME_,AMBIENT,_VARYINGNAME_,Ambient,_MATRIXNAME_,ambient,_INFONAME_,AmbientInfos.x) #include(_DEFINENAME_,OPACITY,_VARYINGNAME_,Opacity,_MATRIXNAME_,opacity,_INFONAME_,OpacityInfos.x) diff --git a/packages/dev/inspector/src/components/actionTabs/tabs/propertyGrids/materials/pbrMaterialPropertyGridComponent.tsx b/packages/dev/inspector/src/components/actionTabs/tabs/propertyGrids/materials/pbrMaterialPropertyGridComponent.tsx index 140180e670d..c5882e7afd2 100644 --- a/packages/dev/inspector/src/components/actionTabs/tabs/propertyGrids/materials/pbrMaterialPropertyGridComponent.tsx +++ b/packages/dev/inspector/src/components/actionTabs/tabs/propertyGrids/materials/pbrMaterialPropertyGridComponent.tsx @@ -62,6 +62,14 @@ export class PBRMaterialPropertyGridComponent extends React.Component + + + extensionStates["KHR_materials_dispersion"].enabled} onSelect={(value) => (extensionStates["KHR_materials_dispersion"].enabled = value)} /> + extensionStates["EXT_materials_diffuse_roughness"].enabled} + onSelect={(value) => (extensionStates["EXT_materials_diffuse_roughness"].enabled = value)} + /> extensionStates["KHR_mesh_quantization"].enabled} diff --git a/packages/dev/inspector/src/components/globalState.ts b/packages/dev/inspector/src/components/globalState.ts index cc54db2f61c..b664b7edc02 100644 --- a/packages/dev/inspector/src/components/globalState.ts +++ b/packages/dev/inspector/src/components/globalState.ts @@ -64,6 +64,7 @@ export class GlobalState { EXT_mesh_gpu_instancing: { enabled: true }, EXT_texture_webp: { enabled: true }, EXT_texture_avif: { enabled: true }, + EXT_materials_diffuse_roughness: { enabled: true }, }; public glTFLoaderOverrideConfig = false; diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/EXT_materials_diffuse_roughness.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/EXT_materials_diffuse_roughness.ts new file mode 100644 index 00000000000..989a6062bc7 --- /dev/null +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/EXT_materials_diffuse_roughness.ts @@ -0,0 +1,102 @@ +import type { Nullable } from "core/types"; +import { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { Material } from "core/Materials/material"; + +import type { IMaterial } from "../glTFLoaderInterfaces"; +import type { IGLTFLoaderExtension } from "../glTFLoaderExtension"; +import { GLTFLoader } from "../glTFLoader"; +import type { IEXTMaterialsDiffuseRoughness } from "babylonjs-gltf2interface"; +import { registerGLTFExtension, unregisterGLTFExtension } from "../glTFLoaderExtensionRegistry"; +import { Constants } from "core/Engines/constants"; + +const NAME = "EXT_materials_diffuse_roughness"; + +declare module "../../glTFFileLoader" { + // eslint-disable-next-line jsdoc/require-jsdoc + export interface GLTFLoaderExtensionOptions { + /** + * Defines options for the EXT_materials_diffuse_roughness extension. + */ + // NOTE: Don't use NAME here as it will break the UMD type declarations. + ["EXT_materials_diffuse_roughness"]: {}; + } +} + +/** + * [Specification](https://github.com/KhronosGroup/glTF/blob/fdee35425ae560ea378092e38977216d63a094ec/extensions/2.0/Khronos/EXT_materials_diffuse_roughness/README.md) + * @experimental + */ +// eslint-disable-next-line @typescript-eslint/naming-convention +export class EXT_materials_diffuse_roughness implements IGLTFLoaderExtension { + /** + * The name of this extension. + */ + public readonly name = NAME; + + /** + * Defines whether this extension is enabled. + */ + public enabled: boolean; + + /** + * Defines a number that determines the order the extensions are applied. + */ + public order = 190; + + private _loader: GLTFLoader; + + /** + * @internal + */ + constructor(loader: GLTFLoader) { + this._loader = loader; + this.enabled = this._loader.isExtensionUsed(NAME); + } + + /** @internal */ + public dispose() { + (this._loader as any) = null; + } + + /** + * @internal + */ + public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material): Nullable> { + return GLTFLoader.LoadExtensionAsync(context, material, this.name, (extensionContext, extension) => { + const promises = new Array>(); + promises.push(this._loader.loadMaterialPropertiesAsync(context, material, babylonMaterial)); + promises.push(this._loadDiffuseRoughnessPropertiesAsync(extensionContext, extension, babylonMaterial)); + return Promise.all(promises).then(() => {}); + }); + } + + private _loadDiffuseRoughnessPropertiesAsync(context: string, properties: IEXTMaterialsDiffuseRoughness, babylonMaterial: Material): Promise { + if (!(babylonMaterial instanceof PBRMaterial)) { + throw new Error(`${context}: Material type not supported`); + } + + const promises = new Array>(); + + babylonMaterial.baseDiffuseModel = Constants.MATERIAL_DIFFUSE_MODEL_E_OREN_NAYAR; + + if (properties.diffuseRoughnessFactor != undefined) { + babylonMaterial.baseDiffuseRoughness = properties.diffuseRoughnessFactor; + } else { + babylonMaterial.baseDiffuseRoughness = 0; + } + + if (properties.diffuseRoughnessTexture) { + promises.push( + this._loader.loadTextureInfoAsync(`${context}/diffuseRoughnessTexture`, properties.diffuseRoughnessTexture, (texture) => { + texture.name = `${babylonMaterial.name} (Diffuse Roughness)`; + babylonMaterial.baseDiffuseRoughnessTexture = texture; + }) + ); + } + + return Promise.all(promises).then(() => {}); + } +} + +unregisterGLTFExtension(NAME); +registerGLTFExtension(NAME, true, (loader) => new EXT_materials_diffuse_roughness(loader)); diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/dynamic.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/dynamic.ts index f222d9cc1de..b10a6fde5b2 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/dynamic.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/dynamic.ts @@ -71,6 +71,11 @@ export function registerBuiltInGLTFExtensions() { return new KHR_materials_clearcoat(loader); }); + registerGLTFExtension("EXT_materials_diffuse_roughness", true, async (loader) => { + const { EXT_materials_diffuse_roughness } = await import("./EXT_materials_diffuse_roughness"); + return new EXT_materials_diffuse_roughness(loader); + }); + registerGLTFExtension("KHR_materials_diffuse_transmission", true, async (loader) => { const { KHR_materials_diffuse_transmission } = await import("./KHR_materials_diffuse_transmission"); return new KHR_materials_diffuse_transmission(loader); diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/index.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/index.ts index f4b098f37db..fdf17731d54 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/index.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/index.ts @@ -21,6 +21,7 @@ export * from "./KHR_materials_transmission"; export * from "./KHR_materials_diffuse_transmission"; export * from "./KHR_materials_volume"; export * from "./KHR_materials_dispersion"; +export * from "./EXT_materials_diffuse_roughness"; export * from "./KHR_mesh_quantization"; export * from "./KHR_texture_basisu"; export * from "./KHR_texture_transform"; diff --git a/packages/dev/serializers/src/glTF/2.0/Extensions/EXT_materials_diffuse_roughness.ts b/packages/dev/serializers/src/glTF/2.0/Extensions/EXT_materials_diffuse_roughness.ts new file mode 100644 index 00000000000..d874bf18b03 --- /dev/null +++ b/packages/dev/serializers/src/glTF/2.0/Extensions/EXT_materials_diffuse_roughness.ts @@ -0,0 +1,83 @@ +import type { IMaterial, IEXTMaterialsDiffuseRoughness } from "babylonjs-gltf2interface"; +import type { IGLTFExporterExtensionV2 } from "../glTFExporterExtension"; +import { GLTFExporter } from "../glTFExporter"; +import type { Material } from "core/Materials/material"; +import { PBRBaseMaterial } from "core/Materials/PBR/pbrBaseMaterial"; +import type { BaseTexture } from "core/Materials/Textures/baseTexture"; + +const NAME = "EXT_materials_diffuse_roughness"; + +/** + * @internal + */ +// eslint-disable-next-line @typescript-eslint/naming-convention +export class EXT_materials_diffuse_roughness implements IGLTFExporterExtensionV2 { + /** Name of this extension */ + public readonly name = NAME; + + /** Defines whether this extension is enabled */ + public enabled = true; + + /** Defines whether this extension is required */ + public required = false; + + private _exporter: GLTFExporter; + + private _wasUsed = false; + + constructor(exporter: GLTFExporter) { + this._exporter = exporter; + } + + public dispose() {} + + /** @internal */ + public get wasUsed() { + return this._wasUsed; + } + + public postExportMaterialAdditionalTextures?(context: string, node: IMaterial, babylonMaterial: Material): BaseTexture[] { + const additionalTextures: BaseTexture[] = []; + if (babylonMaterial instanceof PBRBaseMaterial) { + if (babylonMaterial._baseDiffuseRoughness) { + if (babylonMaterial._baseDiffuseRoughnessTexture) { + additionalTextures.push(babylonMaterial._baseDiffuseRoughnessTexture); + } + return additionalTextures; + } + } + + return []; + } + + public postExportMaterialAsync?(context: string, node: IMaterial, babylonMaterial: Material): Promise { + return new Promise((resolve) => { + if (babylonMaterial instanceof PBRBaseMaterial) { + if (!babylonMaterial._baseDiffuseRoughness) { + resolve(node); + return; + } + + this._wasUsed = true; + + node.extensions = node.extensions || {}; + + const diffuseRoughnessTextureInfo = this._exporter._materialExporter.getTextureInfo(babylonMaterial._baseDiffuseRoughnessTexture); + + const diffuseRoughnessInfo: IEXTMaterialsDiffuseRoughness = { + diffuseRoughnessFactor: babylonMaterial._baseDiffuseRoughness, + diffuseRoughnessTexture: diffuseRoughnessTextureInfo ?? undefined, + }; + + if (diffuseRoughnessInfo.diffuseRoughnessTexture !== null) { + this._exporter._materialNeedsUVsSet.add(babylonMaterial); + } + + node.extensions[NAME] = diffuseRoughnessInfo; + } + resolve(node); + }); + } +} + +GLTFExporter.RegisterExtension(NAME, (exporter) => new EXT_materials_diffuse_roughness(exporter)); diff --git a/packages/dev/serializers/src/glTF/2.0/Extensions/index.ts b/packages/dev/serializers/src/glTF/2.0/Extensions/index.ts index b6faee955af..d2b5adfee31 100644 --- a/packages/dev/serializers/src/glTF/2.0/Extensions/index.ts +++ b/packages/dev/serializers/src/glTF/2.0/Extensions/index.ts @@ -13,4 +13,5 @@ export * from "./KHR_materials_specular"; export * from "./KHR_materials_transmission"; export * from "./KHR_materials_unlit"; export * from "./KHR_materials_volume"; +export * from "./EXT_materials_diffuse_roughness"; export * from "./KHR_texture_transform"; diff --git a/packages/public/glTF2Interface/babylon.glTF2Interface.d.ts b/packages/public/glTF2Interface/babylon.glTF2Interface.d.ts index 9f17d09ce3d..34a0e4ceef9 100644 --- a/packages/public/glTF2Interface/babylon.glTF2Interface.d.ts +++ b/packages/public/glTF2Interface/babylon.glTF2Interface.d.ts @@ -1058,6 +1058,9 @@ declare module BABYLON.GLTF2 { lights: IKHRLightsPunctual_Light[]; } + /** + * Interfaces from the KHR_materials_clearcoat extension + */ /** @internal */ interface IKHRMaterialsClearcoat { clearcoatFactor?: number; @@ -1183,6 +1186,15 @@ declare module BABYLON.GLTF2 { diffuseTransmissionColorTexture?: ITextureInfo; } + /** + * Interfaces from the EXT_materials_diffuse_roughness extension + */ + /** @internal */ + interface IEXTMaterialsDiffuseRoughness { + diffuseRoughnessFactor?: number; + diffuseRoughnessTexture?: ITextureInfo; + } + /** * Interfaces from the KHR_materials_variants extension */ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/iesprofile2.png b/packages/tools/tests/test/visualization/ReferenceImages/iesprofile2.png index ab99931ac7b..b0865a558ea 100644 Binary files a/packages/tools/tests/test/visualization/ReferenceImages/iesprofile2.png and b/packages/tools/tests/test/visualization/ReferenceImages/iesprofile2.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/normals.png b/packages/tools/tests/test/visualization/ReferenceImages/normals.png index e5731b8fbcf..c14089f9eef 100644 Binary files a/packages/tools/tests/test/visualization/ReferenceImages/normals.png and b/packages/tools/tests/test/visualization/ReferenceImages/normals.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/openpbr-base-diffuse-roughness-prefiltered-ibl-with-cdf.png b/packages/tools/tests/test/visualization/ReferenceImages/openpbr-base-diffuse-roughness-prefiltered-ibl-with-cdf.png new file mode 100644 index 00000000000..5874a207279 Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/openpbr-base-diffuse-roughness-prefiltered-ibl-with-cdf.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/openpbr-base-diffuse-roughness-prefiltered-ibl.png b/packages/tools/tests/test/visualization/ReferenceImages/openpbr-base-diffuse-roughness-prefiltered-ibl.png new file mode 100644 index 00000000000..66a5dcef4e4 Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/openpbr-base-diffuse-roughness-prefiltered-ibl.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/openpbr-base-diffuse-roughness-realtime-ibl.png b/packages/tools/tests/test/visualization/ReferenceImages/openpbr-base-diffuse-roughness-realtime-ibl.png new file mode 100644 index 00000000000..e2aaa629312 Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/openpbr-base-diffuse-roughness-realtime-ibl.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/openpbr-base-diffuse-roughness-spherical-harmonics.png b/packages/tools/tests/test/visualization/ReferenceImages/openpbr-base-diffuse-roughness-spherical-harmonics.png new file mode 100644 index 00000000000..9eb8e4112e3 Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/openpbr-base-diffuse-roughness-spherical-harmonics.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/openpbr-base-diffuse-roughness.png b/packages/tools/tests/test/visualization/ReferenceImages/openpbr-base-diffuse-roughness.png new file mode 100644 index 00000000000..f12bab7d72d Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/openpbr-base-diffuse-roughness.png differ diff --git a/packages/tools/tests/test/visualization/config.json b/packages/tools/tests/test/visualization/config.json index b9ae2bbb8af..32c4c248636 100644 --- a/packages/tools/tests/test/visualization/config.json +++ b/packages/tools/tests/test/visualization/config.json @@ -11,6 +11,35 @@ "playgroundId": "#DT1XPP#4", "referenceImage": "openpbr_base_weight.png" }, + { + "title": "OpenPBR Base Diffuse Roughness Analytical Lights", + "playgroundId": "#MXACV7#23", + "referenceImage": "openpbr_base_diffuse_roughness.png" + }, + { + "title": "OpenPBR Base Diffuse Roughness Realtime IBL", + "playgroundId": "#MXACV7#22", + "renderCount": 15, + "referenceImage": "openpbr_base_diffuse_roughness_realtime_ibl.png" + }, + { + "title": "OpenPBR Base Diffuse Roughness Prefiltered IBL with CDF", + "playgroundId": "#MXACV7#19", + "renderCount": 5, + "referenceImage": "openpbr_base_diffuse_roughness_prefiltered_ibl_with_cdf.png" + }, + { + "title": "OpenPBR Base Diffuse Roughness Prefiltered IBL", + "playgroundId": "#MXACV7#20", + "renderCount": 5, + "referenceImage": "openpbr_base_diffuse_roughness_prefiltered_ibl.png" + }, + { + "title": "OpenPBR Base Diffuse Roughness SH IBL", + "playgroundId": "#MXACV7#21", + "renderCount": 2, + "referenceImage": "openpbr_base_diffuse_roughness_spherical_harmonics.png" + }, { "title": "NME Glow Manual", "playgroundId": "#7QCYPB#320",