diff --git a/packages/dev/buildTools/src/addJSToCompiledFiles.ts b/packages/dev/buildTools/src/addJSToCompiledFiles.ts index 6e55dbb546a..686a7ef5383 100644 --- a/packages/dev/buildTools/src/addJSToCompiledFiles.ts +++ b/packages/dev/buildTools/src/addJSToCompiledFiles.ts @@ -8,6 +8,8 @@ function ProcessSource(sourceCode: string, forceMJS: boolean) { const extension = forceMJS ? ".mjs" : ".js"; return ( sourceCode + // replace imports from directories with index.js (mixins are generating them) + .replace(/import\("([./]+)"\)/g, `import("$1/index${extension}")`) // replace imports and exports with js extensions .replace(/((import|export).*["'](@babylonjs\/.*\/|\.{1,2}\/)((?!\.scss|\.svg|\.png|\.jpg).)*?)("|');/g, `$1${extension}$5;`) .replace(/((import|export)\(["']((@babylonjs\/.*\/|\.{1,2}\/)((?!\.scss|\.svg|\.png|\.jpg).)*?))(["'])\)/g, `$1${extension}$6)`) diff --git a/packages/dev/buildTools/src/generateDeclaration.ts b/packages/dev/buildTools/src/generateDeclaration.ts index 4d17d0a3849..162e73a9374 100644 --- a/packages/dev/buildTools/src/generateDeclaration.ts +++ b/packages/dev/buildTools/src/generateDeclaration.ts @@ -76,6 +76,9 @@ function GetModuleDeclaration( line = line.startsWith(" ") ? " //" + line.substring(3) : "// " + line; } + // replace type imports from directories with index (mixins are generating them) + line = line.replace(/import\("([./]+)"\)/g, `import("$1/index")`); + [ // Declaration /declare module ['"](.*)['"]/, @@ -85,7 +88,7 @@ function GetModuleDeclaration( / {4}module ['"](.*)['"]/, /^module ['"](\..*)['"]/, // Inlined Import - /import\(['"](.*)['"]/, + /import\(['"]([^'"]*)['"]/, // Side Effect Import /import ['"](.*)['"]/, ].forEach((regex) => { @@ -93,7 +96,9 @@ function GetModuleDeclaration( if (match) { if (match[1][0] === ".") { const newLocation = path.join(sourceDir, match[1]).replace(/\\/g, "/"); - line = line.replace(match[1], newLocation); + // replaceAll only avaialable by modifying the typescript lib + // which we prefered to not change for now + line = (line as any).replaceAll(match[1], newLocation); } else { let found = false; Object.keys(mapping).forEach((devPackageName) => { @@ -319,8 +324,8 @@ function GetPackageDeclaration( while (i < lines.length) { let line = lines[i]; - if (/import\("\.(.*)\)./g.test(line) && !/^declare type (.*) import/g.test(line)) { - line = line.replace(/import\((.*)\)./, ""); + if (/import\("\.([^)]*)\)./g.test(line) && !/^declare type (.*) import/g.test(line)) { + line = line.replace(/import\(([^)]*)\)./g, ""); } if (!line.includes("const enum") && !line.includes("=")) { diff --git a/packages/dev/core/src/Materials/Background/backgroundMaterial.ts b/packages/dev/core/src/Materials/Background/backgroundMaterial.ts index 8caad63d4e0..540ea120722 100644 --- a/packages/dev/core/src/Materials/Background/backgroundMaterial.ts +++ b/packages/dev/core/src/Materials/Background/backgroundMaterial.ts @@ -1,7 +1,6 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import { serialize, serializeAsColor3, expandToProperty, serializeAsTexture, serializeAsVector3, serializeAsImageProcessingConfiguration } from "../../Misc/decorators"; +import { serialize, serializeAsColor3, expandToProperty, serializeAsTexture, serializeAsVector3 } from "../../Misc/decorators"; import { SmartArray } from "../../Misc/smartArray"; -import type { Observer } from "../../Misc/observable"; import { Logger } from "../../Misc/logger"; import type { Nullable, int, float } from "../../types"; import type { Scene } from "../../scene"; @@ -14,11 +13,9 @@ import type { Mesh } from "../../Meshes/mesh"; import type { IEffectCreationOptions } from "../../Materials/effect"; import { MaterialDefines } from "../../Materials/materialDefines"; import { PushMaterial } from "../../Materials/pushMaterial"; -import type { ColorCurves } from "../../Materials/colorCurves"; -import type { IImageProcessingConfigurationDefines } from "../../Materials/imageProcessingConfiguration.defines"; +import { ImageProcessingDefinesMixin } from "../../Materials/imageProcessingConfiguration.defines"; import { ImageProcessingConfiguration } from "../../Materials/imageProcessingConfiguration"; import type { BaseTexture } from "../../Materials/Textures/baseTexture"; -import { Texture } from "../../Materials/Textures/texture"; import type { RenderTargetTexture } from "../../Materials/Textures/renderTargetTexture"; import type { IShadowLight } from "../../Lights/shadowLight"; import { Constants } from "../../Engines/constants"; @@ -34,25 +31,33 @@ import { BindLights, BindLogDepth, BindTextureMatrix, + BindIBLParameters, + BindIBLSamplers, HandleFallbacksForShadows, PrepareAttributesForBones, PrepareAttributesForInstances, PrepareDefinesForAttributes, PrepareDefinesForFrameBoundValues, PrepareDefinesForLights, + PrepareDefinesForIBL, PrepareDefinesForMergedUV, PrepareDefinesForMisc, PrepareDefinesForMultiview, PrepareUniformsAndSamplersList, + PrepareUniformsAndSamplersForIBL, + PrepareUniformLayoutForIBL, } from "../materialHelper.functions"; import { SerializationHelper } from "../../Misc/decorators.serialization"; import { ShaderLanguage } from "../shaderLanguage"; +import { ImageProcessingMixin } from "../imageProcessing"; + +class BackgroundMaterialDefinesBase extends MaterialDefines {} /** * Background material defines definition. * @internal Mainly internal Use */ -class BackgroundMaterialDefines extends MaterialDefines implements IImageProcessingConfigurationDefines { +class BackgroundMaterialDefines extends ImageProcessingDefinesMixin(BackgroundMaterialDefinesBase) { /** * True if the diffuse texture is in use. */ @@ -135,23 +140,6 @@ class BackgroundMaterialDefines extends MaterialDefines implements IImageProcess */ public PROJECTED_GROUND = false; - public IMAGEPROCESSING = false; - public VIGNETTE = false; - public VIGNETTEBLENDMODEMULTIPLY = false; - public VIGNETTEBLENDMODEOPAQUE = false; - public TONEMAPPING = 0; - public CONTRAST = false; - public COLORCURVES = false; - public COLORGRADING = false; - public COLORGRADING3D = false; - public SAMPLER3DGREENDEPTH = false; - public SAMPLER3DBGRMAP = false; - public DITHER = false; - public IMAGEPROCESSINGPOSTPROCESS = false; - public SKIPFINALCOLORCLAMP = false; - public EXPOSURE = false; - public MULTIVIEW = false; - // Reflection. public REFLECTION = false; public REFLECTIONMAP_3D = false; @@ -202,11 +190,12 @@ class BackgroundMaterialDefines extends MaterialDefines implements IImageProcess } } +class BackgroundMaterialBase extends ImageProcessingMixin(PushMaterial) {} /** * Background material used to create an efficient environment around your scene. * #157MGZ: simple test */ -export class BackgroundMaterial extends PushMaterial { +export class BackgroundMaterial extends BackgroundMaterialBase { /** * Standard reflectance value at parallel view angle. */ @@ -454,168 +443,6 @@ export class BackgroundMaterial extends PushMaterial { @expandToProperty("_markAllSubMeshesAsLightsDirty") public shadowOnly: boolean = false; - /** - * Default configuration related to image processing available in the Background Material. - */ - @serializeAsImageProcessingConfiguration() - protected _imageProcessingConfiguration: ImageProcessingConfiguration; - - /** - * Keep track of the image processing observer to allow dispose and replace. - */ - private _imageProcessingObserver: Nullable> = null; - - /** - * Attaches a new image processing configuration to the PBR Material. - * @param configuration (if null the scene configuration will be use) - */ - protected _attachImageProcessingConfiguration(configuration: Nullable): void { - if (configuration === this._imageProcessingConfiguration) { - return; - } - - // Detaches observer. - if (this._imageProcessingConfiguration && this._imageProcessingObserver) { - this._imageProcessingConfiguration.onUpdateParameters.remove(this._imageProcessingObserver); - } - - // Pick the scene configuration if needed. - if (!configuration) { - this._imageProcessingConfiguration = this.getScene().imageProcessingConfiguration; - } else { - this._imageProcessingConfiguration = configuration; - } - - // Attaches observer. - if (this._imageProcessingConfiguration) { - this._imageProcessingObserver = this._imageProcessingConfiguration.onUpdateParameters.add(() => { - this._computePrimaryColorFromPerceptualColor(); - this._markAllSubMeshesAsImageProcessingDirty(); - }); - } - } - - /** - * Gets the image processing configuration used either in this material. - */ - public get imageProcessingConfiguration(): Nullable { - return this._imageProcessingConfiguration; - } - - /** - * Sets the Default image processing configuration used either in the this material. - * - * If sets to null, the scene one is in use. - */ - public set imageProcessingConfiguration(value: Nullable) { - this._attachImageProcessingConfiguration(value); - - // Ensure the effect will be rebuilt. - this._markAllSubMeshesAsTexturesDirty(); - } - - /** - * Gets whether the color curves effect is enabled. - */ - public get cameraColorCurvesEnabled(): boolean { - return (this.imageProcessingConfiguration).colorCurvesEnabled; - } - /** - * Sets whether the color curves effect is enabled. - */ - public set cameraColorCurvesEnabled(value: boolean) { - (this.imageProcessingConfiguration).colorCurvesEnabled = value; - } - - /** - * Gets whether the color grading effect is enabled. - */ - public get cameraColorGradingEnabled(): boolean { - return (this.imageProcessingConfiguration).colorGradingEnabled; - } - /** - * Gets whether the color grading effect is enabled. - */ - public set cameraColorGradingEnabled(value: boolean) { - (this.imageProcessingConfiguration).colorGradingEnabled = value; - } - - /** - * Gets whether tonemapping is enabled or not. - */ - public get cameraToneMappingEnabled(): boolean { - return this._imageProcessingConfiguration.toneMappingEnabled; - } - /** - * Sets whether tonemapping is enabled or not - */ - public set cameraToneMappingEnabled(value: boolean) { - this._imageProcessingConfiguration.toneMappingEnabled = value; - } - - /** - * The camera exposure used on this material. - * This property is here and not in the camera to allow controlling exposure without full screen post process. - * This corresponds to a photographic exposure. - */ - public get cameraExposure(): float { - return this._imageProcessingConfiguration.exposure; - } - /** - * The camera exposure used on this material. - * This property is here and not in the camera to allow controlling exposure without full screen post process. - * This corresponds to a photographic exposure. - */ - public set cameraExposure(value: float) { - this._imageProcessingConfiguration.exposure = value; - } - - /** - * Gets The camera contrast used on this material. - */ - public get cameraContrast(): float { - return this._imageProcessingConfiguration.contrast; - } - - /** - * Sets The camera contrast used on this material. - */ - public set cameraContrast(value: float) { - this._imageProcessingConfiguration.contrast = value; - } - - /** - * Gets the Color Grading 2D Lookup Texture. - */ - public get cameraColorGradingTexture(): Nullable { - return this._imageProcessingConfiguration.colorGradingTexture; - } - /** - * Sets the Color Grading 2D Lookup Texture. - */ - public set cameraColorGradingTexture(value: Nullable) { - (this.imageProcessingConfiguration).colorGradingTexture = value; - } - - /** - * The color grading curves provide additional color adjustment that is applied after any color grading transform (3D LUT). - * They allow basic adjustment of saturation and small exposure adjustments, along with color filter tinting to provide white balance adjustment or more stylistic effects. - * These are similar to controls found in many professional imaging or colorist software. The global controls are applied to the entire image. For advanced tuning, extra controls are provided to adjust the shadow, midtone and highlight areas of the image; - * corresponding to low luminance, medium luminance, and high luminance areas respectively. - */ - public get cameraColorCurves(): Nullable { - return (this.imageProcessingConfiguration).colorCurves; - } - /** - * The color grading curves provide additional color adjustment that is applied after any color grading transform (3D LUT). - * They allow basic adjustment of saturation and small exposure adjustments, along with color filter tinting to provide white balance adjustment or more stylistic effects. - * These are similar to controls found in many professional imaging or colorist software. The global controls are applied to the entire image. For advanced tuning, extra controls are provided to adjust the shadow, midtone and highlight areas of the image; - * corresponding to low luminance, medium luminance, and high luminance areas respectively. - */ - public set cameraColorCurves(value: Nullable) { - (this.imageProcessingConfiguration).colorCurves = value; - } - /** * Due to a bug in iOS10, video tags (which are using the background material) are in BGR and not RGB. * Setting this flag to true (not done automatically!) will convert it back to RGB. @@ -774,57 +601,14 @@ export class BackgroundMaterial extends PushMaterial { } const reflectionTexture = this._reflectionTexture; + PrepareDefinesForIBL(scene, reflectionTexture, defines); if (reflectionTexture && MaterialFlags.ReflectionTextureEnabled) { if (!reflectionTexture.isReadyOrNotBlocking()) { return false; } - - defines.REFLECTION = true; - defines.GAMMAREFLECTION = reflectionTexture.gammaSpace; - defines.RGBDREFLECTION = reflectionTexture.isRGBD; - defines.REFLECTIONBLUR = this._reflectionBlur > 0; - defines.LODINREFLECTIONALPHA = reflectionTexture.lodLevelInAlpha; defines.EQUIRECTANGULAR_RELFECTION_FOV = this.useEquirectangularFOV; defines.REFLECTIONBGR = this.switchToBGR; - - if (reflectionTexture.coordinatesMode === Texture.INVCUBIC_MODE) { - defines.INVERTCUBICMAP = true; - } - - defines.REFLECTIONMAP_3D = reflectionTexture.isCube; - defines.REFLECTIONMAP_OPPOSITEZ = defines.REFLECTIONMAP_3D && this.getScene().useRightHandedSystem ? !reflectionTexture.invertZ : reflectionTexture.invertZ; - - switch (reflectionTexture.coordinatesMode) { - case Texture.EXPLICIT_MODE: - defines.REFLECTIONMAP_EXPLICIT = true; - break; - case Texture.PLANAR_MODE: - defines.REFLECTIONMAP_PLANAR = true; - break; - case Texture.PROJECTION_MODE: - defines.REFLECTIONMAP_PROJECTION = true; - break; - case Texture.SKYBOX_MODE: - defines.REFLECTIONMAP_SKYBOX = true; - break; - case Texture.SPHERICAL_MODE: - defines.REFLECTIONMAP_SPHERICAL = true; - break; - case Texture.EQUIRECTANGULAR_MODE: - defines.REFLECTIONMAP_EQUIRECTANGULAR = true; - break; - case Texture.FIXED_EQUIRECTANGULAR_MODE: - defines.REFLECTIONMAP_EQUIRECTANGULAR_FIXED = true; - break; - case Texture.FIXED_EQUIRECTANGULAR_MIRRORED_MODE: - defines.REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED = true; - break; - case Texture.CUBIC_MODE: - case Texture.INVCUBIC_MODE: - default: - defines.REFLECTIONMAP_CUBIC = true; - break; - } + defines.REFLECTIONBLUR = this._reflectionBlur > 0; if (this.reflectionFresnel) { defines.REFLECTIONFRESNEL = true; @@ -839,25 +623,9 @@ export class BackgroundMaterial extends PushMaterial { defines.REFLECTIONFALLOFF = false; } } else { - defines.REFLECTION = false; defines.REFLECTIONFRESNEL = false; defines.REFLECTIONFALLOFF = false; defines.REFLECTIONBLUR = false; - defines.REFLECTIONMAP_3D = false; - defines.REFLECTIONMAP_SPHERICAL = false; - defines.REFLECTIONMAP_PLANAR = false; - defines.REFLECTIONMAP_CUBIC = false; - defines.REFLECTIONMAP_PROJECTION = false; - defines.REFLECTIONMAP_SKYBOX = false; - defines.REFLECTIONMAP_EXPLICIT = false; - defines.REFLECTIONMAP_EQUIRECTANGULAR = false; - defines.REFLECTIONMAP_EQUIRECTANGULAR_FIXED = false; - defines.REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED = false; - defines.INVERTCUBICMAP = false; - defines.REFLECTIONMAP_OPPOSITEZ = false; - defines.LODINREFLECTIONALPHA = false; - defines.GAMMAREFLECTION = false; - defines.RGBDREFLECTION = false; } } @@ -956,9 +724,6 @@ export class BackgroundMaterial extends PushMaterial { "vPrimaryColor", "vPrimaryColorShadow", - "vReflectionInfos", - "reflectionMatrix", - "vReflectionMicrosurfaceInfos", "fFovMultiplier", "shadowLevel", @@ -975,7 +740,8 @@ export class BackgroundMaterial extends PushMaterial { ]; AddClipPlaneUniforms(uniforms); - const samplers = ["diffuseSampler", "reflectionSampler", "reflectionSamplerLow", "reflectionSamplerHigh"]; + const samplers = ["diffuseSampler"]; + PrepareUniformsAndSamplersForIBL(uniforms, samplers, false); const uniformBuffers = ["Material", "Scene"]; if (ImageProcessingConfiguration) { @@ -1085,10 +851,7 @@ export class BackgroundMaterial extends PushMaterial { this._uniformBuffer.addUniform("vPrimaryColor", 4); this._uniformBuffer.addUniform("vPrimaryColorShadow", 4); this._uniformBuffer.addUniform("vDiffuseInfos", 2); - this._uniformBuffer.addUniform("vReflectionInfos", 2); this._uniformBuffer.addUniform("diffuseMatrix", 16); - this._uniformBuffer.addUniform("reflectionMatrix", 16); - this._uniformBuffer.addUniform("vReflectionMicrosurfaceInfos", 3); this._uniformBuffer.addUniform("fFovMultiplier", 1); this._uniformBuffer.addUniform("pointSize", 1); this._uniformBuffer.addUniform("shadowLevel", 1); @@ -1097,6 +860,8 @@ export class BackgroundMaterial extends PushMaterial { this._uniformBuffer.addUniform("vReflectionControl", 4); this._uniformBuffer.addUniform("projectedGroundInfos", 2); + PrepareUniformLayoutForIBL(this._uniformBuffer, true, false, false); + this._uniformBuffer.create(); } @@ -1164,17 +929,7 @@ export class BackgroundMaterial extends PushMaterial { BindTextureMatrix(this._diffuseTexture, this._uniformBuffer, "diffuse"); } - if (reflectionTexture && MaterialFlags.ReflectionTextureEnabled) { - this._uniformBuffer.updateMatrix("reflectionMatrix", reflectionTexture.getReflectionTextureMatrix()); - this._uniformBuffer.updateFloat2("vReflectionInfos", reflectionTexture.level, this._reflectionBlur); - - this._uniformBuffer.updateFloat3( - "vReflectionMicrosurfaceInfos", - reflectionTexture.getSize().width, - reflectionTexture.lodGenerationScale, - reflectionTexture.lodGenerationOffset - ); - } + BindIBLParameters(scene, defines, this._uniformBuffer, reflectionTexture, false, true); } if (this.shadowLevel > 0) { @@ -1204,15 +959,7 @@ export class BackgroundMaterial extends PushMaterial { } if (reflectionTexture && MaterialFlags.ReflectionTextureEnabled) { - if (defines.REFLECTIONBLUR && defines.TEXTURELODSUPPORT) { - this._uniformBuffer.setTexture("reflectionSampler", reflectionTexture); - } else if (!defines.REFLECTIONBLUR) { - this._uniformBuffer.setTexture("reflectionSampler", reflectionTexture); - } else { - this._uniformBuffer.setTexture("reflectionSampler", reflectionTexture._lodTextureMid || reflectionTexture); - this._uniformBuffer.setTexture("reflectionSamplerLow", reflectionTexture._lodTextureLow || reflectionTexture); - this._uniformBuffer.setTexture("reflectionSamplerHigh", reflectionTexture._lodTextureHigh || reflectionTexture); - } + BindIBLSamplers(scene, defines, this._uniformBuffer, reflectionTexture); if (defines.REFLECTIONFRESNEL) { this._uniformBuffer.updateFloat3("vBackgroundCenter", this.sceneCenter.x, this.sceneCenter.y, this.sceneCenter.z); diff --git a/packages/dev/core/src/Materials/Node/nodeMaterial.ts b/packages/dev/core/src/Materials/Node/nodeMaterial.ts index f51b7058009..3c699c44048 100644 --- a/packages/dev/core/src/Materials/Node/nodeMaterial.ts +++ b/packages/dev/core/src/Materials/Node/nodeMaterial.ts @@ -10,14 +10,12 @@ import { NodeMaterialBuildState } from "./nodeMaterialBuildState"; import type { IEffectCreationOptions } from "../effect"; import { Effect } from "../effect"; import type { BaseTexture } from "../../Materials/Textures/baseTexture"; -import type { Observer } from "../../Misc/observable"; import { Observable } from "../../Misc/observable"; import { NodeMaterialBlockTargets } from "./Enums/nodeMaterialBlockTargets"; import { NodeMaterialBuildStateSharedData } from "./nodeMaterialBuildStateSharedData"; import type { SubMesh } from "../../Meshes/subMesh"; import { MaterialDefines } from "../../Materials/materialDefines"; import type { NodeMaterialOptimizer } from "./Optimizers/nodeMaterialOptimizer"; -import type { ImageProcessingConfiguration } from "../imageProcessingConfiguration"; import type { Nullable } from "../../types"; import { VertexBuffer } from "../../Buffers/buffer"; import { Tools } from "../../Misc/tools"; @@ -67,11 +65,13 @@ import type { NodeMaterialTeleportOutBlock } from "./Blocks/Teleport/teleportOut import type { NodeMaterialTeleportInBlock } from "./Blocks/Teleport/teleportInBlock"; import { Logger } from "core/Misc/logger"; import { PrepareDefinesForCamera, PrepareDefinesForPrePass } from "../materialHelper.functions"; -import type { IImageProcessingConfigurationDefines } from "../imageProcessingConfiguration.defines"; +import { ImageProcessingDefinesMixin } from "../imageProcessingConfiguration.defines"; import { ShaderLanguage } from "../shaderLanguage"; import { AbstractEngine } from "../../Engines/abstractEngine"; import type { LoopBlock } from "./Blocks/loopBlock"; import { MaterialHelperGeometryRendering } from "../materialHelper.geometryrendering"; +import { UVDefinesMixin } from "../uv.defines"; +import { ImageProcessingMixin } from "../imageProcessing"; const onCreatedEffectParameters = { effect: null as unknown as Effect, subMesh: null as unknown as Nullable }; @@ -91,26 +91,16 @@ export interface INodeMaterialEditorOptions { }; } +class NodeMaterialDefinesBase extends UVDefinesMixin(MaterialDefines) {} + /** @internal */ -export class NodeMaterialDefines extends MaterialDefines implements IImageProcessingConfigurationDefines { +export class NodeMaterialDefines extends ImageProcessingDefinesMixin(NodeMaterialDefinesBase) { /** Normal */ public NORMAL = false; /** Tangent */ public TANGENT = false; /** Vertex color */ public VERTEXCOLOR_NME = false; - /** Uv1 **/ - public UV1 = false; - /** Uv2 **/ - public UV2 = false; - /** Uv3 **/ - public UV3 = false; - /** Uv4 **/ - public UV4 = false; - /** Uv5 **/ - public UV5 = false; - /** Uv6 **/ - public UV6 = false; /** Prepass **/ public PREPASS = false; @@ -177,37 +167,6 @@ export class NodeMaterialDefines extends MaterialDefines implements IImageProces /** Using a texture to store morph target data */ public MORPHTARGETS_TEXTURE = false; - /** IMAGE PROCESSING */ - public IMAGEPROCESSING = false; - /** Vignette */ - public VIGNETTE = false; - /** Multiply blend mode for vignette */ - public VIGNETTEBLENDMODEMULTIPLY = false; - /** Opaque blend mode for vignette */ - public VIGNETTEBLENDMODEOPAQUE = false; - /** Tone mapping */ - public TONEMAPPING = 0; - /** Contrast */ - public CONTRAST = false; - /** Exposure */ - public EXPOSURE = false; - /** Color curves */ - public COLORCURVES = false; - /** Color grading */ - public COLORGRADING = false; - /** 3D color grading */ - public COLORGRADING3D = false; - /** Sampler green depth */ - public SAMPLER3DGREENDEPTH = false; - /** Sampler for BGR map */ - public SAMPLER3DBGRMAP = false; - /** Dithering */ - public DITHER = false; - /** Using post process for image processing */ - public IMAGEPROCESSINGPOSTPROCESS = false; - /** Skip color clamp */ - public SKIPFINALCOLORCLAMP = false; - /** MISC. */ public BUMPDIRECTUV = 0; /** Camera is orthographic */ @@ -272,10 +231,11 @@ export type NodeMaterialTextureBlocks = | BiPlanarBlock | PrePassTextureBlock; +class NodeMaterialBase extends ImageProcessingMixin(PushMaterial) {} /** * Class used to create a node based material built by assembling shader blocks */ -export class NodeMaterial extends PushMaterial { +export class NodeMaterial extends NodeMaterialBase { private static _BuildIdGenerator: number = 0; private _options: INodeMaterialOptions; private _vertexCompilationState: NodeMaterialBuildState; @@ -416,30 +376,6 @@ export class NodeMaterial extends PushMaterial { this._options = options; } - /** - * Default configuration related to image processing available in the standard Material. - */ - protected _imageProcessingConfiguration: ImageProcessingConfiguration; - - /** - * Gets the image processing configuration used either in this material. - */ - public get imageProcessingConfiguration(): ImageProcessingConfiguration { - return this._imageProcessingConfiguration; - } - - /** - * Sets the Default image processing configuration used either in the this material. - * - * If sets to null, the scene one is in use. - */ - public set imageProcessingConfiguration(value: ImageProcessingConfiguration) { - this._attachImageProcessingConfiguration(value); - - // Ensure the effect will be rebuilt. - this._markAllSubMeshesAsTexturesDirty(); - } - /** * Gets an array of blocks that needs to be serialized even if they are not yet connected */ @@ -513,40 +449,6 @@ export class NodeMaterial extends PushMaterial { return "NodeMaterial"; } - /** - * Keep track of the image processing observer to allow dispose and replace. - */ - private _imageProcessingObserver: Nullable>; - - /** - * Attaches a new image processing configuration to the Standard Material. - * @param configuration - */ - protected _attachImageProcessingConfiguration(configuration: Nullable): void { - if (configuration === this._imageProcessingConfiguration) { - return; - } - - // Detaches observer. - if (this._imageProcessingConfiguration && this._imageProcessingObserver) { - this._imageProcessingConfiguration.onUpdateParameters.remove(this._imageProcessingObserver); - } - - // Pick the scene configuration if needed. - if (!configuration) { - this._imageProcessingConfiguration = this.getScene().imageProcessingConfiguration; - } else { - this._imageProcessingConfiguration = configuration; - } - - // Attaches observer. - if (this._imageProcessingConfiguration) { - this._imageProcessingObserver = this._imageProcessingConfiguration.onUpdateParameters.add(() => { - this._markAllSubMeshesAsImageProcessingDirty(); - }); - } - } - /** * Get a block by its name * @param name defines the name of the block to retrieve diff --git a/packages/dev/core/src/Materials/PBR/index.ts b/packages/dev/core/src/Materials/PBR/index.ts index f97e5346890..1137c63f604 100644 --- a/packages/dev/core/src/Materials/PBR/index.ts +++ b/packages/dev/core/src/Materials/PBR/index.ts @@ -5,6 +5,7 @@ export * from "./pbrBRDFConfiguration"; export * from "./pbrClearCoatConfiguration"; export * from "./pbrIridescenceConfiguration"; export * from "./pbrMaterial"; +export * from "./openPbrMaterial"; export * from "./pbrMetallicRoughnessMaterial"; export * from "./pbrSpecularGlossinessMaterial"; export * from "./pbrSheenConfiguration"; @@ -15,3 +16,7 @@ export * from "../../ShadersWGSL/pbr.vertex"; export * from "../../ShadersWGSL/pbr.fragment"; export * from "../../Shaders/pbr.vertex"; export * from "../../Shaders/pbr.fragment"; +export * from "../../ShadersWGSL/openpbr.vertex"; +export * from "../../ShadersWGSL/openpbr.fragment"; +export * from "../../Shaders/openpbr.vertex"; +export * from "../../Shaders/openpbr.fragment"; diff --git a/packages/dev/core/src/Materials/PBR/openPbrMaterial.ts b/packages/dev/core/src/Materials/PBR/openPbrMaterial.ts new file mode 100644 index 00000000000..b61e8aa0f0d --- /dev/null +++ b/packages/dev/core/src/Materials/PBR/openPbrMaterial.ts @@ -0,0 +1,2407 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { serialize, expandToProperty, addAccessorsForMaterialProperty } from "../../Misc/decorators"; +import { GetEnvironmentBRDFTexture } from "../../Misc/brdfTextureTools"; +import type { Nullable } from "../../types"; +import { Scene } from "../../scene"; +import { Color3, Color4 } from "../../Maths/math.color"; +import { ImageProcessingConfiguration } from "../imageProcessingConfiguration"; +import type { BaseTexture } from "../../Materials/Textures/baseTexture"; +import { PBRBaseMaterial } from "./pbrBaseMaterial"; +import { RegisterClass } from "../../Misc/typeStore"; +import { Material } from "../material"; +import { SerializationHelper } from "../../Misc/decorators.serialization"; +import type { Engine } from "../../Engines/engine"; +import type { AbstractMesh } from "../../Meshes/abstractMesh"; +import type { Effect, IEffectCreationOptions } from "../../Materials/effect"; +import { MaterialDefines } from "../materialDefines"; +import { ImageProcessingDefinesMixin } from "../imageProcessingConfiguration.defines"; +import { EffectFallbacks } from "../effectFallbacks"; +import { AddClipPlaneUniforms, BindClipPlane } from "../clipPlaneMaterialHelper"; +import { + BindBonesParameters, + BindFogParameters, + BindLights, + BindLogDepth, + BindMorphTargetParameters, + BindTextureMatrix, + BindIBLParameters, + BindIBLSamplers, + HandleFallbacksForShadows, + PrepareAttributesForBakedVertexAnimation, + PrepareAttributesForBones, + PrepareAttributesForInstances, + PrepareAttributesForMorphTargets, + PrepareDefinesForAttributes, + PrepareDefinesForFrameBoundValues, + PrepareDefinesForLights, + PrepareDefinesForIBL, + PrepareDefinesForMergedUV, + PrepareDefinesForMisc, + PrepareDefinesForMultiview, + PrepareDefinesForOIT, + PrepareDefinesForPrePass, + PrepareUniformsAndSamplersList, + PrepareUniformsAndSamplersForIBL, + PrepareUniformLayoutForIBL, +} from "../materialHelper.functions"; +import { Constants } from "../../Engines/constants"; +import { VertexBuffer } from "../../Buffers/buffer"; +import { MaterialPluginEvent } from "../materialPluginEvent"; +import { MaterialHelperGeometryRendering } from "../materialHelper.geometryrendering"; +import { PrePassConfiguration } from "../prePassConfiguration"; +import type { IMaterialCompilationOptions, ICustomShaderNameResolveOptions } from "../../Materials/material"; +import { ShaderLanguage } from "../shaderLanguage"; +import { MaterialFlags } from "../materialFlags"; +import type { SubMesh } from "../../Meshes/subMesh"; +import { Logger } from "core/Misc/logger"; +import { UVDefinesMixin } from "../uv.defines"; +import { Vector2, Vector3, Vector4, TmpVectors } from "core/Maths/math.vector"; +import type { Matrix } from "core/Maths/math.vector"; +import type { Mesh } from "../../Meshes/mesh"; +import { ImageProcessingMixin } from "../imageProcessing"; +import { PushMaterial } from "../pushMaterial"; +import { SmartArray } from "../../Misc/smartArray"; +import type { RenderTargetTexture } from "../Textures/renderTargetTexture"; +import type { IAnimatable } from "../../Animations/animatable.interface"; + +const onCreatedEffectParameters = { effect: null as unknown as Effect, subMesh: null as unknown as Nullable }; + +class Uniform { + public name: string; + public numComponents: number; + public linkedProperties: { [name: string]: Property } = {}; + public populateVectorFromLinkedProperties(vector: Vector4 | Vector3 | Vector2): void { + const destinationSize = vector instanceof Vector4 ? 4 : vector instanceof Vector3 ? 3 : vector instanceof Vector2 ? 2 : 1; + for (const propKey in this.linkedProperties) { + const prop = this.linkedProperties[propKey]; + const sourceSize = prop.numComponents; + if (destinationSize < sourceSize || prop.targetUniformComponentOffset > destinationSize - sourceSize) { + if (sourceSize == 1) { + Logger.Error(`Float property ${prop.name} has an offset that is too large.`); + } else { + Logger.Error(`Vector${sourceSize} property ${prop.name} won't fit in Vector${destinationSize} or has an offset that is too large.`); + } + return; + } + if (typeof prop.value === "number") { + Uniform._tmpArray[prop.targetUniformComponentOffset] = prop.value; + } else { + prop.value.toArray(Uniform._tmpArray, prop.targetUniformComponentOffset); + } + } + vector.fromArray(Uniform._tmpArray); + } + public constructor(name: string, componentNum: number) { + this.name = name; + this.numComponents = componentNum; + } + private static _tmpArray: number[] = [0, 0, 0, 0]; +} + +/** + * Defines a property for the OpenPBRMaterial. + */ +class Property { + public name: string; + public targetUniformName: string; + public defaultValue: T; + public value: T; + // public includeAlphaFromProp: string = ""; + + /** + * If not given a type, there will be no uniform defined for this property and + * it will be assumed that the value will be packed into the already existing "uniformName" uniform. + */ + public targetUniformComponentNum: number = 4; // Default to vec4 + public targetUniformComponentOffset: number = 0; + + /** + * Creates a new Property instance. + * @param name The name of the property in the shader + * @param defaultValue The default value of the property + * @param targetUniformName The name of the property in the shader uniform block + * @param targetUniformComponentNum The number of components in the target uniform. All properties that are + * packed into the same uniform must agree on the size of the target uniform. + * @param targetUniformComponentOffset The offset in the uniform where this property will be packed. + */ + constructor(name: string, defaultValue: T, targetUniformName: string, targetUniformComponentNum: number, targetUniformComponentOffset: number = 0) { + this.name = name; + this.targetUniformName = targetUniformName; + this.defaultValue = defaultValue; + this.value = defaultValue; + this.targetUniformComponentNum = targetUniformComponentNum; + this.targetUniformComponentOffset = targetUniformComponentOffset; + } + + /** + * Returns the number of components of the property based on its default value type. + */ + public get numComponents(): number { + if (typeof this.defaultValue === "number") { + return 1; + } else if (this.defaultValue instanceof Color3) { + return 3; + } else if (this.defaultValue instanceof Color4) { + return 4; + } else if (this.defaultValue instanceof Vector2) { + return 2; + } else if (this.defaultValue instanceof Vector3) { + return 3; + } else if (this.defaultValue instanceof Vector4) { + return 4; + } + return 0; // Default size for unsupported types + } +} + +class Sampler { + public name: string; + public value: Nullable = null; // Texture value, default to null + public samplerPrefix: string = ""; // The name of the sampler in the shader + public textureDefine: string = ""; // The define used in the shader for this sampler + + /** + * The name of the sampler used in the shader. + * If this naming changes, we'll also need to change: + * - samplerFragmentDeclaration.fx + * - openpbr.fragment.fx + */ + public get samplerName(): string { + return this.samplerPrefix + "Sampler"; + } + /** + * The name of the sampler info used in the shader. + * If this naming changes, we'll also need to change: + * - openpbr.vertex.fx + * - openpbr.fragment.fx + */ + public get samplerInfoName(): string { + return "v" + this.samplerPrefix.charAt(0).toUpperCase() + this.samplerPrefix.slice(1) + "Infos"; + } + /** + * The name of the matrix used for this sampler in the shader. + * If this naming changes, we'll also need to change: + * - materialHelper.functions.BindTextureMatrix + * - samplerVertexImplementation.fx + * - openpbr.fragment.fx + */ + public get samplerMatrixName(): string { + return this.samplerPrefix + "Matrix"; + } + /** + * Creates a new Sampler instance. + * @param name The name of the texture property + * @param samplerPrefix The prefix used for the name of the sampler in the shader + * @param textureDefine The define used in the shader for this sampler + */ + constructor(name: string, samplerPrefix: string, textureDefine: string) { + this.name = name; + this.samplerPrefix = samplerPrefix; + this.textureDefine = textureDefine; + } +} + +class OpenPBRMaterialDefinesBase extends UVDefinesMixin(MaterialDefines) {} +/** + * Manages the defines for the PBR Material. + * @internal + */ +export class OpenPBRMaterialDefines extends ImageProcessingDefinesMixin(OpenPBRMaterialDefinesBase) { + public NUM_SAMPLES = "0"; + public REALTIME_FILTERING = false; + public IBL_CDF_FILTERING = false; + + public VERTEXCOLOR = false; + + public BAKED_VERTEX_ANIMATION_TEXTURE = false; + + public VERTEXALPHA = false; + public ALPHATEST = false; + public DEPTHPREPASS = false; + public ALPHABLEND = false; + public ALPHAFROMALBEDO = false; + public ALPHATESTVALUE = "0.5"; + public PREMULTIPLYALPHA = false; + + public REFLECTIVITY_GAMMA = false; + public REFLECTIVITYDIRECTUV = 0; + public SPECULARTERM = false; + + public LODBASEDMICROSFURACE = true; + + public METALLICWORKFLOW = true; + public ROUGHNESSSTOREINMETALMAPALPHA = false; + public ROUGHNESSSTOREINMETALMAPGREEN = false; + public METALLNESSSTOREINMETALMAPBLUE = false; + public AOSTOREINMETALMAPRED = false; + public SPECULAR_WEIGHT_USE_ALPHA_ONLY = false; + + public ENVIRONMENTBRDF = false; + public ENVIRONMENTBRDF_RGBD = false; + + public NORMAL = false; + public TANGENT = false; + public OBJECTSPACE_NORMALMAP = false; + public PARALLAX = false; + public PARALLAX_RHS = false; + public PARALLAXOCCLUSION = false; + public NORMALXYSCALE = true; + + public REFLECTION = false; + public REFLECTIONMAP_3D = false; + public REFLECTIONMAP_SPHERICAL = false; + public REFLECTIONMAP_PLANAR = false; + public REFLECTIONMAP_CUBIC = false; + public USE_LOCAL_REFLECTIONMAP_CUBIC = false; + public REFLECTIONMAP_PROJECTION = false; + public REFLECTIONMAP_SKYBOX = false; + public REFLECTIONMAP_EXPLICIT = false; + public REFLECTIONMAP_EQUIRECTANGULAR = false; + public REFLECTIONMAP_EQUIRECTANGULAR_FIXED = false; + public REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED = false; + 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; + public GAMMAREFLECTION = false; + public RGBDREFLECTION = false; + public RADIANCEOCCLUSION = false; + public HORIZONOCCLUSION = false; + + public INSTANCES = false; + public THIN_INSTANCES = false; + public INSTANCESCOLOR = false; + + public PREPASS = false; + public PREPASS_COLOR = false; + public PREPASS_COLOR_INDEX = -1; + public PREPASS_IRRADIANCE = false; + public PREPASS_IRRADIANCE_INDEX = -1; + public PREPASS_ALBEDO = false; + public PREPASS_ALBEDO_INDEX = -1; + public PREPASS_ALBEDO_SQRT = false; + public PREPASS_ALBEDO_SQRT_INDEX = -1; + public PREPASS_DEPTH = false; + public PREPASS_DEPTH_INDEX = -1; + public PREPASS_SCREENSPACE_DEPTH = false; + public PREPASS_SCREENSPACE_DEPTH_INDEX = -1; + public PREPASS_NORMALIZED_VIEW_DEPTH = false; + public PREPASS_NORMALIZED_VIEW_DEPTH_INDEX = -1; + public PREPASS_NORMAL = false; + public PREPASS_NORMAL_INDEX = -1; + public PREPASS_NORMAL_WORLDSPACE = false; + public PREPASS_WORLD_NORMAL = false; + public PREPASS_WORLD_NORMAL_INDEX = -1; + public PREPASS_POSITION = false; + public PREPASS_POSITION_INDEX = -1; + public PREPASS_LOCAL_POSITION = false; + public PREPASS_LOCAL_POSITION_INDEX = -1; + public PREPASS_VELOCITY = false; + public PREPASS_VELOCITY_INDEX = -1; + public PREPASS_VELOCITY_LINEAR = false; + public PREPASS_VELOCITY_LINEAR_INDEX = -1; + public PREPASS_REFLECTIVITY = false; + public PREPASS_REFLECTIVITY_INDEX = -1; + public SCENE_MRT_COUNT = 0; + + public NUM_BONE_INFLUENCERS = 0; + public BonesPerMesh = 0; + public BONETEXTURE = false; + public BONES_VELOCITY_ENABLED = false; + + public NONUNIFORMSCALING = false; + + public MORPHTARGETS = false; + public MORPHTARGETS_POSITION = false; + public MORPHTARGETS_NORMAL = false; + public MORPHTARGETS_TANGENT = false; + public MORPHTARGETS_UV = false; + public MORPHTARGETS_UV2 = false; + public MORPHTARGETS_COLOR = false; + public MORPHTARGETTEXTURE_HASPOSITIONS = false; + public MORPHTARGETTEXTURE_HASNORMALS = false; + public MORPHTARGETTEXTURE_HASTANGENTS = false; + public MORPHTARGETTEXTURE_HASUVS = false; + public MORPHTARGETTEXTURE_HASUV2S = false; + public MORPHTARGETTEXTURE_HASCOLORS = false; + public NUM_MORPH_INFLUENCERS = 0; + public MORPHTARGETS_TEXTURE = false; + + public USEPHYSICALLIGHTFALLOFF = false; + public USEGLTFLIGHTFALLOFF = false; + public TWOSIDEDLIGHTING = false; + public MIRRORED = false; + public SHADOWFLOAT = false; + public CLIPPLANE = false; + public CLIPPLANE2 = false; + public CLIPPLANE3 = false; + public CLIPPLANE4 = false; + public CLIPPLANE5 = false; + public CLIPPLANE6 = false; + public POINTSIZE = false; + public FOG = false; + public LOGARITHMICDEPTH = false; + public CAMERA_ORTHOGRAPHIC = false; + public CAMERA_PERSPECTIVE = false; + public AREALIGHTSUPPORTED = true; + + public FORCENORMALFORWARD = false; + + public SPECULARAA = false; + + public UNLIT = false; + + public DECAL_AFTER_DETAIL = false; + + public DEBUGMODE = 0; + + // BRDF defines + BRDF_V_HEIGHT_CORRELATED = true; + MS_BRDF_ENERGY_CONSERVATION = true; + SPHERICAL_HARMONICS = true; + SPECULAR_GLOSSINESS_ENERGY_CONSERVATION = true; + MIX_IBL_RADIANCE_WITH_IRRADIANCE = true; + LEGACY_SPECULAR_ENERGY_CONSERVATION = false; + BASE_DIFFUSE_MODEL = Constants.MATERIAL_DIFFUSE_MODEL_E_OREN_NAYAR; + DIELECTRIC_SPECULAR_MODEL = Constants.MATERIAL_DIELECTRIC_SPECULAR_MODEL_OPENPBR; + CONDUCTOR_SPECULAR_MODEL = Constants.MATERIAL_CONDUCTOR_SPECULAR_MODEL_OPENPBR; + + /** + * Initializes the PBR Material defines. + * @param externalProperties The external properties + */ + constructor(externalProperties?: { [name: string]: { type: string; default: any } }) { + super(externalProperties); + this.rebuild(); + } + + /** + * Resets the PBR Material defines. + */ + public override reset(): void { + super.reset(); + this.ALPHATESTVALUE = "0.5"; + this.NORMALXYSCALE = true; + } +} + +class OpenPBRMaterialBase extends ImageProcessingMixin(PushMaterial) {} +/** + * The Physically based material of BJS. + * + * This offers the main features of a standard PBR material. + * For more information, please refer to the documentation : + * https://doc.babylonjs.com/features/featuresDeepDive/materials/using/introToPBR + */ +export class OpenPBRMaterial extends OpenPBRMaterialBase { + /** + * OpenPBRMaterialTransparencyMode: No transparency mode, Alpha channel is not use. + */ + public static readonly PBRMATERIAL_OPAQUE = PBRBaseMaterial.PBRMATERIAL_OPAQUE; + + /** + * OpenPBRMaterialTransparencyMode: Alpha Test mode, pixel are discarded below a certain threshold defined by the alpha cutoff value. + */ + public static readonly PBRMATERIAL_ALPHATEST = PBRBaseMaterial.PBRMATERIAL_ALPHATEST; + + /** + * OpenPBRMaterialTransparencyMode: Pixels are blended (according to the alpha mode) with the already drawn pixels in the current frame buffer. + */ + public static readonly PBRMATERIAL_ALPHABLEND = PBRBaseMaterial.PBRMATERIAL_ALPHABLEND; + + /** + * OpenPBRMaterialTransparencyMode: Pixels are blended (according to the alpha mode) with the already drawn pixels in the current frame buffer. + * They are also discarded below the alpha cutoff threshold to improve performances. + */ + public static readonly PBRMATERIAL_ALPHATESTANDBLEND = PBRBaseMaterial.PBRMATERIAL_ALPHATESTANDBLEND; + + /** + * Defines the default value of how much AO map is occluding the analytical lights + * (point spot...). + */ + public static DEFAULT_AO_ON_ANALYTICAL_LIGHTS = PBRBaseMaterial.DEFAULT_AO_ON_ANALYTICAL_LIGHTS; + + /** + * Base Weight is a multiplier on the diffuse and metal lobes. + * See OpenPBR's specs for base_weight + */ + public baseWeight: number; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "baseWeight") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _baseWeight: Property = new Property("base_weight", 1, "vBaseWeight", 1); + + /** + * Base Weight is a multiplier on the diffuse and metal lobes. + * See OpenPBR's specs for base_weight + */ + public baseWeightTexture: BaseTexture; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "baseColorTexture") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _baseWeightTexture: Sampler = new Sampler("base_weight", "baseWeight", "BASE_WEIGHT"); + + /** + * Color of the base diffuse lobe. + * See OpenPBR's specs for base_color + */ + public baseColor: Color3; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "baseColor") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _baseColor: Property = new Property("base_color", Color3.White(), "vBaseColor", 4); + + /** + * Base Color Texture property. + * See OpenPBR's specs for base_color + */ + public baseColorTexture: BaseTexture; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "baseColorTexture") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _baseColorTexture: Sampler = new Sampler("base_color", "baseColor", "BASE_COLOR"); + + /** + * Roughness of the diffuse lobe. + * See OpenPBR's specs for base_diffuse_roughness + */ + public baseDiffuseRoughness: number; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "baseDiffuseRoughness") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _baseDiffuseRoughness: Property = new Property("base_diffuse_roughness", 0, "vBaseDiffuseRoughness", 1); + + /** + * Roughness of the diffuse lobe. + * See OpenPBR's specs for base_diffuse_roughness + */ + public baseDiffuseRoughnessTexture: BaseTexture; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "baseDiffuseRoughnessTexture") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _baseDiffuseRoughnessTexture: Sampler = new Sampler("base_diffuse_roughness", "baseDiffuseRoughness", "BASE_DIFFUSE_ROUGHNESS"); + + /** + * Metalness of the base lobe. + * See OpenPBR's specs for base_metalness + */ + public baseMetalness: number; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "baseMetalness") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _baseMetalness: Property = new Property("base_metalness", 0, "vReflectanceInfo", 4, 0); + + /** + * Weight of the specular lobe. + * See OpenPBR's specs for specular_weight + */ + public specularWeight: number; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "specularWeight") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _specularWeight: Property = new Property("specular_weight", 1, "vReflectanceInfo", 4, 3); + + /** + * Roughness of the diffuse lobe. + * See OpenPBR's specs for base_diffuse_roughness + */ + public specularWeightTexture: BaseTexture; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "specularWeightTexture") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _specularWeightTexture: Sampler = new Sampler("specular_weight", "specularWeight", "SPECULAR_WEIGHT"); + + /** + * Color of the specular lobe. + * See OpenPBR's specs for specular_color + */ + public specularColor: Color3; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "specularColor") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _specularColor: Property = new Property("specular_color", Color3.White(), "vSpecularColor", 4); + + /** + * Specular Color Texture property. + * See OpenPBR's specs for specular_color + */ + public specularColorTexture: BaseTexture; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "specularColorTexture") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _specularColorTexture: Sampler = new Sampler("specular_color", "specularColor", "SPECULAR_COLOR"); + + /** + * Roughness of the specular lobe. + * See OpenPBR's specs for specular_roughness + */ + public specularRoughness: number; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "specularRoughness") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _specularRoughness: Property = new Property("specular_roughness", 0, "vReflectanceInfo", 4, 1); + + /** + * IOR of the specular lobe. + * See OpenPBR's specs for specular_roughness + */ + public specularIor: number; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "specularIor") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _specularIor: Property = new Property("specular_ior", 1.5, "vReflectanceInfo", 4, 2); + + /** + * Metalness and Roughness texture. + * See OpenPBR's specs for base_metalness and specular_roughness + */ + public baseMetalRoughTexture: BaseTexture; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "baseMetalRoughTexture") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _baseMetalRoughTexture: Sampler = new Sampler("base_metalness_specular_roughness", "baseMetalRough", "METALLIC_ROUGHNESS"); + + /** + * Defines the amount of clear coat on the surface. + * See OpenPBR's specs for coat_weight + */ + public coatWeight: number; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "coatWeight") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _coatWeight: Property = new Property("coat_weight", 0.0, "vCoatWeight", 1, 0); + + /** + * Coat weight texture. + * See OpenPBR's specs for coat_weight + */ + public coatWeightTexture: Nullable; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "coatWeightTexture") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _coatWeightTexture: Sampler = new Sampler("coat_weight", "coatWeight", "COAT_WEIGHT"); + + /** + * Defines the color of the clear coat on the surface. + * See OpenPBR's specs for coat_color + */ + public coatColor: number; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "coatColor") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _coatColor: Property = new Property("coat_color", Color3.White(), "vCoatColor", 3, 0); + + /** + * Color of the clear coat. + * See OpenPBR's specs for coat_color + */ + public coatColorTexture: Nullable; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "coatColorTexture") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _coatColorTexture: Sampler = new Sampler("coat_color", "coatColor", "COAT_COLOR"); + + /** + * Defines the roughness of the clear coat on the surface. + * See OpenPBR's specs for coat_roughness + */ + public coatRoughness: number; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "coatRoughness") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _coatRoughness: Property = new Property("coat_roughness", 0.0, "vCoatRoughness", 1, 0); + + /** + * Roughness of the clear coat. + * See OpenPBR's specs for coat_roughness + */ + public coatRoughnessTexture: Nullable; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "coatRoughnessTexture") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _coatRoughnessTexture: Sampler = new Sampler("coat_roughness", "coatRoughness", "COAT_ROUGHNESS"); + + /** + * Defines the IOR of the clear coat on the surface. + * See OpenPBR's specs for coat_ior + */ + public coatIor: number; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "coatIor") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _coatIor: Property = new Property("coat_ior", 1.5, "vCoatIor", 1, 0); + + /** + * Defines the amount that interreflections within the coat allow the underlying surface + * to be darkened. A value of 1.0 means that the physically correct amount of darkening + * is applied, while a value of 0.0 means that no darkening is applied. + * See OpenPBR's specs for coat_darkening + */ + public coatDarkening: number; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "coatDarkening") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _coatDarkening: Property = new Property("coat_darkening", 1.0, "vCoatDarkening", 1, 0); + + /** + * Defines the amount that interreflections within the coat allow the underlying surface + * to be darkened. A value of 1.0 means that the physically correct amount of darkening + * is applied, while a value of 0.0 means that no darkening is applied. + * See OpenPBR's specs for coat_darkening + */ + public coatDarkeningTexture: Nullable; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "coatDarkeningTexture") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _coatDarkeningTexture: Sampler = new Sampler("coat_darkening", "coatDarkening", "COAT_DARKENING"); + + /** + * Specifies whether the coat roughness is taken from the + * same texture as the coat_weight. + */ + public useCoatRoughnessFromWeightTexture: boolean = false; + + /** + * Defines the normal of the material's geometry. + * See OpenPBR's specs for geometry_normal + */ + public geometryNormalTexture: BaseTexture; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "geometryNormalTexture") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _geometryNormalTexture: Sampler = new Sampler("geometry_normal", "geometryNormal", "GEOMETRY_NORMAL"); + + /** + * Defines the normal of the material's coat layer. + * See OpenPBR's specs for geometry_coat_normal + */ + public geometryCoatNormalTexture: BaseTexture; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "geometryCoatNormalTexture") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _geometryCoatNormalTexture: Sampler = new Sampler("geometry_coat_normal", "geometryCoatNormal", "GEOMETRY_COAT_NORMAL"); + + /** + * Defines the opacity of the material's geometry. + * See OpenPBR's specs for geometry_opacity + */ + public geometryOpacity: number; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "geometryOpacity") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _geometryOpacity: Property = new Property("geometry_opacity", 1.0, "vBaseColor", 4, 3); + + /** + * Defines the opacity of the material's geometry. + * See OpenPBR's specs for geometry_opacity + */ + public geometryOpacityTexture: BaseTexture; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "geometryOpacityTexture") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _geometryOpacityTexture: Sampler = new Sampler("geometry_opacity", "geometryOpacity", "GEOMETRY_OPACITY"); + + /** + * Defines the opacity of the material's geometry. + * See OpenPBR's specs for geometry_opacity + */ + public emissionLuminance: number; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "emissionLuminance") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _emissionLuminance: Property = new Property("emission_luminance", 1.0, "vLightingIntensity", 4, 1); + + /** + * Defines the color of the material's emission. + * See OpenPBR's specs for emission_color + */ + public emissionColor: Color3; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "emissionColor") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _emissionColor: Property = new Property("emission_color", Color3.Black(), "vEmissionColor", 3); + + /** + * Defines the color of the material's emission. + * See OpenPBR's specs for emission_color + */ + public emissionColorTexture: BaseTexture; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "emissionColorTexture") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _emissionColorTexture: Sampler = new Sampler("emission_color", "emissionColor", "EMISSION_COLOR"); + + /** + * Defines the ambient occlusion texture. + */ + public ambientOcclusionTexture: BaseTexture; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "ambientOcclusionTexture") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _ambientOcclusionTexture: Sampler = new Sampler("ambient_occlusion", "ambientOcclusion", "AMBIENT_OCCLUSION"); + + private _propertyList: { [name: string]: Property }; + private _uniformsList: { [name: string]: Uniform } = {}; + private _samplersList: { [name: string]: Sampler } = {}; + private _samplerDefines: { [name: string]: { type: string; default: any } } = {}; + + /** + * Intensity of the direct lights e.g. the four lights available in your scene. + * This impacts both the direct diffuse and specular highlights. + */ + @serialize() + @expandToProperty("_markAllSubMeshesAsTexturesDirty") + public directIntensity: number = 1.0; + + /** + * Intensity of the environment e.g. how much the environment will light the object + * either through harmonics for rough material or through the reflection for shiny ones. + */ + @serialize() + @expandToProperty("_markAllSubMeshesAsTexturesDirty") + public environmentIntensity: number = 1.0; + + /** + * Specifies that the specular weight is stored in the alpha channel of the specular weight texture. + */ + @serialize() + @expandToProperty("_markAllSubMeshesAsTexturesDirty") + public useSpecularWeightFromTextureAlpha = false; + + /** + * Specifies that the alpha is coming form the albedo channel alpha channel for alpha blending. + */ + @serialize() + @expandToProperty("_markAllSubMeshesAsTexturesAndMiscDirty") + public useAlphaFromAlbedoTexture = false; + + /** + * Enforces alpha test in opaque or blend mode in order to improve the performances of some situations. + */ + @serialize() + @expandToProperty("_markAllSubMeshesAsTexturesAndMiscDirty") + public forceAlphaTest = false; + + /** + * Defines the alpha limits in alpha test mode. + */ + @serialize() + @expandToProperty("_markAllSubMeshesAsTexturesAndMiscDirty") + public alphaCutOff = 0.4; + + /** + * Specifies if the metallic texture contains the ambient occlusion information in its red channel. + */ + @serialize() + @expandToProperty("_markAllSubMeshesAsTexturesDirty") + public useAmbientOcclusionFromMetallicTextureRed = false; + + /** + * Specifies if the ambient texture contains the ambient occlusion information in its red channel only. + */ + @serialize() + @expandToProperty("_markAllSubMeshesAsTexturesDirty") + public useAmbientInGrayScale = false; + + /** + * BJS is using an hardcoded light falloff based on a manually sets up range. + * In PBR, one way to represents the falloff is to use the inverse squared root algorithm. + * This parameter can help you switch back to the BJS mode in order to create scenes using both materials. + */ + @serialize() + public get usePhysicalLightFalloff(): boolean { + return this._lightFalloff === PBRBaseMaterial.LIGHTFALLOFF_PHYSICAL; + } + + /** + * BJS is using an hardcoded light falloff based on a manually sets up range. + * In PBR, one way to represents the falloff is to use the inverse squared root algorithm. + * This parameter can help you switch back to the BJS mode in order to create scenes using both materials. + */ + public set usePhysicalLightFalloff(value: boolean) { + if (value !== this.usePhysicalLightFalloff) { + // Ensure the effect will be rebuilt. + this._markAllSubMeshesAsTexturesDirty(); + + if (value) { + this._lightFalloff = PBRBaseMaterial.LIGHTFALLOFF_PHYSICAL; + } else { + this._lightFalloff = PBRBaseMaterial.LIGHTFALLOFF_STANDARD; + } + } + } + + /** + * In order to support the falloff compatibility with gltf, a special mode has been added + * to reproduce the gltf light falloff. + */ + @serialize() + public get useGLTFLightFalloff(): boolean { + return this._lightFalloff === PBRBaseMaterial.LIGHTFALLOFF_GLTF; + } + + /** + * In order to support the falloff compatibility with gltf, a special mode has been added + * to reproduce the gltf light falloff. + */ + public set useGLTFLightFalloff(value: boolean) { + if (value !== this.useGLTFLightFalloff) { + // Ensure the effect will be rebuilt. + this._markAllSubMeshesAsTexturesDirty(); + + if (value) { + this._lightFalloff = PBRBaseMaterial.LIGHTFALLOFF_GLTF; + } else { + this._lightFalloff = PBRBaseMaterial.LIGHTFALLOFF_STANDARD; + } + } + } + + /** + * Allows using an object space normal map (instead of tangent space). + */ + @serialize() + @expandToProperty("_markAllSubMeshesAsTexturesDirty") + public useObjectSpaceNormalMap = false; + + /** + * Allows using the normal map in parallax mode. + */ + @serialize() + @expandToProperty("_markAllSubMeshesAsTexturesDirty") + public useParallax = false; + + /** + * Allows using the normal map in parallax occlusion mode. + */ + @serialize() + @expandToProperty("_markAllSubMeshesAsTexturesDirty") + public useParallaxOcclusion = false; + + /** + * Controls the scale bias of the parallax mode. + */ + @serialize() + @expandToProperty("_markAllSubMeshesAsTexturesDirty") + public parallaxScaleBias = 0.05; + + /** + * If sets to true, disables all the lights affecting the material. + */ + @serialize() + @expandToProperty("_markAllSubMeshesAsLightsDirty") + public disableLighting = false; + + /** + * Force the shader to compute irradiance in the fragment shader in order to take normal mapping into account. + */ + @serialize() + @expandToProperty("_markAllSubMeshesAsTexturesDirty") + public forceIrradianceInFragment = false; + + /** + * Number of Simultaneous lights allowed on the material. + */ + @serialize() + @expandToProperty("_markAllSubMeshesAsLightsDirty") + public maxSimultaneousLights = 4; + + /** + * If sets to true, x component of normal map value will invert (x = 1.0 - x). + */ + @serialize() + @expandToProperty("_markAllSubMeshesAsTexturesDirty") + public invertNormalMapX = false; + + /** + * If sets to true, y component of normal map value will invert (y = 1.0 - y). + */ + @serialize() + @expandToProperty("_markAllSubMeshesAsTexturesDirty") + public invertNormalMapY = false; + + /** + * If sets to true and backfaceCulling is false, normals will be flipped on the backside. + */ + @serialize() + @expandToProperty("_markAllSubMeshesAsTexturesDirty") + public twoSidedLighting = false; + + /** + * A fresnel is applied to the alpha of the model to ensure grazing angles edges are not alpha tested. + * And/Or occlude the blended part. (alpha is converted to gamma to compute the fresnel) + */ + @serialize() + @expandToProperty("_markAllSubMeshesAsTexturesDirty") + public useAlphaFresnel = false; + + /** + * A fresnel is applied to the alpha of the model to ensure grazing angles edges are not alpha tested. + * And/Or occlude the blended part. (alpha stays linear to compute the fresnel) + */ + @serialize() + @expandToProperty("_markAllSubMeshesAsTexturesDirty") + public useLinearAlphaFresnel = false; + + /** + * Let user defines the brdf lookup texture used for IBL. + * A default 8bit version is embedded but you could point at : + * * Default texture: https://assets.babylonjs.com/environments/correlatedMSBRDF_RGBD.png + * * Default 16bit pixel depth texture: https://assets.babylonjs.com/environments/correlatedMSBRDF.dds + * * LEGACY Default None correlated https://assets.babylonjs.com/environments/uncorrelatedBRDF_RGBD.png + * * LEGACY Default None correlated 16bit pixel depth https://assets.babylonjs.com/environments/uncorrelatedBRDF.dds + */ + @expandToProperty("_markAllSubMeshesAsTexturesDirty") + public environmentBRDFTexture: Nullable = null; + + /** + * Force normal to face away from face. + */ + @serialize() + @expandToProperty("_markAllSubMeshesAsTexturesDirty") + public forceNormalForward = false; + + /** + * Enables specular anti aliasing in the PBR shader. + * It will both interacts on the Geometry for analytical and IBL lighting. + * It also prefilter the roughness map based on the normalmap values. + */ + @serialize() + @expandToProperty("_markAllSubMeshesAsTexturesDirty") + public enableSpecularAntiAliasing = false; + + /** + * This parameters will enable/disable Horizon occlusion to prevent normal maps to look shiny when the normal + * makes the reflect vector face the model (under horizon). + */ + @serialize() + @expandToProperty("_markAllSubMeshesAsTexturesDirty") + public useHorizonOcclusion = true; + + /** + * This parameters will enable/disable radiance occlusion by preventing the radiance to lit + * too much the area relying on ambient texture to define their ambient occlusion. + */ + @serialize() + @expandToProperty("_markAllSubMeshesAsTexturesDirty") + public useRadianceOcclusion = true; + + /** + * If set to true, no lighting calculations will be applied. + */ + @serialize() + @expandToProperty("_markAllSubMeshesAsMiscDirty") + public unlit = false; + + /** + * If sets to true, the decal map will be applied after the detail map. Else, it is applied before (default: false) + */ + @serialize() + @expandToProperty("_markAllSubMeshesAsMiscDirty") + public applyDecalMapAfterDetailMap = false; + + /** + * PBRMaterialLightFalloff Physical: light is falling off following the inverse squared distance law. + */ + public static readonly LIGHTFALLOFF_PHYSICAL = 0; + + /** + * PBRMaterialLightFalloff gltf: light is falling off as described in the gltf moving to PBR document + * to enhance interoperability with other engines. + */ + public static readonly LIGHTFALLOFF_GLTF = 1; + + /** + * PBRMaterialLightFalloff Standard: light is falling off like in the standard material + * to enhance interoperability with other materials. + */ + public static readonly LIGHTFALLOFF_STANDARD = 2; + + /** + * Force all the PBR materials to compile to glsl even on WebGPU engines. + * False by default. This is mostly meant for backward compatibility. + */ + public static ForceGLSL = false; + + /** + * Intensity of the direct lights e.g. the four lights available in your scene. + * This impacts both the direct diffuse and specular highlights. + * @internal + */ + public _directIntensity: number = 1.0; + + /** + * Intensity of the environment e.g. how much the environment will light the object + * either through harmonics for rough material or through the reflection for shiny ones. + * @internal + */ + public _environmentIntensity: number = 1.0; + + /** + * This stores the direct, emissive, environment, and specular light intensities into a Vector4. + */ + private _lightingInfos: Vector4 = new Vector4(this._directIntensity, 1.0, this._environmentIntensity, 1.0); + + /** + * Stores the radiance (and, possibly, irradiance) values in a texture. + * @internal + */ + public _radianceTexture: Nullable = null; + + /** + * Specifies that only the A channel from _metallicReflectanceTexture should be used. + * If false, both RGB and A channels will be used + * @internal + */ + public _useSpecularWeightFromTextureAlpha = false; + + /** + * This parameters will enable/disable Horizon occlusion to prevent normal maps to look shiny when the normal + * makes the reflect vector face the model (under horizon). + * @internal + */ + public _useHorizonOcclusion = true; + + /** + * This parameters will enable/disable radiance occlusion by preventing the radiance to lit + * too much the area relying on ambient texture to define their ambient occlusion. + * @internal + */ + public _useRadianceOcclusion = true; + + /** + * Specifies that the alpha is coming form the albedo channel alpha channel for alpha blending. + * @internal + */ + public _useAlphaFromAlbedoTexture = false; + + /** + * Specifies if the metallic texture contains the ambient occlusion information in its red channel. + * @internal + */ + public _useAmbientOcclusionFromMetallicTextureRed = false; + + /** + * Defines the falloff type used in this material. + * It by default is Physical. + * @internal + */ + public _lightFalloff = PBRBaseMaterial.LIGHTFALLOFF_PHYSICAL; + + /** + * Allows using an object space normal map (instead of tangent space). + * @internal + */ + public _useObjectSpaceNormalMap = false; + + /** + * Allows using the normal map in parallax mode. + * @internal + */ + public _useParallax = false; + + /** + * Allows using the normal map in parallax occlusion mode. + * @internal + */ + public _useParallaxOcclusion = false; + + /** + * Controls the scale bias of the parallax mode. + * @internal + */ + public _parallaxScaleBias = 0.05; + + /** + * If sets to true, disables all the lights affecting the material. + * @internal + */ + public _disableLighting = false; + + /** + * Number of Simultaneous lights allowed on the material. + * @internal + */ + public _maxSimultaneousLights = 4; + + /** + * If sets to true, x component of normal map value will be inverted (x = 1.0 - x). + * @internal + */ + public _invertNormalMapX = false; + + /** + * If sets to true, y component of normal map value will be inverted (y = 1.0 - y). + * @internal + */ + public _invertNormalMapY = false; + + /** + * If sets to true and backfaceCulling is false, normals will be flipped on the backside. + * @internal + */ + public _twoSidedLighting = false; + + /** + * Defines the alpha limits in alpha test mode. + * @internal + */ + public _alphaCutOff = 0.4; + + /** + * A fresnel is applied to the alpha of the model to ensure grazing angles edges are not alpha tested. + * And/Or occlude the blended part. (alpha is converted to gamma to compute the fresnel) + * @internal + */ + public _useAlphaFresnel = false; + + /** + * A fresnel is applied to the alpha of the model to ensure grazing angles edges are not alpha tested. + * And/Or occlude the blended part. (alpha stays linear to compute the fresnel) + * @internal + */ + public _useLinearAlphaFresnel = false; + + /** + * Specifies the environment BRDF texture used to compute the scale and offset roughness values + * from cos theta and roughness: + * http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf + * @internal + */ + public _environmentBRDFTexture: Nullable = null; + + /** + * Force the shader to compute irradiance in the fragment shader in order to take normal mapping into account. + * @internal + */ + public _forceIrradianceInFragment = false; + + private _realTimeFiltering: boolean = false; + /** + * Enables realtime filtering on the texture. + */ + public get realTimeFiltering() { + return this._realTimeFiltering; + } + public set realTimeFiltering(b: boolean) { + this._realTimeFiltering = b; + this.markAsDirty(Constants.MATERIAL_TextureDirtyFlag); + } + + private _realTimeFilteringQuality: number = Constants.TEXTURE_FILTERING_QUALITY_LOW; + /** + * Quality switch for realtime filtering + */ + public get realTimeFilteringQuality(): number { + return this._realTimeFilteringQuality; + } + public set realTimeFilteringQuality(n: number) { + this._realTimeFilteringQuality = n; + this.markAsDirty(Constants.MATERIAL_TextureDirtyFlag); + } + + /** + * Can this material render to several textures at once + */ + public override get canRenderToMRT() { + return true; + } + + /** + * Force normal to face away from face. + * @internal + */ + public _forceNormalForward = false; + + /** + * Enables specular anti aliasing in the PBR shader. + * It will both interacts on the Geometry for analytical and IBL lighting. + * It also prefilter the roughness map based on the normalmap values. + * @internal + */ + public _enableSpecularAntiAliasing = false; + + /** + * Stores the available render targets. + */ + private _renderTargets = new SmartArray(16); + + /** + * If set to true, no lighting calculations will be applied. + */ + private _unlit = false; + + /** + * If sets to true, the decal map will be applied after the detail map. Else, it is applied before (default: false) + */ + private _applyDecalMapAfterDetailMap = false; + + private _debugMode = 0; + + private _shadersLoaded = false; + private _breakShaderLoadedCheck = false; + + /** + * @internal + * This is reserved for the inspector. + * Defines the material debug mode. + * It helps seeing only some components of the material while troubleshooting. + */ + @expandToProperty("_markAllSubMeshesAsMiscDirty") + public debugMode = 0; + + /** + * @internal + * This is reserved for the inspector. + * Specify from where on screen the debug mode should start. + * The value goes from -1 (full screen) to 1 (not visible) + * It helps with side by side comparison against the final render + * This defaults to -1 + */ + public debugLimit = -1; + + /** + * @internal + * This is reserved for the inspector. + * As the default viewing range might not be enough (if the ambient is really small for instance) + * You can use the factor to better multiply the final value. + */ + public debugFactor = 1; + + /** + * Defines additional PrePass parameters for the material. + */ + public readonly prePassConfiguration: PrePassConfiguration; + + protected _cacheHasRenderTargetTextures = false; + + /** + * Instantiates a new OpenPBRMaterial instance. + * + * @param name The material name + * @param scene The scene the material will be use in. + * @param forceGLSL Use the GLSL code generation for the shader (even on WebGPU). Default is false + */ + constructor(name: string, scene?: Scene, forceGLSL = false) { + super(name, scene, undefined, forceGLSL || PBRBaseMaterial.ForceGLSL); + // TODO: Check if we're running WebGL 2.0 or above + if (scene && !scene?.getEngine().isWebGPU && (scene.getEngine() as Engine).webGLVersion < 2) { + Logger.Error("OpenPBRMaterial: WebGL 2.0 or above is required for this material."); + } + + // Setup the default processing configuration to the scene. + this._attachImageProcessingConfiguration(null); + + this.getRenderTargetTextures = (): SmartArray => { + this._renderTargets.reset(); + + if (MaterialFlags.ReflectionTextureEnabled && this._radianceTexture && this._radianceTexture.isRenderTarget) { + this._renderTargets.push(this._radianceTexture); + } + + this._eventInfo.renderTargets = this._renderTargets; + this._callbackPluginEventFillRenderTargetTextures(this._eventInfo); + + return this._renderTargets; + }; + + this._environmentBRDFTexture = GetEnvironmentBRDFTexture(this.getScene()); + this.prePassConfiguration = new PrePassConfiguration(); + this._environmentBRDFTexture = GetEnvironmentBRDFTexture(this.getScene()); + + // Build the internal property list that can be used to generate and update the uniform buffer + this._propertyList = {}; + for (const key of Object.getOwnPropertyNames(this)) { + const value = (this as any)[key]; + if (value instanceof Property) { + this._propertyList[key] = value; + } + } + // Build the internal uniforms list that is used for combining and updating + // property values in the uniform buffer + const propertyKeys = Object.keys(this._propertyList); + propertyKeys.forEach((key) => { + const prop = this._propertyList[key]; + let uniform = this._uniformsList[prop.targetUniformName]; + if (!uniform) { + uniform = new Uniform(prop.targetUniformName, prop.targetUniformComponentNum); + this._uniformsList[prop.targetUniformName] = uniform; + } else if (uniform.numComponents !== prop.targetUniformComponentNum) { + Logger.Error(`Uniform ${prop.targetUniformName} already exists of size ${uniform.numComponents}, but trying to set it to ${prop.targetUniformComponentNum}.`); + } + uniform.linkedProperties[prop.name] = prop; + }); + + // Build the internal list of samplers + this._samplersList = {}; + for (const key of Object.getOwnPropertyNames(this)) { + const value = (this as any)[key]; + if (value instanceof Sampler) { + this._samplersList[key] = value; + } + } + + // For each sampler in _samplersList, add defines to be added to OpenPBRMaterialDefines + for (const samplerKey in this._samplersList) { + const sampler = this._samplersList[samplerKey]; + const defineName = sampler.textureDefine; + this._samplerDefines[defineName] = { type: "boolean", default: false }; + this._samplerDefines[defineName + "DIRECTUV"] = { type: "number", default: 0 }; + this._samplerDefines[defineName + "_GAMMA"] = { type: "boolean", default: false }; + } + + // Arg. Why do I have to add these references to get rid of the linting errors? + this._baseWeight; + this._baseWeightTexture; + this._baseColor; + this._baseColorTexture; + this._baseDiffuseRoughness; + this._baseDiffuseRoughnessTexture; + this._baseMetalness; + this._specularWeight; + this._specularWeightTexture; + this._specularColor; + this._specularColorTexture; + this._specularRoughness; + this._specularIor; + this._baseMetalRoughTexture; + this._coatWeight; + this._coatWeightTexture; + this._coatColor; + this._coatColorTexture; + this._coatRoughness; + this._coatRoughnessTexture; + this._coatIor; + this._coatDarkening; + this._coatDarkeningTexture; + this._geometryNormalTexture; + this._geometryCoatNormalTexture; + this._geometryOpacity; + this._geometryOpacityTexture; + this._emissionLuminance; + this._emissionColor; + this._emissionColorTexture; + this._ambientOcclusionTexture; + } + + /** + * Gets a boolean indicating that current material needs to register RTT + */ + public override get hasRenderTargetTextures(): boolean { + if (MaterialFlags.ReflectionTextureEnabled && this._radianceTexture && this._radianceTexture.isRenderTarget) { + return true; + } + + return this._cacheHasRenderTargetTextures; + } + + /** + * Can this material render to prepass + */ + public override get isPrePassCapable(): boolean { + return !this.disableDepthWrite; + } + + /** + * @returns the name of the material class. + */ + public override getClassName(): string { + return "OpenPBRMaterial"; + } + + /** + * Returns true if alpha blending should be disabled. + */ + protected override get _disableAlphaBlending(): boolean { + return this._transparencyMode === PBRBaseMaterial.PBRMATERIAL_OPAQUE || this._transparencyMode === PBRBaseMaterial.PBRMATERIAL_ALPHATEST; + } + + /** + * @returns whether or not this material should be rendered in alpha blend mode. + */ + public override needAlphaBlending(): boolean { + if (this._hasTransparencyMode) { + return this._transparencyModeIsBlend; + } + + if (this._disableAlphaBlending) { + return false; + } + + return this.geometryOpacity < 1.0 || this.geometryOpacityTexture != null || this._shouldUseAlphaFromAlbedoTexture(); + } + + /** + * @returns whether or not this material should be rendered in alpha test mode. + */ + public override needAlphaTesting(): boolean { + if (this._hasTransparencyMode) { + return this._transparencyModeIsTest; + } + + return this._hasAlphaChannel() && (this._transparencyMode == null || this._transparencyMode === PBRBaseMaterial.PBRMATERIAL_ALPHATEST); + } + + /** + * @returns whether or not the alpha value of the albedo texture should be used for alpha blending. + */ + protected _shouldUseAlphaFromAlbedoTexture(): boolean { + return this.baseColorTexture != null && this.baseColorTexture.hasAlpha && this._useAlphaFromAlbedoTexture && this._transparencyMode !== PBRBaseMaterial.PBRMATERIAL_OPAQUE; + } + + /** + * @returns whether or not there is a usable alpha channel for transparency. + */ + protected _hasAlphaChannel(): boolean { + return this.geometryOpacityTexture != null; + } + + /** + * Makes a duplicate of the current material. + * @param name - name to use for the new material. + * @param cloneTexturesOnlyOnce - if a texture is used in more than one channel (e.g baseColor and opacity), only clone it once and reuse it on the other channels. Default false. + * @param rootUrl defines the root URL to use to load textures + * @returns cloned material instance + */ + public override clone(name: string, cloneTexturesOnlyOnce: boolean = true, rootUrl = ""): OpenPBRMaterial { + const clone = SerializationHelper.Clone(() => new OpenPBRMaterial(name, this.getScene()), this, { cloneTexturesOnlyOnce }); + + clone.id = name; + clone.name = name; + + this.stencil.copyTo(clone.stencil); + + this._clonePlugins(clone, rootUrl); + + return clone; + } + + /** + * Serializes this PBR Material. + * @returns - An object with the serialized material. + */ + public override serialize(): any { + const serializationObject = super.serialize(); + serializationObject.customType = "BABYLON.OpenPBRMaterial"; + + return serializationObject; + } + + // Statics + /** + * Parses a PBR Material from a serialized object. + * @param source - Serialized object. + * @param scene - BJS scene instance. + * @param rootUrl - url for the scene object + * @returns - OpenPBRMaterial + */ + public static override Parse(source: any, scene: Scene, rootUrl: string): OpenPBRMaterial { + const material = SerializationHelper.Parse(() => new OpenPBRMaterial(source.name, scene), source, scene, rootUrl); + + if (source.stencil) { + material.stencil.parse(source.stencil, scene, rootUrl); + } + + Material._ParsePlugins(source, material, scene, rootUrl); + + return material; + } + + /** + * Force shader compilation + * @param mesh - Define the mesh we want to force the compilation for + * @param onCompiled - Define a callback triggered when the compilation completes + * @param options - Define the options used to create the compilation + */ + public override forceCompilation(mesh: AbstractMesh, onCompiled?: (material: Material) => void, options?: Partial): void { + const localOptions = { + clipPlane: false, + useInstances: false, + ...options, + }; + + if (!this._uniformBufferLayoutBuilt) { + this.buildUniformLayout(); + } + + this._callbackPluginEventGeneric(MaterialPluginEvent.GetDefineNames, this._eventInfo); + const checkReady = () => { + if (this._breakShaderLoadedCheck) { + return; + } + + const defines = new OpenPBRMaterialDefines({ + ...(this._eventInfo.defineNames || {}), + ...(this._samplerDefines || {}), + }); + const effect = this._prepareEffect(mesh, defines, undefined, undefined, localOptions.useInstances, localOptions.clipPlane, mesh.hasThinInstances)!; + if (this._onEffectCreatedObservable) { + onCreatedEffectParameters.effect = effect; + onCreatedEffectParameters.subMesh = null; + this._onEffectCreatedObservable.notifyObservers(onCreatedEffectParameters); + } + if (effect.isReady()) { + if (onCompiled) { + onCompiled(this); + } + } else { + effect.onCompileObservable.add(() => { + if (onCompiled) { + onCompiled(this); + } + }); + } + }; + checkReady(); + } + + /** + * Specifies that the submesh is ready to be used. + * @param mesh - BJS mesh. + * @param subMesh - A submesh of the BJS mesh. Used to check if it is ready. + * @param useInstances - Specifies that instances should be used. + * @returns - boolean indicating that the submesh is ready or not. + */ + public override isReadyForSubMesh(mesh: AbstractMesh, subMesh: SubMesh, useInstances?: boolean): boolean { + if (!this._uniformBufferLayoutBuilt) { + this.buildUniformLayout(); + } + + const drawWrapper = subMesh._drawWrapper; + + if (drawWrapper.effect && this.isFrozen) { + if (drawWrapper._wasPreviouslyReady && drawWrapper._wasPreviouslyUsingInstances === useInstances) { + return true; + } + } + + if (!subMesh.materialDefines) { + this._callbackPluginEventGeneric(MaterialPluginEvent.GetDefineNames, this._eventInfo); + subMesh.materialDefines = new OpenPBRMaterialDefines({ + ...(this._eventInfo.defineNames || {}), + ...(this._samplerDefines || {}), + }); + } + + const defines = subMesh.materialDefines; + if (this._isReadyForSubMesh(subMesh)) { + return true; + } + + const scene = this.getScene(); + const engine = scene.getEngine(); + + if (defines._areTexturesDirty) { + this._eventInfo.hasRenderTargetTextures = false; + this._callbackPluginEventHasRenderTargetTextures(this._eventInfo); + this._cacheHasRenderTargetTextures = this._eventInfo.hasRenderTargetTextures; + if (scene.texturesEnabled) { + // Loop through samplers, check MaterialFlag and whether the texture is ready or not. + for (const key in this._samplersList) { + const sampler = this._samplersList[key]; + if (sampler.value) { + if (!sampler.value.isReadyOrNotBlocking()) { + return false; + } + } + } + + const radianceTexture = this._getRadianceTexture(); + if (radianceTexture && MaterialFlags.ReflectionTextureEnabled) { + if (!radianceTexture.isReadyOrNotBlocking()) { + return false; + } + if (radianceTexture.irradianceTexture) { + if (!radianceTexture.irradianceTexture.isReadyOrNotBlocking()) { + return false; + } + } else { + // Not ready until spherical are ready too. + if (!radianceTexture.sphericalPolynomial && radianceTexture.getInternalTexture()?._sphericalPolynomialPromise) { + return false; + } + } + } + + if (this._environmentBRDFTexture && MaterialFlags.ReflectionTextureEnabled) { + // This is blocking. + if (!this._environmentBRDFTexture.isReady()) { + return false; + } + } + } + } + + this._eventInfo.isReadyForSubMesh = true; + this._eventInfo.defines = defines; + this._eventInfo.subMesh = subMesh; + this._callbackPluginEventIsReadyForSubMesh(this._eventInfo); + + if (!this._eventInfo.isReadyForSubMesh) { + return false; + } + + if (defines._areImageProcessingDirty && this._imageProcessingConfiguration) { + if (!this._imageProcessingConfiguration.isReady()) { + return false; + } + } + + // Check if Area Lights have LTC texture. + if (defines["AREALIGHTUSED"]) { + for (let index = 0; index < mesh.lightSources.length; index++) { + if (!mesh.lightSources[index]._isReady()) { + return false; + } + } + } + + if (!engine.getCaps().standardDerivatives && !mesh.isVerticesDataPresent(VertexBuffer.NormalKind)) { + mesh.createNormals(true); + Logger.Warn("OpenPBRMaterial: Normals have been created for the mesh: " + mesh.name); + } + + const previousEffect = subMesh.effect; + const lightDisposed = defines._areLightsDisposed; + let effect = this._prepareEffect(mesh, defines, this.onCompiled, this.onError, useInstances, null, subMesh.getRenderingMesh().hasThinInstances); + + let forceWasNotReadyPreviously = false; + + if (effect) { + if (this._onEffectCreatedObservable) { + onCreatedEffectParameters.effect = effect; + onCreatedEffectParameters.subMesh = subMesh; + this._onEffectCreatedObservable.notifyObservers(onCreatedEffectParameters); + } + + // Use previous effect while new one is compiling + if (this.allowShaderHotSwapping && previousEffect && !effect.isReady()) { + effect = previousEffect; + defines.markAsUnprocessed(); + + forceWasNotReadyPreviously = this.isFrozen; + + if (lightDisposed) { + // re register in case it takes more than one frame. + defines._areLightsDisposed = true; + return false; + } + } else { + scene.resetCachedMaterial(); + subMesh.setEffect(effect, defines, this._materialContext); + } + } + + if (!subMesh.effect || !subMesh.effect.isReady()) { + return false; + } + + defines._renderId = scene.getRenderId(); + drawWrapper._wasPreviouslyReady = forceWasNotReadyPreviously ? false : true; + drawWrapper._wasPreviouslyUsingInstances = !!useInstances; + + this._checkScenePerformancePriority(); + + return true; + } + + /** + * Initializes the uniform buffer layout for the shader. + */ + public override buildUniformLayout(): void { + // Order is important ! + const ubo = this._uniformBuffer; + ubo.addUniform("vTangentSpaceParams", 2); + ubo.addUniform("vLightingIntensity", 4); + + ubo.addUniform("pointSize", 1); + + ubo.addUniform("vDebugMode", 2); + + ubo.addUniform("cameraInfo", 4); + PrepareUniformLayoutForIBL(ubo, true, true, true, true, true); + + Object.values(this._uniformsList).forEach((uniform) => { + ubo.addUniform(uniform.name, uniform.numComponents); + }); + + Object.values(this._samplersList).forEach((sampler) => { + ubo.addUniform(sampler.samplerInfoName, 2); + ubo.addUniform(sampler.samplerMatrixName, 16); + }); + + super.buildUniformLayout(); + } + + /** + * Binds the submesh data. + * @param world - The world matrix. + * @param mesh - The BJS mesh. + * @param subMesh - A submesh of the BJS mesh. + */ + public override bindForSubMesh(world: Matrix, mesh: Mesh, subMesh: SubMesh): void { + const scene = this.getScene(); + + const defines = subMesh.materialDefines; + if (!defines) { + return; + } + + const effect = subMesh.effect; + + if (!effect) { + return; + } + + this._activeEffect = effect; + + // Matrices Mesh. + mesh.getMeshUniformBuffer().bindToEffect(effect, "Mesh"); + mesh.transferToEffect(world); + + const engine = scene.getEngine(); + + // Binding unconditionally + this._uniformBuffer.bindToEffect(effect, "Material"); + + this.prePassConfiguration.bindForSubMesh(this._activeEffect, scene, mesh, world, this.isFrozen); + + MaterialHelperGeometryRendering.Bind(engine.currentRenderPassId, this._activeEffect, mesh, world, this); + + const camera = scene.activeCamera; + if (camera) { + this._uniformBuffer.updateFloat4("cameraInfo", camera.minZ, camera.maxZ, 0, 0); + } else { + this._uniformBuffer.updateFloat4("cameraInfo", 0, 0, 0, 0); + } + + this._eventInfo.subMesh = subMesh; + this._callbackPluginEventHardBindForSubMesh(this._eventInfo); + + // Normal Matrix + if (defines.OBJECTSPACE_NORMALMAP) { + world.toNormalMatrix(this._normalMatrix); + this.bindOnlyNormalMatrix(this._normalMatrix); + } + + const mustRebind = this._mustRebind(scene, effect, subMesh, mesh.visibility); + + // Bones + BindBonesParameters(mesh, this._activeEffect, this.prePassConfiguration); + + let radianceTexture: Nullable = null; + const ubo = this._uniformBuffer; + if (mustRebind) { + this.bindViewProjection(effect); + radianceTexture = this._getRadianceTexture(); + + if (!ubo.useUbo || !this.isFrozen || !ubo.isSync || subMesh._drawWrapper._forceRebindOnNextCall) { + // Texture uniforms + if (scene.texturesEnabled) { + // Loop through samplers and bind info and matrix for each texture. + for (const key in this._samplersList) { + const sampler = this._samplersList[key]; + if (sampler.value) { + ubo.updateFloat2(sampler.samplerInfoName, sampler.value.coordinatesIndex, sampler.value.level); + BindTextureMatrix(sampler.value, ubo, sampler.samplerPrefix); + } + } + + if (this.geometryNormalTexture) { + if (scene._mirroredCameraPosition) { + ubo.updateFloat2("vTangentSpaceParams", this._invertNormalMapX ? 1.0 : -1.0, this._invertNormalMapY ? 1.0 : -1.0); + } else { + ubo.updateFloat2("vTangentSpaceParams", this._invertNormalMapX ? -1.0 : 1.0, this._invertNormalMapY ? -1.0 : 1.0); + } + } + } + + BindIBLParameters(scene, defines, ubo, radianceTexture, this.realTimeFiltering, true, true, true, true, true, Color3.White()); + + // Point size + if (this.pointsCloud) { + ubo.updateFloat("pointSize", this.pointSize); + } + + Object.values(this._uniformsList).forEach((uniform) => { + // If the property actually defines a uniform, update it. + if (uniform.numComponents === 4) { + uniform.populateVectorFromLinkedProperties(TmpVectors.Vector4[0]); + ubo.updateVector4(uniform.name, TmpVectors.Vector4[0]); + } else if (uniform.numComponents === 3) { + uniform.populateVectorFromLinkedProperties(TmpVectors.Vector3[0]); + ubo.updateVector3(uniform.name, TmpVectors.Vector3[0]); + } else if (uniform.numComponents === 2) { + uniform.populateVectorFromLinkedProperties(TmpVectors.Vector2[0]); + ubo.updateFloat2(uniform.name, TmpVectors.Vector2[0].x, TmpVectors.Vector2[0].y); + } else if (uniform.numComponents === 1) { + ubo.updateFloat(uniform.name, uniform.linkedProperties[Object.keys(uniform.linkedProperties)[0]].value); + } + }); + + // Misc + this._lightingInfos.x = this._directIntensity; + this._lightingInfos.y = this.emissionLuminance; + this._lightingInfos.z = this._environmentIntensity * scene.environmentIntensity; + this._lightingInfos.w = 1.0; // This is used to be _specularIntensity. + + ubo.updateVector4("vLightingIntensity", this._lightingInfos); + + ubo.updateFloat2("vDebugMode", this.debugLimit, this.debugFactor); + } + + // Textures + if (scene.texturesEnabled) { + // Loop through samplers and set textures + for (const key in this._samplersList) { + const sampler = this._samplersList[key]; + if (sampler.value) { + ubo.setTexture(sampler.samplerName, sampler.value); + } + } + + BindIBLSamplers(scene, defines, ubo, radianceTexture, this.realTimeFiltering); + + if (defines.ENVIRONMENTBRDF) { + ubo.setTexture("environmentBrdfSampler", this._environmentBRDFTexture); + } + } + + // OIT with depth peeling + if (this.getScene().useOrderIndependentTransparency && this.needAlphaBlendingForMesh(mesh)) { + this.getScene().depthPeelingRenderer!.bind(effect); + } + + this._eventInfo.subMesh = subMesh; + this._callbackPluginEventBindForSubMesh(this._eventInfo); + + // Clip plane + BindClipPlane(this._activeEffect, this, scene); + + this.bindEyePosition(effect); + } else if (scene.getEngine()._features.needToAlwaysBindUniformBuffers) { + this._needToBindSceneUbo = true; + } + + if (mustRebind || !this.isFrozen) { + // Lights + if (scene.lightsEnabled && !this._disableLighting) { + BindLights(scene, mesh, this._activeEffect, defines, this._maxSimultaneousLights); + } + + // View + if ((scene.fogEnabled && mesh.applyFog && scene.fogMode !== Scene.FOGMODE_NONE) || radianceTexture || mesh.receiveShadows || defines.PREPASS) { + this.bindView(effect); + } + + // Fog + BindFogParameters(scene, mesh, this._activeEffect, true); + + // Morph targets + if (defines.NUM_MORPH_INFLUENCERS) { + BindMorphTargetParameters(mesh, this._activeEffect); + } + + if (defines.BAKED_VERTEX_ANIMATION_TEXTURE) { + mesh.bakedVertexAnimationManager?.bind(effect, defines.INSTANCES); + } + + // image processing + this._imageProcessingConfiguration.bind(this._activeEffect); + + // Log. depth + BindLogDepth(defines, this._activeEffect, scene); + } + + this._afterBind(mesh, this._activeEffect, subMesh); + + ubo.update(); + } + + /** + * Returns the animatable textures. + * If material have animatable metallic texture, then reflectivity texture will not be returned, even if it has animations. + * @returns - Array of animatable textures. + */ + public override getAnimatables(): IAnimatable[] { + const results = super.getAnimatables(); + + // Loop through samplers and push animated textures to list. + for (const key in this._samplersList) { + const sampler = this._samplersList[key]; + if (sampler.value && sampler.value.animations && sampler.value.animations.length > 0) { + results.push(sampler.value); + } + } + + if (this._radianceTexture && this._radianceTexture.animations && this._radianceTexture.animations.length > 0) { + results.push(this._radianceTexture); + } + + return results; + } + + /** + * Returns an array of the actively used textures. + * @returns - Array of BaseTextures + */ + public override getActiveTextures(): BaseTexture[] { + const activeTextures = super.getActiveTextures(); + + // Loop through samplers and push active textures + for (const key in this._samplersList) { + const sampler = this._samplersList[key]; + if (sampler.value) { + activeTextures.push(sampler.value); + } + } + + if (this._radianceTexture) { + activeTextures.push(this._radianceTexture); + } + + return activeTextures; + } + + /** + * Checks to see if a texture is used in the material. + * @param texture - Base texture to use. + * @returns - Boolean specifying if a texture is used in the material. + */ + public override hasTexture(texture: BaseTexture): boolean { + if (super.hasTexture(texture)) { + return true; + } + + // Loop through samplers and check each texture for equality + for (const key in this._samplersList) { + const sampler = this._samplersList[key]; + if (sampler.value === texture) { + return true; + } + } + + if (this._radianceTexture === texture) { + return true; + } + + return false; + } + + /** + * Sets the required values to the prepass renderer. + * It can't be sets when subsurface scattering of this material is disabled. + * When scene have ability to enable subsurface prepass effect, it will enable. + * @returns - If prepass is enabled or not. + */ + public override setPrePassRenderer(): boolean { + return false; + } + + /** + * Disposes the resources of the material. + * @param forceDisposeEffect - Forces the disposal of effects. + * @param forceDisposeTextures - Forces the disposal of all textures. + */ + public override dispose(forceDisposeEffect?: boolean, forceDisposeTextures?: boolean): void { + this._breakShaderLoadedCheck = true; + if (forceDisposeTextures) { + if (this._environmentBRDFTexture && this.getScene().environmentBRDFTexture !== this._environmentBRDFTexture) { + this._environmentBRDFTexture.dispose(); + } + + // Loop through samplers and dispose the textures + for (const key in this._samplersList) { + const sampler = this._samplersList[key]; + sampler.value?.dispose(); + } + + this._radianceTexture?.dispose(); + } + + this._renderTargets.dispose(); + + if (this._imageProcessingConfiguration && this._imageProcessingObserver) { + this._imageProcessingConfiguration.onUpdateParameters.remove(this._imageProcessingObserver); + } + + super.dispose(forceDisposeEffect, forceDisposeTextures); + } + + /** + * Returns the texture used for reflections. + * @returns - Reflection texture if present. Otherwise, returns the environment texture. + */ + private _getRadianceTexture(): Nullable { + if (this._radianceTexture) { + return this._radianceTexture; + } + + return this.getScene().environmentTexture; + } + + private _prepareEffect( + mesh: AbstractMesh, + defines: OpenPBRMaterialDefines, + onCompiled: Nullable<(effect: Effect) => void> = null, + onError: Nullable<(effect: Effect, errors: string) => void> = null, + useInstances: Nullable = null, + useClipPlane: Nullable = null, + useThinInstances: boolean + ): Nullable { + this._prepareDefines(mesh, defines, useInstances, useClipPlane, useThinInstances); + + if (!defines.isDirty) { + return null; + } + + defines.markAsProcessed(); + + const scene = this.getScene(); + const engine = scene.getEngine(); + + // Fallbacks + const fallbacks = new EffectFallbacks(); + let fallbackRank = 0; + if (defines.USESPHERICALINVERTEX) { + fallbacks.addFallback(fallbackRank++, "USESPHERICALINVERTEX"); + } + + if (defines.FOG) { + fallbacks.addFallback(fallbackRank, "FOG"); + } + if (defines.SPECULARAA) { + fallbacks.addFallback(fallbackRank, "SPECULARAA"); + } + if (defines.POINTSIZE) { + fallbacks.addFallback(fallbackRank, "POINTSIZE"); + } + if (defines.LOGARITHMICDEPTH) { + fallbacks.addFallback(fallbackRank, "LOGARITHMICDEPTH"); + } + if (defines.PARALLAX) { + fallbacks.addFallback(fallbackRank, "PARALLAX"); + } + if (defines.PARALLAX_RHS) { + fallbacks.addFallback(fallbackRank, "PARALLAX_RHS"); + } + if (defines.PARALLAXOCCLUSION) { + fallbacks.addFallback(fallbackRank++, "PARALLAXOCCLUSION"); + } + + if (defines.ENVIRONMENTBRDF) { + fallbacks.addFallback(fallbackRank++, "ENVIRONMENTBRDF"); + } + + if (defines.TANGENT) { + fallbacks.addFallback(fallbackRank++, "TANGENT"); + } + + fallbackRank = HandleFallbacksForShadows(defines, fallbacks, this._maxSimultaneousLights, fallbackRank++); + + if (defines.SPECULARTERM) { + fallbacks.addFallback(fallbackRank++, "SPECULARTERM"); + } + + if (defines.USESPHERICALFROMREFLECTIONMAP) { + fallbacks.addFallback(fallbackRank++, "USESPHERICALFROMREFLECTIONMAP"); + } + + if (defines.USEIRRADIANCEMAP) { + fallbacks.addFallback(fallbackRank++, "USEIRRADIANCEMAP"); + } + + if (defines.NORMAL) { + fallbacks.addFallback(fallbackRank++, "NORMAL"); + } + + if (defines.VERTEXCOLOR) { + fallbacks.addFallback(fallbackRank++, "VERTEXCOLOR"); + } + + if (defines.MORPHTARGETS) { + fallbacks.addFallback(fallbackRank++, "MORPHTARGETS"); + } + + if (defines.MULTIVIEW) { + fallbacks.addFallback(0, "MULTIVIEW"); + } + + //Attributes + const attribs = [VertexBuffer.PositionKind]; + + if (defines.NORMAL) { + attribs.push(VertexBuffer.NormalKind); + } + + if (defines.TANGENT) { + attribs.push(VertexBuffer.TangentKind); + } + + for (let i = 1; i <= Constants.MAX_SUPPORTED_UV_SETS; ++i) { + if (defines["UV" + i]) { + attribs.push(`uv${i === 1 ? "" : i}`); + } + } + + if (defines.VERTEXCOLOR) { + attribs.push(VertexBuffer.ColorKind); + } + + PrepareAttributesForBones(attribs, mesh, defines, fallbacks); + PrepareAttributesForInstances(attribs, defines); + PrepareAttributesForMorphTargets(attribs, mesh, defines); + PrepareAttributesForBakedVertexAnimation(attribs, mesh, defines); + + let shaderName = "openpbr"; + + const uniforms = [ + "world", + "view", + "viewProjection", + "vEyePosition", + "vLightsType", + "visibility", + "vFogInfos", + "vFogColor", + "pointSize", + "mBones", + "normalMatrix", + "vLightingIntensity", + "logarithmicDepthConstant", + "vTangentSpaceParams", + "boneTextureWidth", + "vDebugMode", + "morphTargetTextureInfo", + "morphTargetTextureIndices", + "cameraInfo", + ]; + + for (const uniformName in Object.keys(this._uniformsList)) { + uniforms.push(uniformName); + } + + const samplers = ["environmentBrdfSampler", "boneSampler", "morphTargets", "oitDepthSampler", "oitFrontColorSampler", "areaLightsLTC1Sampler", "areaLightsLTC2Sampler"]; + + for (const key in this._samplersList) { + const sampler = this._samplersList[key]; + samplers.push(sampler.samplerName); + } + + PrepareUniformsAndSamplersForIBL(uniforms, samplers, true); + + const uniformBuffers = ["Material", "Scene", "Mesh"]; + + const indexParameters = { maxSimultaneousLights: this._maxSimultaneousLights, maxSimultaneousMorphTargets: defines.NUM_MORPH_INFLUENCERS }; + + this._eventInfo.fallbacks = fallbacks; + this._eventInfo.fallbackRank = fallbackRank; + this._eventInfo.defines = defines; + this._eventInfo.uniforms = uniforms; + this._eventInfo.attributes = attribs; + this._eventInfo.samplers = samplers; + this._eventInfo.uniformBuffersNames = uniformBuffers; + this._eventInfo.customCode = undefined; + this._eventInfo.mesh = mesh; + this._eventInfo.indexParameters = indexParameters; + this._callbackPluginEventGeneric(MaterialPluginEvent.PrepareEffect, this._eventInfo); + + MaterialHelperGeometryRendering.AddUniformsAndSamplers(uniforms, samplers); + + PrePassConfiguration.AddUniforms(uniforms); + PrePassConfiguration.AddSamplers(samplers); + AddClipPlaneUniforms(uniforms); + + if (ImageProcessingConfiguration) { + ImageProcessingConfiguration.PrepareUniforms(uniforms, defines); + ImageProcessingConfiguration.PrepareSamplers(samplers, defines); + } + + PrepareUniformsAndSamplersList({ + uniformsNames: uniforms, + uniformBuffersNames: uniformBuffers, + samplers: samplers, + defines: defines, + maxSimultaneousLights: this._maxSimultaneousLights, + }); + + const csnrOptions: ICustomShaderNameResolveOptions = {}; + + if (this.customShaderNameResolve) { + shaderName = this.customShaderNameResolve(shaderName, uniforms, uniformBuffers, samplers, defines, attribs, csnrOptions); + } + + const join = defines.toString(); + const effect = engine.createEffect( + shaderName, + { + attributes: attribs, + uniformsNames: uniforms, + uniformBuffersNames: uniformBuffers, + samplers: samplers, + defines: join, + fallbacks: fallbacks, + onCompiled: onCompiled, + onError: onError, + indexParameters, + processFinalCode: csnrOptions.processFinalCode, + processCodeAfterIncludes: this._eventInfo.customCode, + multiTarget: defines.PREPASS, + shaderLanguage: this._shaderLanguage, + extraInitializationsAsync: this._shadersLoaded + ? undefined + : async () => { + if (this.shaderLanguage === ShaderLanguage.WGSL) { + await Promise.all([import("../../ShadersWGSL/openpbr.vertex"), import("../../ShadersWGSL/openpbr.fragment")]); + } else { + await Promise.all([import("../../Shaders/openpbr.vertex"), import("../../Shaders/openpbr.fragment")]); + } + + this._shadersLoaded = true; + }, + }, + engine + ); + + this._eventInfo.customCode = undefined; + + return effect; + } + + private _prepareDefines( + mesh: AbstractMesh, + defines: OpenPBRMaterialDefines, + useInstances: Nullable = null, + useClipPlane: Nullable = null, + useThinInstances: boolean = false + ): void { + const scene = this.getScene(); + const engine = scene.getEngine(); + + // Lights + PrepareDefinesForLights(scene, mesh, defines, true, this._maxSimultaneousLights, this._disableLighting); + defines._needNormals = true; + + // Multiview + PrepareDefinesForMultiview(scene, defines); + + // PrePass + const oit = this.needAlphaBlendingForMesh(mesh) && this.getScene().useOrderIndependentTransparency; + PrepareDefinesForPrePass(scene, defines, this.canRenderToMRT && !oit); + + // Order independant transparency + PrepareDefinesForOIT(scene, defines, oit); + + MaterialHelperGeometryRendering.PrepareDefines(engine.currentRenderPassId, mesh, defines); + + // Textures + defines.METALLICWORKFLOW = true; + if (defines._areTexturesDirty) { + defines._needUVs = false; + for (let i = 1; i <= Constants.MAX_SUPPORTED_UV_SETS; ++i) { + defines["MAINUV" + i] = false; + } + if (scene.texturesEnabled) { + // TODO - loop through samplers and prepare defines for each texture + for (const key in this._samplersList) { + const sampler = this._samplersList[key]; + if (sampler.value) { + PrepareDefinesForMergedUV(sampler.value, defines, sampler.textureDefine); + defines[sampler.textureDefine + "_GAMMA"] = sampler.value.gammaSpace; + } else { + defines[sampler.textureDefine] = false; + } + } + + const radianceTexture = this._getRadianceTexture(); + const useSHInFragment: boolean = + this._forceIrradianceInFragment || + this.realTimeFiltering || + this._twoSidedLighting || + engine.getCaps().maxVaryingVectors <= 8 || + this._baseDiffuseRoughnessTexture != null; + PrepareDefinesForIBL(scene, radianceTexture, defines, this.realTimeFiltering, this.realTimeFilteringQuality, !useSHInFragment); + + if (MaterialFlags.SpecularTextureEnabled) { + if (this._baseMetalRoughTexture) { + defines.AOSTOREINMETALMAPRED = this._useAmbientOcclusionFromMetallicTextureRed; + } + + defines.SPECULAR_WEIGHT_USE_ALPHA_ONLY = this._useSpecularWeightFromTextureAlpha; + } + + if (this.geometryNormalTexture) { + if (this._useParallax && this.baseColorTexture && MaterialFlags.DiffuseTextureEnabled) { + defines.PARALLAX = true; + defines.PARALLAX_RHS = scene.useRightHandedSystem; + defines.PARALLAXOCCLUSION = !!this._useParallaxOcclusion; + } else { + defines.PARALLAX = false; + } + defines.OBJECTSPACE_NORMALMAP = this._useObjectSpaceNormalMap; + } else { + defines.PARALLAX = false; + defines.PARALLAX_RHS = false; + defines.PARALLAXOCCLUSION = false; + defines.OBJECTSPACE_NORMALMAP = false; + } + + if (this._environmentBRDFTexture && MaterialFlags.ReflectionTextureEnabled) { + defines.ENVIRONMENTBRDF = true; + defines.ENVIRONMENTBRDF_RGBD = this._environmentBRDFTexture.isRGBD; + } else { + defines.ENVIRONMENTBRDF = false; + defines.ENVIRONMENTBRDF_RGBD = false; + } + + if (this._shouldUseAlphaFromAlbedoTexture()) { + defines.ALPHAFROMALBEDO = true; + } else { + defines.ALPHAFROMALBEDO = false; + } + } + + if (this._lightFalloff === PBRBaseMaterial.LIGHTFALLOFF_STANDARD) { + defines.USEPHYSICALLIGHTFALLOFF = false; + defines.USEGLTFLIGHTFALLOFF = false; + } else if (this._lightFalloff === PBRBaseMaterial.LIGHTFALLOFF_GLTF) { + defines.USEPHYSICALLIGHTFALLOFF = false; + defines.USEGLTFLIGHTFALLOFF = true; + } else { + defines.USEPHYSICALLIGHTFALLOFF = true; + defines.USEGLTFLIGHTFALLOFF = false; + } + + if (!this.backFaceCulling && this._twoSidedLighting) { + defines.TWOSIDEDLIGHTING = true; + } else { + defines.TWOSIDEDLIGHTING = false; + } + + // We need it to not invert normals in two sided lighting mode (based on the winding of the face) + defines.MIRRORED = !!scene._mirroredCameraPosition; + + defines.SPECULARAA = engine.getCaps().standardDerivatives && this._enableSpecularAntiAliasing; + } + + if (defines._areTexturesDirty || defines._areMiscDirty) { + defines.ALPHATESTVALUE = `${this._alphaCutOff}${this._alphaCutOff % 1 === 0 ? "." : ""}`; + defines.PREMULTIPLYALPHA = this.alphaMode === Constants.ALPHA_PREMULTIPLIED || this.alphaMode === Constants.ALPHA_PREMULTIPLIED_PORTERDUFF; + defines.ALPHABLEND = this.needAlphaBlendingForMesh(mesh); + } + + if (defines._areImageProcessingDirty && this._imageProcessingConfiguration) { + this._imageProcessingConfiguration.prepareDefines(defines); + } + + defines.FORCENORMALFORWARD = this._forceNormalForward; + + defines.RADIANCEOCCLUSION = this._useRadianceOcclusion; + + defines.HORIZONOCCLUSION = this._useHorizonOcclusion; + + // Misc. + if (defines._areMiscDirty) { + PrepareDefinesForMisc( + mesh, + scene, + this._useLogarithmicDepth, + this.pointsCloud, + this.fogEnabled, + this.needAlphaTestingForMesh(mesh), + defines, + this._applyDecalMapAfterDetailMap + ); + defines.UNLIT = this._unlit || ((this.pointsCloud || this.wireframe) && !mesh.isVerticesDataPresent(VertexBuffer.NormalKind)); + defines.DEBUGMODE = this._debugMode; + } + + // Values that need to be evaluated on every frame + PrepareDefinesForFrameBoundValues(scene, engine, this, defines, useInstances ? true : false, useClipPlane, useThinInstances); + + // External config + this._eventInfo.defines = defines; + this._eventInfo.mesh = mesh; + this._callbackPluginEventPrepareDefinesBeforeAttributes(this._eventInfo); + + // Attribs + PrepareDefinesForAttributes(mesh, defines, true, true, true, this._transparencyMode !== PBRBaseMaterial.PBRMATERIAL_OPAQUE); + + // External config + this._callbackPluginEventPrepareDefines(this._eventInfo); + } +} + +RegisterClass("BABYLON.OpenPBRMaterial", OpenPBRMaterial); diff --git a/packages/dev/core/src/Materials/PBR/pbrBaseMaterial.ts b/packages/dev/core/src/Materials/PBR/pbrBaseMaterial.ts index 63fcb5abbaf..87e78f6bd7f 100644 --- a/packages/dev/core/src/Materials/PBR/pbrBaseMaterial.ts +++ b/packages/dev/core/src/Materials/PBR/pbrBaseMaterial.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import { serializeAsImageProcessingConfiguration, expandToProperty } from "../../Misc/decorators"; -import type { Observer } from "../../Misc/observable"; +import { expandToProperty } from "../../Misc/decorators"; import { Logger } from "../../Misc/logger"; import { SmartArray } from "../../Misc/smartArray"; import { GetEnvironmentBRDFTexture } from "../../Misc/brdfTextureTools"; @@ -16,7 +15,7 @@ import { PBRBRDFConfiguration } from "./pbrBRDFConfiguration"; import { PrePassConfiguration } from "../prePassConfiguration"; import { Color3, TmpColors } from "../../Maths/math.color"; -import type { IImageProcessingConfigurationDefines } from "../../Materials/imageProcessingConfiguration.defines"; +import { ImageProcessingDefinesMixin } from "../../Materials/imageProcessingConfiguration.defines"; import { ImageProcessingConfiguration } from "../../Materials/imageProcessingConfiguration"; import type { Effect, IEffectCreationOptions } from "../../Materials/effect"; import type { IMaterialCompilationOptions, ICustomShaderNameResolveOptions } from "../../Materials/material"; @@ -26,9 +25,7 @@ import { MaterialDefines } from "../../Materials/materialDefines"; import { PushMaterial } from "../../Materials/pushMaterial"; import type { BaseTexture } from "../../Materials/Textures/baseTexture"; -import { Texture } from "../../Materials/Textures/texture"; import type { RenderTargetTexture } from "../../Materials/Textures/renderTargetTexture"; -import type { CubeTexture } from "../../Materials/Textures/cubeTexture"; import { MaterialFlags } from "../materialFlags"; import { Constants } from "../../Engines/constants"; @@ -51,6 +48,8 @@ import { BindLogDepth, BindMorphTargetParameters, BindTextureMatrix, + BindIBLParameters, + BindIBLSamplers, HandleFallbacksForShadows, PrepareAttributesForBakedVertexAnimation, PrepareAttributesForBones, @@ -59,40 +58,35 @@ import { PrepareDefinesForAttributes, PrepareDefinesForFrameBoundValues, PrepareDefinesForLights, + PrepareDefinesForIBL, PrepareDefinesForMergedUV, PrepareDefinesForMisc, PrepareDefinesForMultiview, PrepareDefinesForOIT, PrepareDefinesForPrePass, PrepareUniformsAndSamplersList, + PrepareUniformsAndSamplersForIBL, + PrepareUniformLayoutForIBL, } from "../materialHelper.functions"; import { ShaderLanguage } from "../shaderLanguage"; import { MaterialHelperGeometryRendering } from "../materialHelper.geometryrendering"; +import { UVDefinesMixin } from "../uv.defines"; +import { ImageProcessingMixin } from "../imageProcessing"; const onCreatedEffectParameters = { effect: null as unknown as Effect, subMesh: null as unknown as Nullable }; +class PBRMaterialDefinesBase extends UVDefinesMixin(MaterialDefines) {} + /** * Manages the defines for the PBR Material. * @internal */ -export class PBRMaterialDefines extends MaterialDefines implements IImageProcessingConfigurationDefines { +export class PBRMaterialDefines extends ImageProcessingDefinesMixin(PBRMaterialDefinesBase) { public PBR = true; public NUM_SAMPLES = "0"; public REALTIME_FILTERING = false; public IBL_CDF_FILTERING = false; - public MAINUV1 = false; - public MAINUV2 = false; - public MAINUV3 = false; - public MAINUV4 = false; - public MAINUV5 = false; - public MAINUV6 = false; - public UV1 = false; - public UV2 = false; - public UV3 = false; - public UV4 = false; - public UV5 = false; - public UV6 = false; public ALBEDO = false; public GAMMAALBEDO = false; @@ -256,25 +250,6 @@ export class PBRMaterialDefines extends MaterialDefines implements IImageProcess public NUM_MORPH_INFLUENCERS = 0; public MORPHTARGETS_TEXTURE = false; - public IMAGEPROCESSING = false; - public VIGNETTE = false; - public VIGNETTEBLENDMODEMULTIPLY = false; - public VIGNETTEBLENDMODEOPAQUE = false; - public TONEMAPPING = 0; - public CONTRAST = false; - public COLORCURVES = false; - public COLORGRADING = false; - public COLORGRADING3D = false; - public SAMPLER3DGREENDEPTH = false; - public SAMPLER3DBGRMAP = false; - public DITHER = false; - public IMAGEPROCESSINGPOSTPROCESS = false; - public SKIPFINALCOLORCLAMP = false; - public EXPOSURE = false; - public MULTIVIEW = false; - public ORDER_INDEPENDENT_TRANSPARENCY = false; - public ORDER_INDEPENDENT_TRANSPARENCY_16BITS = false; - public USEPHYSICALLIGHTFALLOFF = false; public USEGLTFLIGHTFALLOFF = false; public TWOSIDEDLIGHTING = false; @@ -324,6 +299,7 @@ export class PBRMaterialDefines extends MaterialDefines implements IImageProcess } } +class PBRBaseMaterialBase extends ImageProcessingMixin(PushMaterial) {} /** * The Physically based material base class of BJS. * @@ -333,7 +309,7 @@ export class PBRMaterialDefines extends MaterialDefines implements IImageProcess * @see [WebGL](https://playground.babylonjs.com/#CGHTSM#1) * @see [WebGPU](https://playground.babylonjs.com/#CGHTSM#2) */ -export abstract class PBRBaseMaterial extends PushMaterial { +export abstract class PBRBaseMaterial extends PBRBaseMaterialBase { /** * PBRMaterialTransparencyMode: No transparency mode, Alpha channel is not use. */ @@ -843,46 +819,6 @@ export abstract class PBRBaseMaterial extends PushMaterial { */ public _enableSpecularAntiAliasing = false; - /** - * Default configuration related to image processing available in the PBR Material. - */ - @serializeAsImageProcessingConfiguration() - protected _imageProcessingConfiguration: ImageProcessingConfiguration; - - /** - * Keep track of the image processing observer to allow dispose and replace. - */ - private _imageProcessingObserver: Nullable> = null; - - /** - * Attaches a new image processing configuration to the PBR Material. - * @param configuration - */ - protected _attachImageProcessingConfiguration(configuration: Nullable): void { - if (configuration === this._imageProcessingConfiguration) { - return; - } - - // Detaches observer. - if (this._imageProcessingConfiguration && this._imageProcessingObserver) { - this._imageProcessingConfiguration.onUpdateParameters.remove(this._imageProcessingObserver); - } - - // Pick the scene configuration if needed. - if (!configuration) { - this._imageProcessingConfiguration = this.getScene().imageProcessingConfiguration; - } else { - this._imageProcessingConfiguration = configuration; - } - - // Attaches observer. - if (this._imageProcessingConfiguration) { - this._imageProcessingObserver = this._imageProcessingConfiguration.onUpdateParameters.add(() => { - this._markAllSubMeshesAsImageProcessingDirty(); - }); - } - } - /** * Stores the available render targets. */ @@ -1475,7 +1411,6 @@ export abstract class PBRBaseMaterial extends PushMaterial { "vMetallicReflectanceFactors", "vEmissiveColor", "visibility", - "vReflectionColor", "vFogInfos", "vFogColor", "pointSize", @@ -1484,12 +1419,8 @@ export abstract class PBRBaseMaterial extends PushMaterial { "vBaseDiffuseRoughnessInfos", "vAmbientInfos", "vOpacityInfos", - "vReflectionInfos", - "vReflectionPosition", - "vReflectionSize", "vEmissiveInfos", "vReflectivityInfos", - "vReflectionFilteringInfo", "vMetallicReflectanceInfos", "vReflectanceInfos", "vMicroSurfaceSamplerInfos", @@ -1501,7 +1432,6 @@ export abstract class PBRBaseMaterial extends PushMaterial { "baseDiffuseRoughnessMatrix", "ambientMatrix", "opacityMatrix", - "reflectionMatrix", "emissiveMatrix", "reflectivityMatrix", "normalMatrix", @@ -1512,26 +1442,6 @@ export abstract class PBRBaseMaterial extends PushMaterial { "reflectanceMatrix", "vLightingIntensity", "logarithmicDepthConstant", - "vSphericalX", - "vSphericalY", - "vSphericalZ", - "vSphericalXX_ZZ", - "vSphericalYY_ZZ", - "vSphericalZZ", - "vSphericalXY", - "vSphericalYZ", - "vSphericalZX", - "vSphericalL00", - "vSphericalL1_1", - "vSphericalL10", - "vSphericalL11", - "vSphericalL2_2", - "vSphericalL2_1", - "vSphericalL20", - "vSphericalL21", - "vSphericalL22", - "vReflectionMicrosurfaceInfos", - "vReflectionDominantDirection", "vTangentSpaceParams", "boneTextureWidth", "vDebugMode", @@ -1550,10 +1460,6 @@ export abstract class PBRBaseMaterial extends PushMaterial { "bumpSampler", "lightmapSampler", "opacitySampler", - "reflectionSampler", - "reflectionSamplerLow", - "reflectionSamplerHigh", - "irradianceSampler", "microSurfaceSampler", "environmentBrdfSampler", "boneSampler", @@ -1562,11 +1468,12 @@ export abstract class PBRBaseMaterial extends PushMaterial { "morphTargets", "oitDepthSampler", "oitFrontColorSampler", - "icdfSampler", "areaLightsLTC1Sampler", "areaLightsLTC2Sampler", ]; + PrepareUniformsAndSamplersForIBL(uniforms, samplers, true); + const uniformBuffers = ["Material", "Scene", "Mesh"]; const indexParameters = { maxSimultaneousLights: this._maxSimultaneousLights, maxSimultaneousMorphTargets: defines.NUM_MORPH_INFLUENCERS }; @@ -1732,126 +1639,13 @@ export abstract class PBRBaseMaterial extends PushMaterial { } const reflectionTexture = this._getReflectionTexture(); - if (reflectionTexture && MaterialFlags.ReflectionTextureEnabled) { - defines.REFLECTION = true; - defines.GAMMAREFLECTION = reflectionTexture.gammaSpace; - defines.RGBDREFLECTION = reflectionTexture.isRGBD; - defines.LODINREFLECTIONALPHA = reflectionTexture.lodLevelInAlpha; - defines.LINEARSPECULARREFLECTION = reflectionTexture.linearSpecularLOD; - defines.USEIRRADIANCEMAP = false; - - if (this.realTimeFiltering && this.realTimeFilteringQuality > 0) { - defines.NUM_SAMPLES = "" + this.realTimeFilteringQuality; - if (engine._features.needTypeSuffixInShaderConstants) { - defines.NUM_SAMPLES = defines.NUM_SAMPLES + "u"; - } - - defines.REALTIME_FILTERING = true; - if (this.getScene().iblCdfGenerator) { - defines.IBL_CDF_FILTERING = true; - } - } else { - defines.REALTIME_FILTERING = false; - } - - defines.INVERTCUBICMAP = reflectionTexture.coordinatesMode === Texture.INVCUBIC_MODE; - defines.REFLECTIONMAP_3D = reflectionTexture.isCube; - defines.REFLECTIONMAP_OPPOSITEZ = defines.REFLECTIONMAP_3D && this.getScene().useRightHandedSystem ? !reflectionTexture.invertZ : reflectionTexture.invertZ; - - defines.REFLECTIONMAP_CUBIC = false; - defines.REFLECTIONMAP_EXPLICIT = false; - defines.REFLECTIONMAP_PLANAR = false; - defines.REFLECTIONMAP_PROJECTION = false; - defines.REFLECTIONMAP_SKYBOX = false; - defines.REFLECTIONMAP_SPHERICAL = false; - defines.REFLECTIONMAP_EQUIRECTANGULAR = false; - defines.REFLECTIONMAP_EQUIRECTANGULAR_FIXED = false; - defines.REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED = false; - - switch (reflectionTexture.coordinatesMode) { - case Texture.EXPLICIT_MODE: - defines.REFLECTIONMAP_EXPLICIT = true; - break; - case Texture.PLANAR_MODE: - defines.REFLECTIONMAP_PLANAR = true; - break; - case Texture.PROJECTION_MODE: - defines.REFLECTIONMAP_PROJECTION = true; - break; - case Texture.SKYBOX_MODE: - defines.REFLECTIONMAP_SKYBOX = true; - break; - case Texture.SPHERICAL_MODE: - defines.REFLECTIONMAP_SPHERICAL = true; - break; - case Texture.EQUIRECTANGULAR_MODE: - defines.REFLECTIONMAP_EQUIRECTANGULAR = true; - break; - case Texture.FIXED_EQUIRECTANGULAR_MODE: - defines.REFLECTIONMAP_EQUIRECTANGULAR_FIXED = true; - break; - case Texture.FIXED_EQUIRECTANGULAR_MIRRORED_MODE: - defines.REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED = true; - break; - case Texture.CUBIC_MODE: - case Texture.INVCUBIC_MODE: - default: - defines.REFLECTIONMAP_CUBIC = true; - defines.USE_LOCAL_REFLECTIONMAP_CUBIC = (reflectionTexture).boundingBoxSize ? true : false; - break; - } - - if (reflectionTexture.coordinatesMode !== Texture.SKYBOX_MODE) { - if (reflectionTexture.irradianceTexture) { - 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; - 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; - } - } - } - } else { - defines.REFLECTION = false; - defines.REFLECTIONMAP_3D = false; - defines.REFLECTIONMAP_SPHERICAL = false; - defines.REFLECTIONMAP_PLANAR = false; - defines.REFLECTIONMAP_CUBIC = false; - defines.USE_LOCAL_REFLECTIONMAP_CUBIC = false; - defines.REFLECTIONMAP_PROJECTION = false; - defines.REFLECTIONMAP_SKYBOX = false; - defines.REFLECTIONMAP_EXPLICIT = false; - defines.REFLECTIONMAP_EQUIRECTANGULAR = false; - defines.REFLECTIONMAP_EQUIRECTANGULAR_FIXED = false; - defines.REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED = false; - 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; - defines.GAMMAREFLECTION = false; - defines.RGBDREFLECTION = false; - defines.LINEARSPECULARREFLECTION = false; - } + const useSHInFragment: boolean = + this._forceIrradianceInFragment || + this.realTimeFiltering || + this._twoSidedLighting || + engine.getCaps().maxVaryingVectors <= 8 || + this._baseDiffuseRoughnessTexture != null; + PrepareDefinesForIBL(scene, reflectionTexture, defines, this.realTimeFiltering, this.realTimeFilteringQuality, !useSHInFragment); if (this._lightmapTexture && MaterialFlags.LightmapTextureEnabled) { PrepareDefinesForMergedUV(this._lightmapTexture, defines, "LIGHTMAP"); @@ -2090,10 +1884,6 @@ export abstract class PBRBaseMaterial extends PushMaterial { ubo.addUniform("vLightmapInfos", 2); ubo.addUniform("vReflectivityInfos", 3); ubo.addUniform("vMicroSurfaceSamplerInfos", 2); - ubo.addUniform("vReflectionInfos", 2); - ubo.addUniform("vReflectionFilteringInfo", 2); - ubo.addUniform("vReflectionPosition", 3); - ubo.addUniform("vReflectionSize", 3); ubo.addUniform("vBumpInfos", 3); ubo.addUniform("albedoMatrix", 16); ubo.addUniform("baseWeightMatrix", 16); @@ -2106,16 +1896,11 @@ export abstract class PBRBaseMaterial extends PushMaterial { ubo.addUniform("microSurfaceSamplerMatrix", 16); ubo.addUniform("bumpMatrix", 16); ubo.addUniform("vTangentSpaceParams", 2); - ubo.addUniform("reflectionMatrix", 16); - - 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); @@ -2129,28 +1914,8 @@ export abstract class PBRBaseMaterial extends PushMaterial { ubo.addUniform("vReflectanceInfos", 2); ubo.addUniform("reflectanceMatrix", 16); - ubo.addUniform("vSphericalL00", 3); - ubo.addUniform("vSphericalL1_1", 3); - ubo.addUniform("vSphericalL10", 3); - ubo.addUniform("vSphericalL11", 3); - ubo.addUniform("vSphericalL2_2", 3); - ubo.addUniform("vSphericalL2_1", 3); - ubo.addUniform("vSphericalL20", 3); - ubo.addUniform("vSphericalL21", 3); - ubo.addUniform("vSphericalL22", 3); - - ubo.addUniform("vSphericalX", 3); - ubo.addUniform("vSphericalY", 3); - ubo.addUniform("vSphericalZ", 3); - ubo.addUniform("vSphericalXX_ZZ", 3); - ubo.addUniform("vSphericalYY_ZZ", 3); - ubo.addUniform("vSphericalZZ", 3); - ubo.addUniform("vSphericalXY", 3); - ubo.addUniform("vSphericalYZ", 3); - ubo.addUniform("vSphericalZX", 3); - ubo.addUniform("cameraInfo", 4); - + PrepareUniformLayoutForIBL(ubo, true, true, true, true, true); super.buildUniformLayout(); } @@ -2250,73 +2015,6 @@ export abstract class PBRBaseMaterial extends PushMaterial { BindTextureMatrix(this._opacityTexture, ubo, "opacity"); } - if (reflectionTexture && MaterialFlags.ReflectionTextureEnabled) { - ubo.updateMatrix("reflectionMatrix", reflectionTexture.getReflectionTextureMatrix()); - ubo.updateFloat2("vReflectionInfos", reflectionTexture.level * scene.iblIntensity, 0); - - if ((reflectionTexture).boundingBoxSize) { - const cubeTexture = reflectionTexture; - - ubo.updateVector3("vReflectionPosition", cubeTexture.boundingBoxPosition); - ubo.updateVector3("vReflectionSize", cubeTexture.boundingBoxSize); - } - - if (this.realTimeFiltering) { - const width = reflectionTexture.getSize().width; - ubo.updateFloat2("vReflectionFilteringInfo", width, Math.log2(width)); - } - - if (!defines.USEIRRADIANCEMAP) { - const polynomials = reflectionTexture.sphericalPolynomial; - if (defines.USESPHERICALFROMREFLECTIONMAP && polynomials) { - if (defines.SPHERICAL_HARMONICS) { - const preScaledHarmonics = polynomials.preScaledHarmonics; - ubo.updateVector3("vSphericalL00", preScaledHarmonics.l00); - ubo.updateVector3("vSphericalL1_1", preScaledHarmonics.l1_1); - ubo.updateVector3("vSphericalL10", preScaledHarmonics.l10); - ubo.updateVector3("vSphericalL11", preScaledHarmonics.l11); - ubo.updateVector3("vSphericalL2_2", preScaledHarmonics.l2_2); - ubo.updateVector3("vSphericalL2_1", preScaledHarmonics.l2_1); - ubo.updateVector3("vSphericalL20", preScaledHarmonics.l20); - ubo.updateVector3("vSphericalL21", preScaledHarmonics.l21); - ubo.updateVector3("vSphericalL22", preScaledHarmonics.l22); - } else { - ubo.updateFloat3("vSphericalX", polynomials.x.x, polynomials.x.y, polynomials.x.z); - ubo.updateFloat3("vSphericalY", polynomials.y.x, polynomials.y.y, polynomials.y.z); - ubo.updateFloat3("vSphericalZ", polynomials.z.x, polynomials.z.y, polynomials.z.z); - ubo.updateFloat3( - "vSphericalXX_ZZ", - polynomials.xx.x - polynomials.zz.x, - polynomials.xx.y - polynomials.zz.y, - polynomials.xx.z - polynomials.zz.z - ); - ubo.updateFloat3( - "vSphericalYY_ZZ", - polynomials.yy.x - polynomials.zz.x, - polynomials.yy.y - polynomials.zz.y, - polynomials.yy.z - polynomials.zz.z - ); - ubo.updateFloat3("vSphericalZZ", polynomials.zz.x, polynomials.zz.y, polynomials.zz.z); - ubo.updateFloat3("vSphericalXY", polynomials.xy.x, polynomials.xy.y, polynomials.xy.z); - ubo.updateFloat3("vSphericalYZ", polynomials.yz.x, polynomials.yz.y, polynomials.yz.z); - 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 && defines.USE_IRRADIANCE_DOMINANT_DIRECTION) { - ubo.updateVector3("vReflectionDominantDirection", reflectionTexture.irradianceTexture!._dominantDirection!); - } - } - - ubo.updateFloat3( - "vReflectionMicrosurfaceInfos", - reflectionTexture.getSize().width, - reflectionTexture.lodGenerationScale, - reflectionTexture.lodGenerationOffset - ); - } - if (this._emissiveTexture && MaterialFlags.EmissiveTextureEnabled) { ubo.updateFloat2("vEmissiveInfos", this._emissiveTexture.coordinatesIndex, this._emissiveTexture.level); BindTextureMatrix(this._emissiveTexture, ubo, "emissive"); @@ -2364,6 +2062,8 @@ export abstract class PBRBaseMaterial extends PushMaterial { } } + BindIBLParameters(scene, defines, ubo, reflectionTexture, this.realTimeFiltering, true, true, true, true, true, this._reflectionColor); + // Point size if (this.pointsCloud) { ubo.updateFloat("pointSize", this.pointSize); @@ -2388,7 +2088,7 @@ export abstract class PBRBaseMaterial extends PushMaterial { } ubo.updateColor3("vEmissiveColor", MaterialFlags.EmissiveTextureEnabled ? this._emissiveColor : Color3.BlackReadOnly); - ubo.updateColor3("vReflectionColor", this._reflectionColor); + if (!defines.SS_REFRACTION && this.subSurface?._linkRefractionWithTransparency) { ubo.updateColor4("vAlbedoColor", this._albedoColor, 1); } else { @@ -2436,25 +2136,7 @@ export abstract class PBRBaseMaterial extends PushMaterial { ubo.setTexture("opacitySampler", this._opacityTexture); } - if (reflectionTexture && MaterialFlags.ReflectionTextureEnabled) { - if (defines.LODBASEDMICROSFURACE) { - ubo.setTexture("reflectionSampler", reflectionTexture); - } else { - ubo.setTexture("reflectionSampler", reflectionTexture._lodTextureMid || reflectionTexture); - ubo.setTexture("reflectionSamplerLow", reflectionTexture._lodTextureLow || reflectionTexture); - ubo.setTexture("reflectionSamplerHigh", reflectionTexture._lodTextureHigh || reflectionTexture); - } - - if (defines.USEIRRADIANCEMAP) { - ubo.setTexture("irradianceSampler", reflectionTexture.irradianceTexture); - } - - //if realtime filtering and using CDF maps, set them. - const cdfGenerator = this.getScene().iblCdfGenerator; - if (this.realTimeFiltering && cdfGenerator) { - ubo.setTexture("icdfSampler", cdfGenerator.getIcdfTexture()); - } - } + BindIBLSamplers(scene, defines, ubo, reflectionTexture, this.realTimeFiltering); if (defines.ENVIRONMENTBRDF) { ubo.setTexture("environmentBrdfSampler", this._environmentBRDFTexture); diff --git a/packages/dev/core/src/Materials/PBR/pbrMaterial.ts b/packages/dev/core/src/Materials/PBR/pbrMaterial.ts index 212a3a0c30b..366acf92344 100644 --- a/packages/dev/core/src/Materials/PBR/pbrMaterial.ts +++ b/packages/dev/core/src/Materials/PBR/pbrMaterial.ts @@ -3,8 +3,6 @@ import { GetEnvironmentBRDFTexture } from "../../Misc/brdfTextureTools"; import type { Nullable } from "../../types"; import type { Scene } from "../../scene"; import { Color3 } from "../../Maths/math.color"; -import type { ImageProcessingConfiguration } from "../../Materials/imageProcessingConfiguration"; -import type { ColorCurves } from "../../Materials/colorCurves"; import type { BaseTexture } from "../../Materials/Textures/baseTexture"; import { PBRBaseMaterial } from "./pbrBaseMaterial"; import { RegisterClass } from "../../Misc/typeStore"; @@ -657,127 +655,6 @@ export class PBRMaterial extends PBRBaseMaterial { @expandToProperty("_markAllSubMeshesAsMiscDirty") public applyDecalMapAfterDetailMap = false; - /** - * Gets the image processing configuration used either in this material. - */ - public get imageProcessingConfiguration(): ImageProcessingConfiguration { - return this._imageProcessingConfiguration; - } - - /** - * Sets the Default image processing configuration used either in the this material. - * - * If sets to null, the scene one is in use. - */ - public set imageProcessingConfiguration(value: ImageProcessingConfiguration) { - this._attachImageProcessingConfiguration(value); - - // Ensure the effect will be rebuilt. - this._markAllSubMeshesAsImageProcessingDirty(); - } - - /** - * Gets whether the color curves effect is enabled. - */ - public get cameraColorCurvesEnabled(): boolean { - return this.imageProcessingConfiguration.colorCurvesEnabled; - } - /** - * Sets whether the color curves effect is enabled. - */ - public set cameraColorCurvesEnabled(value: boolean) { - this.imageProcessingConfiguration.colorCurvesEnabled = value; - } - - /** - * Gets whether the color grading effect is enabled. - */ - public get cameraColorGradingEnabled(): boolean { - return this.imageProcessingConfiguration.colorGradingEnabled; - } - /** - * Gets whether the color grading effect is enabled. - */ - public set cameraColorGradingEnabled(value: boolean) { - this.imageProcessingConfiguration.colorGradingEnabled = value; - } - - /** - * Gets whether tonemapping is enabled or not. - */ - public get cameraToneMappingEnabled(): boolean { - return this._imageProcessingConfiguration.toneMappingEnabled; - } - /** - * Sets whether tonemapping is enabled or not - */ - public set cameraToneMappingEnabled(value: boolean) { - this._imageProcessingConfiguration.toneMappingEnabled = value; - } - - /** - * The camera exposure used on this material. - * This property is here and not in the camera to allow controlling exposure without full screen post process. - * This corresponds to a photographic exposure. - */ - public get cameraExposure(): number { - return this._imageProcessingConfiguration.exposure; - } - /** - * The camera exposure used on this material. - * This property is here and not in the camera to allow controlling exposure without full screen post process. - * This corresponds to a photographic exposure. - */ - public set cameraExposure(value: number) { - this._imageProcessingConfiguration.exposure = value; - } - - /** - * Gets The camera contrast used on this material. - */ - public get cameraContrast(): number { - return this._imageProcessingConfiguration.contrast; - } - - /** - * Sets The camera contrast used on this material. - */ - public set cameraContrast(value: number) { - this._imageProcessingConfiguration.contrast = value; - } - - /** - * Gets the Color Grading 2D Lookup Texture. - */ - public get cameraColorGradingTexture(): Nullable { - return this._imageProcessingConfiguration.colorGradingTexture; - } - /** - * Sets the Color Grading 2D Lookup Texture. - */ - public set cameraColorGradingTexture(value: Nullable) { - this._imageProcessingConfiguration.colorGradingTexture = value; - } - - /** - * The color grading curves provide additional color adjustment that is applied after any color grading transform (3D LUT). - * They allow basic adjustment of saturation and small exposure adjustments, along with color filter tinting to provide white balance adjustment or more stylistic effects. - * These are similar to controls found in many professional imaging or colorist software. The global controls are applied to the entire image. For advanced tuning, extra controls are provided to adjust the shadow, midtone and highlight areas of the image; - * corresponding to low luminance, medium luminance, and high luminance areas respectively. - */ - public get cameraColorCurves(): Nullable { - return this._imageProcessingConfiguration.colorCurves; - } - /** - * The color grading curves provide additional color adjustment that is applied after any color grading transform (3D LUT). - * They allow basic adjustment of saturation and small exposure adjustments, along with color filter tinting to provide white balance adjustment or more stylistic effects. - * These are similar to controls found in many professional imaging or colorist software. The global controls are applied to the entire image. For advanced tuning, extra controls are provided to adjust the shadow, midtone and highlight areas of the image; - * corresponding to low luminance, medium luminance, and high luminance areas respectively. - */ - public set cameraColorCurves(value: Nullable) { - this._imageProcessingConfiguration.colorCurves = value; - } - /** * Instantiates a new PBRMaterial instance. * diff --git a/packages/dev/core/src/Materials/imageProcessing.ts b/packages/dev/core/src/Materials/imageProcessing.ts new file mode 100644 index 00000000000..2bcba2bd245 --- /dev/null +++ b/packages/dev/core/src/Materials/imageProcessing.ts @@ -0,0 +1,193 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { serializeAsImageProcessingConfiguration } from "../Misc/decorators"; +import type { Nullable } from "../types"; +import type { ImageProcessingConfiguration } from "./imageProcessingConfiguration"; +import type { Observer } from "../Misc/observable"; +import type { BaseTexture } from "../Materials/Textures/baseTexture"; +import type { ColorCurves } from "../Materials/colorCurves"; + +type ImageProcessingMixinConstructor = new (...args: any[]) => T; + +/** + * Mixin to add Image processing defines to your material defines + * @internal + */ +export function ImageProcessingMixin(base: Tbase) { + return class extends base { + /** + * Constructor for the ImageProcessingMixin. + * @param args - arguments to pass to the base class constructor + */ + constructor(...args: any[]) { + super(...args); + // Decorators don't work on this annonymous class + // so I'm setting this up manually. + const fn = serializeAsImageProcessingConfiguration(); + fn.call(this, this, "_imageProcessingConfiguration"); + } + /** + * Default configuration related to image processing available in the standard Material. + */ + public _imageProcessingConfiguration: ImageProcessingConfiguration; + + /** + * Gets the image processing configuration used either in this material. + */ + public get imageProcessingConfiguration(): ImageProcessingConfiguration { + return this._imageProcessingConfiguration; + } + + /** + * Sets the Default image processing configuration used either in the this material. + * + * If sets to null, the scene one is in use. + */ + public set imageProcessingConfiguration(value: ImageProcessingConfiguration) { + this._attachImageProcessingConfiguration(value); + + // Ensure the effect will be rebuilt. + if ((this as any)._markAllSubMeshesAsImageProcessingDirty) { + (this as any)._markAllSubMeshesAsImageProcessingDirty(); + } + } + + /** + * Keep track of the image processing observer to allow dispose and replace. + */ + public _imageProcessingObserver: Nullable>; + + /** + * Attaches a new image processing configuration to the Standard Material. + * @param configuration + */ + public _attachImageProcessingConfiguration(configuration: Nullable): void { + if (configuration === this._imageProcessingConfiguration) { + return; + } + + // Detaches observer + if (this._imageProcessingConfiguration && this._imageProcessingObserver) { + this._imageProcessingConfiguration.onUpdateParameters.remove(this._imageProcessingObserver); + } + + // Pick the scene configuration if needed + if (!configuration && (this as any).getScene) { + this._imageProcessingConfiguration = (this as any).getScene().imageProcessingConfiguration; + } else if (configuration) { + this._imageProcessingConfiguration = configuration; + } + + // Attaches observer + if (this._imageProcessingConfiguration) { + this._imageProcessingObserver = this._imageProcessingConfiguration.onUpdateParameters.add(() => { + // Ensure the effect will be rebuilt. + if ((this as any)._markAllSubMeshesAsImageProcessingDirty) { + (this as any)._markAllSubMeshesAsImageProcessingDirty(); + } + }); + } + } + + /** + * Gets whether the color curves effect is enabled. + */ + public get cameraColorCurvesEnabled(): boolean { + return this.imageProcessingConfiguration.colorCurvesEnabled; + } + /** + * Sets whether the color curves effect is enabled. + */ + public set cameraColorCurvesEnabled(value: boolean) { + this.imageProcessingConfiguration.colorCurvesEnabled = value; + } + + /** + * Gets whether the color grading effect is enabled. + */ + public get cameraColorGradingEnabled(): boolean { + return this.imageProcessingConfiguration.colorGradingEnabled; + } + /** + * Gets whether the color grading effect is enabled. + */ + public set cameraColorGradingEnabled(value: boolean) { + this.imageProcessingConfiguration.colorGradingEnabled = value; + } + + /** + * Gets whether tonemapping is enabled or not. + */ + public get cameraToneMappingEnabled(): boolean { + return this._imageProcessingConfiguration.toneMappingEnabled; + } + /** + * Sets whether tonemapping is enabled or not + */ + public set cameraToneMappingEnabled(value: boolean) { + this._imageProcessingConfiguration.toneMappingEnabled = value; + } + + /** + * The camera exposure used on this material. + * This property is here and not in the camera to allow controlling exposure without full screen post process. + * This corresponds to a photographic exposure. + */ + public get cameraExposure(): number { + return this._imageProcessingConfiguration.exposure; + } + /** + * The camera exposure used on this material. + * This property is here and not in the camera to allow controlling exposure without full screen post process. + * This corresponds to a photographic exposure. + */ + public set cameraExposure(value: number) { + this._imageProcessingConfiguration.exposure = value; + } + + /** + * Gets The camera contrast used on this material. + */ + public get cameraContrast(): number { + return this._imageProcessingConfiguration.contrast; + } + + /** + * Sets The camera contrast used on this material. + */ + public set cameraContrast(value: number) { + this._imageProcessingConfiguration.contrast = value; + } + + /** + * Gets the Color Grading 2D Lookup Texture. + */ + public get cameraColorGradingTexture(): Nullable { + return this._imageProcessingConfiguration.colorGradingTexture; + } + /** + * Sets the Color Grading 2D Lookup Texture. + */ + public set cameraColorGradingTexture(value: Nullable) { + this._imageProcessingConfiguration.colorGradingTexture = value; + } + + /** + * The color grading curves provide additional color adjustmnent that is applied after any color grading transform (3D LUT). + * They allow basic adjustment of saturation and small exposure adjustments, along with color filter tinting to provide white balance adjustment or more stylistic effects. + * These are similar to controls found in many professional imaging or colorist software. The global controls are applied to the entire image. For advanced tuning, extra controls are provided to adjust the shadow, midtone and highlight areas of the image; + * corresponding to low luminance, medium luminance, and high luminance areas respectively. + */ + public get cameraColorCurves(): Nullable { + return this._imageProcessingConfiguration.colorCurves; + } + /** + * The color grading curves provide additional color adjustment that is applied after any color grading transform (3D LUT). + * They allow basic adjustment of saturation and small exposure adjustments, along with color filter tinting to provide white balance adjustment or more stylistic effects. + * These are similar to controls found in many professional imaging or colorist software. The global controls are applied to the entire image. For advanced tuning, extra controls are provided to adjust the shadow, midtone and highlight areas of the image; + * corresponding to low luminance, medium luminance, and high luminance areas respectively. + */ + public set cameraColorCurves(value: Nullable) { + this._imageProcessingConfiguration.colorCurves = value; + } + }; +} diff --git a/packages/dev/core/src/Materials/imageProcessingConfiguration.defines.ts b/packages/dev/core/src/Materials/imageProcessingConfiguration.defines.ts index 53f897fbdc2..7ddc3b16bbf 100644 --- a/packages/dev/core/src/Materials/imageProcessingConfiguration.defines.ts +++ b/packages/dev/core/src/Materials/imageProcessingConfiguration.defines.ts @@ -22,6 +22,36 @@ export interface IImageProcessingConfigurationDefines { SKIPFINALCOLORCLAMP: boolean; } +type ImageProcessingDefinesMixinConstructor = new (...args: any[]) => T; + +/** + * Mixin to add Image processing defines to your material defines + * @internal + */ +export function ImageProcessingDefinesMixin(base: Tbase) { + return class extends base implements IImageProcessingConfigurationDefines { + // Implement all members of IImageProcessingConfigurationDefines here + public IMAGEPROCESSING = false; + public VIGNETTE = false; + public VIGNETTEBLENDMODEMULTIPLY = false; + public VIGNETTEBLENDMODEOPAQUE = false; + public TONEMAPPING = 0; + public CONTRAST = false; + public COLORCURVES = false; + public COLORGRADING = false; + public COLORGRADING3D = false; + public SAMPLER3DGREENDEPTH = false; + public SAMPLER3DBGRMAP = false; + public DITHER = false; + public IMAGEPROCESSINGPOSTPROCESS = false; + public SKIPFINALCOLORCLAMP = false; + public EXPOSURE = false; + public MULTIVIEW = false; + public ORDER_INDEPENDENT_TRANSPARENCY = false; + public ORDER_INDEPENDENT_TRANSPARENCY_16BITS = false; + }; +} + /** * @internal */ diff --git a/packages/dev/core/src/Materials/index.ts b/packages/dev/core/src/Materials/index.ts index 3b2e6566225..fa8f77ae048 100644 --- a/packages/dev/core/src/Materials/index.ts +++ b/packages/dev/core/src/Materials/index.ts @@ -5,6 +5,7 @@ export * from "./iEffectFallbacks"; export * from "./effectFallbacks"; export * from "./effect"; export * from "./fresnelParameters"; +export * from "./imageProcessing"; export * from "./imageProcessingConfiguration"; export * from "./material"; export * from "./materialDefines"; @@ -40,6 +41,7 @@ export * from "./GaussianSplatting/gaussianSplattingMaterial"; export * from "./materialHelper.functions"; export * from "./materialHelper.geometryrendering"; export * from "./materialStencilState"; +export * from "./uv.defines"; import "./material.decalMap"; // async-loaded shaders diff --git a/packages/dev/core/src/Materials/materialHelper.functions.ts b/packages/dev/core/src/Materials/materialHelper.functions.ts index 976429e345f..ae98e59411e 100644 --- a/packages/dev/core/src/Materials/materialHelper.functions.ts +++ b/packages/dev/core/src/Materials/materialHelper.functions.ts @@ -17,8 +17,12 @@ import type { AbstractEngine } from "../Engines/abstractEngine"; import type { Material } from "./material"; import type { Nullable } from "../types"; import { PrepareDefinesForClipPlanes } from "./clipPlaneMaterialHelper"; -import type { MorphTargetManager } from "core/Morph/morphTargetManager"; +import type { MorphTargetManager } from "../Morph/morphTargetManager"; import type { IColor3Like } from "core/Maths"; +import { MaterialFlags } from "./materialFlags"; +import { Texture } from "./Textures/texture"; +import type { CubeTexture } from "./Textures/cubeTexture"; +import { Color3 } from "core/Maths/math.color"; // Temps const TempFogColor: IColor3Like = { r: 0, g: 0, b: 0 }; @@ -266,6 +270,123 @@ export function BindSceneUniformBuffer(effect: Effect, sceneUbo: UniformBuffer): sceneUbo.bindToEffect(effect, "Scene"); } +/** + * Update parameters for IBL + * @param scene The scene + * @param defines The list of shader defines for the material + * @param ubo The uniform buffer to update + * @param reflectionTexture The IBL texture + * @param realTimeFiltering Whether realtime filtering of IBL texture is being used + * @param supportTextureInfo Whether the texture info is supported + * @param supportLocalProjection Whether local projection is supported + * @param usePBR Whether PBR is being used + * @param supportSH Whether spherical harmonics are supported + * @param useColor Whether to use the reflection color + * @param reflectionColor The color to use for the reflection + */ +export function BindIBLParameters( + scene: Scene, + defines: any, + ubo: UniformBuffer, + reflectionTexture: Nullable = null, + realTimeFiltering: boolean = false, + supportTextureInfo: boolean = false, + supportLocalProjection: boolean = false, + usePBR: boolean = false, + supportSH: boolean = false, + useColor: boolean = false, + reflectionColor: Color3 = Color3.White() +): void { + if (scene.texturesEnabled) { + if (reflectionTexture && MaterialFlags.ReflectionTextureEnabled) { + ubo.updateMatrix("reflectionMatrix", reflectionTexture.getReflectionTextureMatrix()); + ubo.updateFloat2("vReflectionInfos", reflectionTexture.level * scene.iblIntensity, 0); + + if (supportLocalProjection && (reflectionTexture).boundingBoxSize) { + const cubeTexture = reflectionTexture; + + ubo.updateVector3("vReflectionPosition", cubeTexture.boundingBoxPosition); + ubo.updateVector3("vReflectionSize", cubeTexture.boundingBoxSize); + } + + if (realTimeFiltering) { + const width = reflectionTexture.getSize().width; + ubo.updateFloat2("vReflectionFilteringInfo", width, Math.log2(width)); + } + + if (supportSH && !defines.USEIRRADIANCEMAP) { + const polynomials = reflectionTexture.sphericalPolynomial; + if (defines.USESPHERICALFROMREFLECTIONMAP && polynomials) { + if (defines.SPHERICAL_HARMONICS) { + const preScaledHarmonics = polynomials.preScaledHarmonics; + ubo.updateVector3("vSphericalL00", preScaledHarmonics.l00); + ubo.updateVector3("vSphericalL1_1", preScaledHarmonics.l1_1); + ubo.updateVector3("vSphericalL10", preScaledHarmonics.l10); + ubo.updateVector3("vSphericalL11", preScaledHarmonics.l11); + ubo.updateVector3("vSphericalL2_2", preScaledHarmonics.l2_2); + ubo.updateVector3("vSphericalL2_1", preScaledHarmonics.l2_1); + ubo.updateVector3("vSphericalL20", preScaledHarmonics.l20); + ubo.updateVector3("vSphericalL21", preScaledHarmonics.l21); + ubo.updateVector3("vSphericalL22", preScaledHarmonics.l22); + } else { + ubo.updateFloat3("vSphericalX", polynomials.x.x, polynomials.x.y, polynomials.x.z); + ubo.updateFloat3("vSphericalY", polynomials.y.x, polynomials.y.y, polynomials.y.z); + ubo.updateFloat3("vSphericalZ", polynomials.z.x, polynomials.z.y, polynomials.z.z); + ubo.updateFloat3("vSphericalXX_ZZ", polynomials.xx.x - polynomials.zz.x, polynomials.xx.y - polynomials.zz.y, polynomials.xx.z - polynomials.zz.z); + ubo.updateFloat3("vSphericalYY_ZZ", polynomials.yy.x - polynomials.zz.x, polynomials.yy.y - polynomials.zz.y, polynomials.yy.z - polynomials.zz.z); + ubo.updateFloat3("vSphericalZZ", polynomials.zz.x, polynomials.zz.y, polynomials.zz.z); + ubo.updateFloat3("vSphericalXY", polynomials.xy.x, polynomials.xy.y, polynomials.xy.z); + ubo.updateFloat3("vSphericalYZ", polynomials.yz.x, polynomials.yz.y, polynomials.yz.z); + ubo.updateFloat3("vSphericalZX", polynomials.zx.x, polynomials.zx.y, polynomials.zx.z); + } + } + } else if (usePBR) { + // If we're using an irradiance map with a dominant direction assigned, set it. + if (defines.USEIRRADIANCEMAP && defines.USE_IRRADIANCE_DOMINANT_DIRECTION) { + ubo.updateVector3("vReflectionDominantDirection", reflectionTexture.irradianceTexture!._dominantDirection!); + } + } + + if (supportTextureInfo) { + ubo.updateFloat3("vReflectionMicrosurfaceInfos", reflectionTexture.getSize().width, reflectionTexture.lodGenerationScale, reflectionTexture.lodGenerationOffset); + } + } + } + if (useColor) { + ubo.updateColor3("vReflectionColor", reflectionColor); + } +} + +/** + * Update parameters for IBL + * @param scene The scene + * @param defines The list of shader defines for the material + * @param ubo The uniform buffer to update + * @param reflectionTexture The IBL texture + * @param realTimeFiltering Whether realtime filtering of IBL texture is being used + */ +export function BindIBLSamplers(scene: Scene, defines: any, ubo: UniformBuffer, reflectionTexture: Nullable = null, realTimeFiltering: boolean = false): void { + if (reflectionTexture && MaterialFlags.ReflectionTextureEnabled) { + if (defines.LODBASEDMICROSFURACE) { + ubo.setTexture("reflectionSampler", reflectionTexture); + } else { + ubo.setTexture("reflectionSampler", reflectionTexture._lodTextureMid || reflectionTexture); + ubo.setTexture("reflectionSamplerLow", reflectionTexture._lodTextureLow || reflectionTexture); + ubo.setTexture("reflectionSamplerHigh", reflectionTexture._lodTextureHigh || reflectionTexture); + } + + if (defines.USEIRRADIANCEMAP) { + ubo.setTexture("irradianceSampler", reflectionTexture.irradianceTexture); + } + + //if realtime filtering and using CDF maps, set them. + const cdfGenerator = scene.iblCdfGenerator; + if (realTimeFiltering && cdfGenerator) { + ubo.setTexture("icdfSampler", cdfGenerator.getIcdfTexture()); + } + } +} + /** * Helps preparing the defines values about the UVs in used in the effect. * UVs are shared as much as we can across channels in the shaders. @@ -612,6 +733,141 @@ export function PrepareDefinesForLights(scene: Scene, mesh: AbstractMesh, define return state.needNormals; } +/** + * Prepare defines relating to IBL logic. + * @param scene The scene + * @param reflectionTexture The texture to use for IBL + * @param defines The defines to update + * @param realTimeFiltering Whether realtime filting of IBL texture is being used + * @param realTimeFilteringQuality The quality of realtime filtering + * @param forceSHInVertex Whether the SH are handled in the vertex shader + * @returns true if the defines were updated + */ +export function PrepareDefinesForIBL( + scene: Scene, + reflectionTexture: Nullable, + defines: any, + realTimeFiltering: boolean = false, + realTimeFilteringQuality: number = Constants.TEXTURE_FILTERING_QUALITY_LOW, + forceSHInVertex: boolean = false +): boolean { + if (reflectionTexture && MaterialFlags.ReflectionTextureEnabled) { + if (!reflectionTexture.isReadyOrNotBlocking()) { + return false; + } + defines.REFLECTION = true; + defines.GAMMAREFLECTION = reflectionTexture.gammaSpace; + defines.RGBDREFLECTION = reflectionTexture.isRGBD; + defines.LODINREFLECTIONALPHA = reflectionTexture.lodLevelInAlpha; + defines.LINEARSPECULARREFLECTION = reflectionTexture.linearSpecularLOD; + defines.USEIRRADIANCEMAP = false; + + const engine = scene.getEngine(); + if (realTimeFiltering && realTimeFilteringQuality > 0) { + defines.NUM_SAMPLES = "" + realTimeFilteringQuality; + if (engine._features.needTypeSuffixInShaderConstants) { + defines.NUM_SAMPLES = defines.NUM_SAMPLES + "u"; + } + + defines.REALTIME_FILTERING = true; + if (scene.iblCdfGenerator) { + defines.IBL_CDF_FILTERING = true; + } + } else { + defines.REALTIME_FILTERING = false; + } + + defines.INVERTCUBICMAP = reflectionTexture.coordinatesMode === Texture.INVCUBIC_MODE; + defines.REFLECTIONMAP_3D = reflectionTexture.isCube; + defines.REFLECTIONMAP_OPPOSITEZ = defines.REFLECTIONMAP_3D && scene.useRightHandedSystem ? !reflectionTexture.invertZ : reflectionTexture.invertZ; + + defines.REFLECTIONMAP_CUBIC = false; + defines.REFLECTIONMAP_EXPLICIT = false; + defines.REFLECTIONMAP_PLANAR = false; + defines.REFLECTIONMAP_PROJECTION = false; + defines.REFLECTIONMAP_SKYBOX = false; + defines.REFLECTIONMAP_SPHERICAL = false; + defines.REFLECTIONMAP_EQUIRECTANGULAR = false; + defines.REFLECTIONMAP_EQUIRECTANGULAR_FIXED = false; + defines.REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED = false; + + switch (reflectionTexture.coordinatesMode) { + case Texture.EXPLICIT_MODE: + defines.REFLECTIONMAP_EXPLICIT = true; + break; + case Texture.PLANAR_MODE: + defines.REFLECTIONMAP_PLANAR = true; + break; + case Texture.PROJECTION_MODE: + defines.REFLECTIONMAP_PROJECTION = true; + break; + case Texture.SKYBOX_MODE: + defines.REFLECTIONMAP_SKYBOX = true; + break; + case Texture.SPHERICAL_MODE: + defines.REFLECTIONMAP_SPHERICAL = true; + break; + case Texture.EQUIRECTANGULAR_MODE: + defines.REFLECTIONMAP_EQUIRECTANGULAR = true; + break; + case Texture.FIXED_EQUIRECTANGULAR_MODE: + defines.REFLECTIONMAP_EQUIRECTANGULAR_FIXED = true; + break; + case Texture.FIXED_EQUIRECTANGULAR_MIRRORED_MODE: + defines.REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED = true; + break; + case Texture.CUBIC_MODE: + case Texture.INVCUBIC_MODE: + default: + defines.REFLECTIONMAP_CUBIC = true; + defines.USE_LOCAL_REFLECTIONMAP_CUBIC = (reflectionTexture).boundingBoxSize ? true : false; + break; + } + + if (reflectionTexture.coordinatesMode !== Texture.SKYBOX_MODE) { + if (reflectionTexture.irradianceTexture) { + 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; + defines.USE_IRRADIANCE_DOMINANT_DIRECTION = false; + defines.USESPHERICALINVERTEX = forceSHInVertex; + } + } + } else { + defines.REFLECTION = false; + defines.REFLECTIONMAP_3D = false; + defines.REFLECTIONMAP_SPHERICAL = false; + defines.REFLECTIONMAP_PLANAR = false; + defines.REFLECTIONMAP_CUBIC = false; + defines.USE_LOCAL_REFLECTIONMAP_CUBIC = false; + defines.REFLECTIONMAP_PROJECTION = false; + defines.REFLECTIONMAP_SKYBOX = false; + defines.REFLECTIONMAP_EXPLICIT = false; + defines.REFLECTIONMAP_EQUIRECTANGULAR = false; + defines.REFLECTIONMAP_EQUIRECTANGULAR_FIXED = false; + defines.REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED = false; + 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; + defines.GAMMAREFLECTION = false; + defines.RGBDREFLECTION = false; + defines.LINEARSPECULARREFLECTION = false; + } + return true; +} + /** * Prepares the defines related to the light information passed in parameter * @param scene The scene we are intending to draw @@ -1136,6 +1392,51 @@ export function PrepareUniformsAndSamplersForLight( } } +/** + * Append uniforms and samplers related to IBL to the provided lists + * @param uniformsList The list of uniforms to append to + * @param samplersList The list of samplers to append to + * @param useSH Whether to include spherical harmonics uniforms + */ +export function PrepareUniformsAndSamplersForIBL(uniformsList: string[], samplersList: string[], useSH: boolean): void { + const iblUniforms = [ + "vReflectionMicrosurfaceInfos", + "vReflectionDominantDirection", + "reflectionMatrix", + "vReflectionInfos", + "vReflectionPosition", + "vReflectionSize", + "vReflectionColor", + "vReflectionFilteringInfo", + ]; + if (useSH) { + iblUniforms.push( + "vSphericalX", + "vSphericalY", + "vSphericalZ", + "vSphericalXX_ZZ", + "vSphericalYY_ZZ", + "vSphericalZZ", + "vSphericalXY", + "vSphericalYZ", + "vSphericalZX", + "vSphericalL00", + "vSphericalL1_1", + "vSphericalL10", + "vSphericalL11", + "vSphericalL2_2", + "vSphericalL2_1", + "vSphericalL20", + "vSphericalL21", + "vSphericalL22" + ); + } + uniformsList.push(...iblUniforms); + + const iblSamplers = ["reflectionSampler", "reflectionSamplerLow", "reflectionSamplerHigh", "irradianceSampler", "icdfSampler"]; + samplersList.push(...iblSamplers); +} + /** * Prepares the uniforms and samplers list to be used in the effect * @param uniformsListOrOptions The uniform names to prepare or an EffectCreationOptions containing the list and extra information @@ -1188,3 +1489,63 @@ export function PrepareUniformsAndSamplersList(uniformsListOrOptions: string[] | samplersList.push("bakedVertexAnimationTexture"); } } + +/** + * + * @param ubo Add uniforms to UBO + * @param supportTextureInfo Add uniforms for texture info if true + * @param supportLocalProjection Add uniforms for local projection if true + * @param usePBR Add uniforms for IBL if true + * @param supportSH Add uniforms for spherical harmonics if true + * @param useColor Add uniforms for reflection color if true + */ +export function PrepareUniformLayoutForIBL( + ubo: UniformBuffer, + supportTextureInfo: boolean = false, + supportLocalProjection: boolean = false, + usePBR: boolean = false, + supportSH: boolean = false, + useColor: boolean = false +): void { + ubo.addUniform("vReflectionInfos", 2); + ubo.addUniform("reflectionMatrix", 16); + if (supportTextureInfo) { + ubo.addUniform("vReflectionMicrosurfaceInfos", 3); + } + + if (supportLocalProjection) { + ubo.addUniform("vReflectionPosition", 3); + ubo.addUniform("vReflectionSize", 3); + } + + if (usePBR) { + ubo.addUniform("vReflectionFilteringInfo", 2); + ubo.addUniform("vReflectionDominantDirection", 3); + } + + if (useColor) { + ubo.addUniform("vReflectionColor", 3); + } + + if (supportSH) { + ubo.addUniform("vSphericalL00", 3); + ubo.addUniform("vSphericalL1_1", 3); + ubo.addUniform("vSphericalL10", 3); + ubo.addUniform("vSphericalL11", 3); + ubo.addUniform("vSphericalL2_2", 3); + ubo.addUniform("vSphericalL2_1", 3); + ubo.addUniform("vSphericalL20", 3); + ubo.addUniform("vSphericalL21", 3); + ubo.addUniform("vSphericalL22", 3); + + ubo.addUniform("vSphericalX", 3); + ubo.addUniform("vSphericalY", 3); + ubo.addUniform("vSphericalZ", 3); + ubo.addUniform("vSphericalXX_ZZ", 3); + ubo.addUniform("vSphericalYY_ZZ", 3); + ubo.addUniform("vSphericalZZ", 3); + ubo.addUniform("vSphericalXY", 3); + ubo.addUniform("vSphericalYZ", 3); + ubo.addUniform("vSphericalZX", 3); + } +} diff --git a/packages/dev/core/src/Materials/standardMaterial.ts b/packages/dev/core/src/Materials/standardMaterial.ts index 157a334484b..b9edecfc53f 100644 --- a/packages/dev/core/src/Materials/standardMaterial.ts +++ b/packages/dev/core/src/Materials/standardMaterial.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { serialize, serializeAsColor3, expandToProperty, serializeAsFresnelParameters, serializeAsTexture } from "../Misc/decorators"; -import type { Observer } from "../Misc/observable"; import { SmartArray } from "../Misc/smartArray"; import type { IAnimatable } from "../Animations/animatable.interface"; @@ -14,9 +13,8 @@ import type { AbstractMesh } from "../Meshes/abstractMesh"; import type { Mesh } from "../Meshes/mesh"; import { PrePassConfiguration } from "./prePassConfiguration"; -import type { IImageProcessingConfigurationDefines } from "./imageProcessingConfiguration.defines"; +import { ImageProcessingDefinesMixin } from "./imageProcessingConfiguration.defines"; import { ImageProcessingConfiguration } from "./imageProcessingConfiguration"; -import type { ColorCurves } from "./colorCurves"; import type { FresnelParameters } from "./fresnelParameters"; import type { ICustomShaderNameResolveOptions } from "../Materials/material"; import { Material } from "../Materials/material"; @@ -25,7 +23,6 @@ import { MaterialDefines } from "../Materials/materialDefines"; import { PushMaterial } from "./pushMaterial"; import type { BaseTexture } from "../Materials/Textures/baseTexture"; -import { Texture } from "../Materials/Textures/texture"; import type { CubeTexture } from "../Materials/Textures/cubeTexture"; import type { RenderTargetTexture } from "../Materials/Textures/renderTargetTexture"; import { RegisterClass } from "../Misc/typeStore"; @@ -43,6 +40,7 @@ import { BindLogDepth, BindMorphTargetParameters, BindTextureMatrix, + BindIBLParameters, HandleFallbacksForShadows, PrepareAttributesForBakedVertexAnimation, PrepareAttributesForBones, @@ -51,27 +49,28 @@ import { PrepareDefinesForAttributes, PrepareDefinesForFrameBoundValues, PrepareDefinesForLights, + PrepareDefinesForIBL, PrepareDefinesForMergedUV, PrepareDefinesForMisc, PrepareDefinesForMultiview, PrepareDefinesForOIT, PrepareDefinesForPrePass, + PrepareUniformsAndSamplersForIBL, PrepareUniformsAndSamplersList, + PrepareUniformLayoutForIBL, } from "./materialHelper.functions"; import { SerializationHelper } from "../Misc/decorators.serialization"; import { ShaderLanguage } from "./shaderLanguage"; import { MaterialHelperGeometryRendering } from "./materialHelper.geometryrendering"; +import { UVDefinesMixin } from "./uv.defines"; +import { ImageProcessingMixin } from "./imageProcessing"; const onCreatedEffectParameters = { effect: null as unknown as Effect, subMesh: null as unknown as Nullable }; +class StandardMaterialDefinesBase extends UVDefinesMixin(MaterialDefines) {} + /** @internal */ -export class StandardMaterialDefines extends MaterialDefines implements IImageProcessingConfigurationDefines { - public MAINUV1 = false; - public MAINUV2 = false; - public MAINUV3 = false; - public MAINUV4 = false; - public MAINUV5 = false; - public MAINUV6 = false; +export class StandardMaterialDefines extends ImageProcessingDefinesMixin(StandardMaterialDefinesBase) { public DIFFUSE = false; public DIFFUSEDIRECTUV = 0; public BAKED_VERTEX_ANIMATION_TEXTURE = false; @@ -111,12 +110,6 @@ export class StandardMaterialDefines extends MaterialDefines implements IImagePr public FRESNEL = false; public NORMAL = false; public TANGENT = false; - public UV1 = false; - public UV2 = false; - public UV3 = false; - public UV4 = false; - public UV5 = false; - public UV6 = false; public VERTEXCOLOR = false; public VERTEXALPHA = false; public NUM_BONE_INFLUENCERS = 0; @@ -211,23 +204,6 @@ export class StandardMaterialDefines extends MaterialDefines implements IImagePr public RGBDREFLECTION = false; public RGBDREFRACTION = false; - public IMAGEPROCESSING = false; - public VIGNETTE = false; - public VIGNETTEBLENDMODEMULTIPLY = false; - public VIGNETTEBLENDMODEOPAQUE = false; - public TONEMAPPING = 0; - public CONTRAST = false; - public COLORCURVES = false; - public COLORGRADING = false; - public COLORGRADING3D = false; - public SAMPLER3DGREENDEPTH = false; - public SAMPLER3DBGRMAP = false; - public DITHER = false; - public IMAGEPROCESSINGPOSTPROCESS = false; - public SKIPFINALCOLORCLAMP = false; - public MULTIVIEW = false; - public ORDER_INDEPENDENT_TRANSPARENCY = false; - public ORDER_INDEPENDENT_TRANSPARENCY_16BITS = false; public CAMERA_ORTHOGRAPHIC = false; public CAMERA_PERSPECTIVE = false; public AREALIGHTSUPPORTED = true; @@ -243,7 +219,6 @@ export class StandardMaterialDefines extends MaterialDefines implements IImagePr * @internal */ public IS_REFRACTION_LINEAR = false; - public EXPOSURE = false; public DECAL_AFTER_DETAIL = false; @@ -255,33 +230,15 @@ export class StandardMaterialDefines extends MaterialDefines implements IImagePr super(externalProperties); this.rebuild(); } - - public setReflectionMode(modeToEnable: string) { - const modes = [ - "REFLECTIONMAP_CUBIC", - "REFLECTIONMAP_EXPLICIT", - "REFLECTIONMAP_PLANAR", - "REFLECTIONMAP_PROJECTION", - "REFLECTIONMAP_PROJECTION", - "REFLECTIONMAP_SKYBOX", - "REFLECTIONMAP_SPHERICAL", - "REFLECTIONMAP_EQUIRECTANGULAR", - "REFLECTIONMAP_EQUIRECTANGULAR_FIXED", - "REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED", - ]; - - for (const mode of modes) { - (this)[mode] = mode === modeToEnable; - } - } } +class StandardMaterialBase extends ImageProcessingMixin(PushMaterial) {} /** * This is the default material used in Babylon. It is the best trade off between quality * and performances. * @see https://doc.babylonjs.com/features/featuresDeepDive/materials/using/materials_introduction */ -export class StandardMaterial extends PushMaterial { +export class StandardMaterial extends StandardMaterialBase { /** * Force all the standard materials to compile to glsl even on WebGPU engines. * False by default. This is mostly meant for backward compatibility. @@ -628,64 +585,6 @@ export class StandardMaterial extends PushMaterial { @expandToProperty("_markAllSubMeshesAsMiscDirty") public applyDecalMapAfterDetailMap: boolean; - /** - * Default configuration related to image processing available in the standard Material. - */ - protected _imageProcessingConfiguration: ImageProcessingConfiguration; - - /** - * Gets the image processing configuration used either in this material. - */ - public get imageProcessingConfiguration(): ImageProcessingConfiguration { - return this._imageProcessingConfiguration; - } - - /** - * Sets the Default image processing configuration used either in the this material. - * - * If sets to null, the scene one is in use. - */ - public set imageProcessingConfiguration(value: ImageProcessingConfiguration) { - this._attachImageProcessingConfiguration(value); - - // Ensure the effect will be rebuilt. - this._markAllSubMeshesAsImageProcessingDirty(); - } - - /** - * Keep track of the image processing observer to allow dispose and replace. - */ - private _imageProcessingObserver: Nullable>; - - /** - * Attaches a new image processing configuration to the Standard Material. - * @param configuration - */ - protected _attachImageProcessingConfiguration(configuration: Nullable): void { - if (configuration === this._imageProcessingConfiguration) { - return; - } - - // Detaches observer - if (this._imageProcessingConfiguration && this._imageProcessingObserver) { - this._imageProcessingConfiguration.onUpdateParameters.remove(this._imageProcessingObserver); - } - - // Pick the scene configuration if needed - if (!configuration) { - this._imageProcessingConfiguration = this.getScene().imageProcessingConfiguration; - } else { - this._imageProcessingConfiguration = configuration; - } - - // Attaches observer - if (this._imageProcessingConfiguration) { - this._imageProcessingObserver = this._imageProcessingConfiguration.onUpdateParameters.add(() => { - this._markAllSubMeshesAsImageProcessingDirty(); - }); - } - } - private _shadersLoaded = false; /** @@ -700,108 +599,6 @@ export class StandardMaterial extends PushMaterial { return !this.disableDepthWrite; } - /** - * Gets whether the color curves effect is enabled. - */ - public get cameraColorCurvesEnabled(): boolean { - return this.imageProcessingConfiguration.colorCurvesEnabled; - } - /** - * Sets whether the color curves effect is enabled. - */ - public set cameraColorCurvesEnabled(value: boolean) { - this.imageProcessingConfiguration.colorCurvesEnabled = value; - } - - /** - * Gets whether the color grading effect is enabled. - */ - public get cameraColorGradingEnabled(): boolean { - return this.imageProcessingConfiguration.colorGradingEnabled; - } - /** - * Gets whether the color grading effect is enabled. - */ - public set cameraColorGradingEnabled(value: boolean) { - this.imageProcessingConfiguration.colorGradingEnabled = value; - } - - /** - * Gets whether tonemapping is enabled or not. - */ - public get cameraToneMappingEnabled(): boolean { - return this._imageProcessingConfiguration.toneMappingEnabled; - } - /** - * Sets whether tonemapping is enabled or not - */ - public set cameraToneMappingEnabled(value: boolean) { - this._imageProcessingConfiguration.toneMappingEnabled = value; - } - - /** - * The camera exposure used on this material. - * This property is here and not in the camera to allow controlling exposure without full screen post process. - * This corresponds to a photographic exposure. - */ - public get cameraExposure(): number { - return this._imageProcessingConfiguration.exposure; - } - /** - * The camera exposure used on this material. - * This property is here and not in the camera to allow controlling exposure without full screen post process. - * This corresponds to a photographic exposure. - */ - public set cameraExposure(value: number) { - this._imageProcessingConfiguration.exposure = value; - } - - /** - * Gets The camera contrast used on this material. - */ - public get cameraContrast(): number { - return this._imageProcessingConfiguration.contrast; - } - - /** - * Sets The camera contrast used on this material. - */ - public set cameraContrast(value: number) { - this._imageProcessingConfiguration.contrast = value; - } - - /** - * Gets the Color Grading 2D Lookup Texture. - */ - public get cameraColorGradingTexture(): Nullable { - return this._imageProcessingConfiguration.colorGradingTexture; - } - /** - * Sets the Color Grading 2D Lookup Texture. - */ - public set cameraColorGradingTexture(value: Nullable) { - this._imageProcessingConfiguration.colorGradingTexture = value; - } - - /** - * The color grading curves provide additional color adjustmnent that is applied after any color grading transform (3D LUT). - * They allow basic adjustment of saturation and small exposure adjustments, along with color filter tinting to provide white balance adjustment or more stylistic effects. - * These are similar to controls found in many professional imaging or colorist software. The global controls are applied to the entire image. For advanced tuning, extra controls are provided to adjust the shadow, midtone and highlight areas of the image; - * corresponding to low luminance, medium luminance, and high luminance areas respectively. - */ - public get cameraColorCurves(): Nullable { - return this._imageProcessingConfiguration.colorCurves; - } - /** - * The color grading curves provide additional color adjustment that is applied after any color grading transform (3D LUT). - * They allow basic adjustment of saturation and small exposure adjustments, along with color filter tinting to provide white balance adjustment or more stylistic effects. - * These are similar to controls found in many professional imaging or colorist software. The global controls are applied to the entire image. For advanced tuning, extra controls are provided to adjust the shadow, midtone and highlight areas of the image; - * corresponding to low luminance, medium luminance, and high luminance areas respectively. - */ - public set cameraColorCurves(value: Nullable) { - this._imageProcessingConfiguration.colorCurves = value; - } - /** * Can this material render to several textures at once */ @@ -1030,59 +827,15 @@ export class StandardMaterial extends PushMaterial { } else { defines.OPACITY = false; } - if (this._reflectionTexture && StandardMaterial.ReflectionTextureEnabled) { - if (!this._reflectionTexture.isReadyOrNotBlocking()) { - return false; - } else { - defines._needNormals = true; - defines.REFLECTION = true; - - defines.ROUGHNESS = this._roughness > 0; - defines.REFLECTIONOVERALPHA = this._useReflectionOverAlpha; - defines.INVERTCUBICMAP = this._reflectionTexture.coordinatesMode === Texture.INVCUBIC_MODE; - defines.REFLECTIONMAP_3D = this._reflectionTexture.isCube; - defines.REFLECTIONMAP_OPPOSITEZ = - defines.REFLECTIONMAP_3D && this.getScene().useRightHandedSystem ? !this._reflectionTexture.invertZ : this._reflectionTexture.invertZ; - defines.RGBDREFLECTION = this._reflectionTexture.isRGBD; - - switch (this._reflectionTexture.coordinatesMode) { - case Texture.EXPLICIT_MODE: - defines.setReflectionMode("REFLECTIONMAP_EXPLICIT"); - break; - case Texture.PLANAR_MODE: - defines.setReflectionMode("REFLECTIONMAP_PLANAR"); - break; - case Texture.PROJECTION_MODE: - defines.setReflectionMode("REFLECTIONMAP_PROJECTION"); - break; - case Texture.SKYBOX_MODE: - defines.setReflectionMode("REFLECTIONMAP_SKYBOX"); - break; - case Texture.SPHERICAL_MODE: - defines.setReflectionMode("REFLECTIONMAP_SPHERICAL"); - break; - case Texture.EQUIRECTANGULAR_MODE: - defines.setReflectionMode("REFLECTIONMAP_EQUIRECTANGULAR"); - break; - case Texture.FIXED_EQUIRECTANGULAR_MODE: - defines.setReflectionMode("REFLECTIONMAP_EQUIRECTANGULAR_FIXED"); - break; - case Texture.FIXED_EQUIRECTANGULAR_MIRRORED_MODE: - defines.setReflectionMode("REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED"); - break; - case Texture.CUBIC_MODE: - case Texture.INVCUBIC_MODE: - default: - defines.setReflectionMode("REFLECTIONMAP_CUBIC"); - break; - } - - defines.USE_LOCAL_REFLECTIONMAP_CUBIC = (this._reflectionTexture).boundingBoxSize ? true : false; - } + defines.ROUGHNESS = this._roughness > 0; + defines.REFLECTIONOVERALPHA = this._useReflectionOverAlpha; } else { - defines.REFLECTION = false; - defines.REFLECTIONMAP_OPPOSITEZ = false; + defines.ROUGHNESS = false; + defines.REFLECTIONOVERALPHA = false; + } + if (!PrepareDefinesForIBL(scene, this._reflectionTexture, defines)) { + return false; } if (this._emissiveTexture && StandardMaterial.EmissiveTextureEnabled) { @@ -1391,7 +1144,6 @@ export class StandardMaterial extends PushMaterial { "vDiffuseInfos", "vAmbientInfos", "vOpacityInfos", - "vReflectionInfos", "vEmissiveInfos", "vSpecularInfos", "vBumpInfos", @@ -1401,7 +1153,6 @@ export class StandardMaterial extends PushMaterial { "diffuseMatrix", "ambientMatrix", "opacityMatrix", - "reflectionMatrix", "emissiveMatrix", "specularMatrix", "bumpMatrix", @@ -1417,8 +1168,6 @@ export class StandardMaterial extends PushMaterial { "emissiveRightColor", "refractionLeftColor", "refractionRightColor", - "vReflectionPosition", - "vReflectionSize", "vRefractionPosition", "vRefractionSize", "logarithmicDepthConstant", @@ -1450,6 +1199,7 @@ export class StandardMaterial extends PushMaterial { "areaLightsLTC2Sampler", ]; + PrepareUniformsAndSamplersForIBL(uniforms, samplers, false); const uniformBuffers = ["Material", "Scene", "Mesh"]; const indexParameters = { maxSimultaneousLights: this._maxSimultaneousLights, maxSimultaneousMorphTargets: defines.NUM_MORPH_INFLUENCERS }; @@ -1586,9 +1336,6 @@ export class StandardMaterial extends PushMaterial { ubo.addUniform("vDiffuseInfos", 2); ubo.addUniform("vAmbientInfos", 2); ubo.addUniform("vOpacityInfos", 2); - ubo.addUniform("vReflectionInfos", 2); - ubo.addUniform("vReflectionPosition", 3); - ubo.addUniform("vReflectionSize", 3); ubo.addUniform("vEmissiveInfos", 2); ubo.addUniform("vLightmapInfos", 2); ubo.addUniform("vSpecularInfos", 2); @@ -1597,7 +1344,6 @@ export class StandardMaterial extends PushMaterial { ubo.addUniform("diffuseMatrix", 16); ubo.addUniform("ambientMatrix", 16); ubo.addUniform("opacityMatrix", 16); - ubo.addUniform("reflectionMatrix", 16); ubo.addUniform("emissiveMatrix", 16); ubo.addUniform("lightmapMatrix", 16); ubo.addUniform("specularMatrix", 16); @@ -1615,6 +1361,8 @@ export class StandardMaterial extends PushMaterial { ubo.addUniform("vAmbientColor", 3); ubo.addUniform("cameraInfo", 4); + PrepareUniformLayoutForIBL(ubo, false, true); + super.buildUniformLayout(); } @@ -1729,17 +1477,8 @@ export class StandardMaterial extends PushMaterial { ubo.updateFloat("alphaCutOff", this.alphaCutOff); } - if (this._reflectionTexture && StandardMaterial.ReflectionTextureEnabled) { - ubo.updateFloat2("vReflectionInfos", this._reflectionTexture.level, this.roughness); - ubo.updateMatrix("reflectionMatrix", this._reflectionTexture.getReflectionTextureMatrix()); - - if ((this._reflectionTexture).boundingBoxSize) { - const cubeTexture = this._reflectionTexture; - - ubo.updateVector3("vReflectionPosition", cubeTexture.boundingBoxPosition); - ubo.updateVector3("vReflectionSize", cubeTexture.boundingBoxSize); - } - } else { + BindIBLParameters(scene, defines, ubo, this._reflectionTexture, false, false, true); + if (!this._reflectionTexture || !StandardMaterial.ReflectionTextureEnabled) { ubo.updateFloat2("vReflectionInfos", 0.0, this.roughness); } diff --git a/packages/dev/core/src/Materials/uv.defines.ts b/packages/dev/core/src/Materials/uv.defines.ts new file mode 100644 index 00000000000..1928a17f486 --- /dev/null +++ b/packages/dev/core/src/Materials/uv.defines.ts @@ -0,0 +1,22 @@ +type UVDefinesMixinConstructor = new (...args: any[]) => T; + +/** + * Mixin to add UV defines to your material defines + * @internal + */ +export function UVDefinesMixin(base: Tbase) { + return class extends base { + public MAINUV1 = false; + public MAINUV2 = false; + public MAINUV3 = false; + public MAINUV4 = false; + public MAINUV5 = false; + public MAINUV6 = false; + public UV1 = false; + public UV2 = false; + public UV3 = false; + public UV4 = false; + public UV5 = false; + public UV6 = false; + }; +} diff --git a/packages/dev/core/src/Misc/decorators.ts b/packages/dev/core/src/Misc/decorators.ts index 9e07eb69699..3abc2bb90cb 100644 --- a/packages/dev/core/src/Misc/decorators.ts +++ b/packages/dev/core/src/Misc/decorators.ts @@ -158,3 +158,33 @@ nativeOverride.filter = function boolean>(predica return (target: any, propertyKey: string, descriptor: TypedPropertyDescriptor<(...params: Parameters) => unknown>) => nativeOverride(target, propertyKey, descriptor, predicate); }; + +export function addAccessorsForMaterialProperty(setCallback: string, targetKey: Nullable = null) { + return (target: any, propertyKey: string) => { + const key = propertyKey; + const newKey = targetKey || ""; + Object.defineProperty(target, newKey, { + get: function (this: any) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return this[key].value; + }, + set: function (this: any, value) { + // does this object (i.e. vector3) has an equals function? use it! + // Note - not using "with epsilon" here, it is expected te behave like the internal cache does. + if (typeof this[key]?.value?.equals === "function") { + if (this[key].value.equals(value)) { + return; + } + } + if (this[key].value === value) { + return; + } + this[key].value = value; + + target[setCallback].apply(this); + }, + enumerable: true, + configurable: true, + }); + }; +} diff --git a/packages/dev/core/src/Particles/baseParticleSystem.ts b/packages/dev/core/src/Particles/baseParticleSystem.ts index 4d082cd9520..dc628b486b7 100644 --- a/packages/dev/core/src/Particles/baseParticleSystem.ts +++ b/packages/dev/core/src/Particles/baseParticleSystem.ts @@ -1,7 +1,6 @@ import type { Nullable } from "../types"; import { Vector2, Vector3 } from "../Maths/math.vector"; import type { AbstractMesh } from "../Meshes/abstractMesh"; -import type { ImageProcessingConfiguration } from "../Materials/imageProcessingConfiguration"; import { ImageProcessingConfigurationDefines } from "../Materials/imageProcessingConfiguration.defines"; import type { ColorGradient, FactorGradient, Color3Gradient, IValueGradient } from "../Misc/gradients"; import type { BoxParticleEmitter } from "../Particles/EmitterTypes/boxParticleEmitter"; @@ -24,14 +23,16 @@ import type { SphereDirectedParticleEmitter, SphereParticleEmitter } from "./Emi import type { CylinderDirectedParticleEmitter, CylinderParticleEmitter } from "./EmitterTypes/cylinderParticleEmitter"; import type { ConeDirectedParticleEmitter, ConeParticleEmitter } from "./EmitterTypes/coneParticleEmitter"; import { RegisterClass } from "../Misc/typeStore"; +import { ImageProcessingMixin } from "core/Materials/imageProcessing"; +class BaseParticleSystemBase extends ImageProcessingMixin(Object) {} /** * This represents the base class for particle system in Babylon. * Particles are often small sprites used to simulate hard-to-reproduce phenomena like fire, smoke, water, or abstract visual effects like magic glitter and faery dust. * Particles can take different shapes while emitted like box, sphere, cone or you can write your custom function. * @example https://doc.babylonjs.com/features/featuresDeepDive/particles/particle_system/particle_system_intro */ -export class BaseParticleSystem implements IClipPlanesHolder { +export class BaseParticleSystem extends BaseParticleSystemBase implements IClipPlanesHolder { /** * Source color is added to the destination color without alpha affecting the result. Great for additive glow effects (fire, magic, lasers) */ @@ -739,44 +740,6 @@ export class BaseParticleSystem implements IClipPlanesHolder { */ protected _imageProcessingConfigurationDefines = new ImageProcessingConfigurationDefines(); - /** - * Default configuration related to image processing available in the standard Material. - */ - protected _imageProcessingConfiguration: Nullable; - - /** - * Gets the image processing configuration used either in this material. - */ - public get imageProcessingConfiguration(): Nullable { - return this._imageProcessingConfiguration; - } - - /** - * Sets the Default image processing configuration used either in the this material. - * - * If sets to null, the scene one is in use. - */ - public set imageProcessingConfiguration(value: Nullable) { - this._attachImageProcessingConfiguration(value); - } - - /** - * Attaches a new image processing configuration to the Standard Material. - * @param configuration - */ - protected _attachImageProcessingConfiguration(configuration: Nullable): void { - if (configuration === this._imageProcessingConfiguration) { - return; - } - - // Pick the scene configuration if needed. - if (!configuration && this._scene) { - this._imageProcessingConfiguration = this._scene.imageProcessingConfiguration; - } else { - this._imageProcessingConfiguration = configuration; - } - } - /** @internal */ protected _reset() {} @@ -810,6 +773,7 @@ export class BaseParticleSystem implements IClipPlanesHolder { * @param name The name of the particle system */ public constructor(name: string) { + super(name); this.id = name; this.name = name; } diff --git a/packages/dev/core/src/Rendering/IBLShadows/iblShadowsPluginMaterial.ts b/packages/dev/core/src/Rendering/IBLShadows/iblShadowsPluginMaterial.ts index 666d6a90324..90c6a465a87 100644 --- a/packages/dev/core/src/Rendering/IBLShadows/iblShadowsPluginMaterial.ts +++ b/packages/dev/core/src/Rendering/IBLShadows/iblShadowsPluginMaterial.ts @@ -10,6 +10,7 @@ import { expandToProperty, serialize } from "core/Misc/decorators"; import { RegisterClass } from "core/Misc/typeStore"; import { ShaderLanguage } from "core/Materials/shaderLanguage"; +import { OpenPBRMaterial } from "core/Materials/PBR/openPbrMaterial"; /** * @internal */ @@ -72,7 +73,7 @@ export class IBLShadowsPluginMaterial extends MaterialPluginBase { return true; } - constructor(material: Material | StandardMaterial | PBRBaseMaterial) { + constructor(material: Material | StandardMaterial | PBRBaseMaterial | OpenPBRMaterial) { super(material, IBLShadowsPluginMaterial.Name, 310, new MaterialIBLShadowsRenderDefines()); this._internalMarkAllSubMeshesAsTexturesDirty = material._dirtyCallbacks[Constants.MATERIAL_TextureDirtyFlag]; } @@ -160,6 +161,27 @@ export class IBLShadowsPluginMaterial extends MaterialPluginBase { #endif #endif `; + } else if (this._material instanceof OpenPBRMaterial) { + // eslint-disable-next-line @typescript-eslint/naming-convention + frag["CUSTOM_FRAGMENT_BEFORE_IBLLAYERCOMPOSITION"] = ` + #ifdef RENDER_WITH_IBL_SHADOWS + #ifndef UNLIT + #ifdef REFLECTION + #ifdef COLORED_IBL_SHADOWS + var shadowValue: vec3f = computeIndirectShadow(); + slab_diffuse_ibl *= shadowValue; + slab_glossy_ibl *= mix(vec3f(1.0), shadowValue, specularAlphaG); + #else + var shadowValue: vec2f = computeIndirectShadow(); + slab_diffuse_ibl *= vec3f(shadowValue.x); + slab_glossy_ibl *= vec3f(mix(pow(shadowValue.y, 4.0), shadowValue.x, specularAlphaG)); + #endif + #endif + #else + slab_diffuse_ibl *= computeIndirectShadow().x; + #endif + #endif + `; } else { frag["CUSTOM_FRAGMENT_BEFORE_FRAGCOLOR"] = ` #ifdef RENDER_WITH_IBL_SHADOWS @@ -217,6 +239,27 @@ export class IBLShadowsPluginMaterial extends MaterialPluginBase { #endif #endif `; + } else if (this._material instanceof OpenPBRMaterial) { + // eslint-disable-next-line @typescript-eslint/naming-convention + frag["CUSTOM_FRAGMENT_BEFORE_IBLLAYERCOMPOSITION"] = ` + #ifdef RENDER_WITH_IBL_SHADOWS + #ifndef UNLIT + #ifdef REFLECTION + #ifdef COLORED_IBL_SHADOWS + vec3 shadowValue = computeIndirectShadow(); + slab_diffuse_ibl.rgb *= shadowValue.rgb; + slab_glossy_ibl *= mix(vec3(1.0), shadowValue.rgb, specularAlphaG); + #else + vec2 shadowValue = computeIndirectShadow(); + slab_diffuse_ibl *= shadowValue.x; + slab_glossy_ibl *= mix(pow(shadowValue.y, 4.0), shadowValue.x, specularAlphaG); + #endif + #endif + #else + slab_diffuse_ibl *= computeIndirectShadow().x; + #endif + #endif + `; } else { frag["CUSTOM_FRAGMENT_BEFORE_FRAGCOLOR"] = ` #ifdef RENDER_WITH_IBL_SHADOWS diff --git a/packages/dev/core/src/Rendering/IBLShadows/iblShadowsRenderPipeline.ts b/packages/dev/core/src/Rendering/IBLShadows/iblShadowsRenderPipeline.ts index 010593f29a0..6eeb74c406c 100644 --- a/packages/dev/core/src/Rendering/IBLShadows/iblShadowsRenderPipeline.ts +++ b/packages/dev/core/src/Rendering/IBLShadows/iblShadowsRenderPipeline.ts @@ -27,6 +27,7 @@ import type { Material } from "core/Materials/material"; import { Observable } from "core/Misc/observable"; import "../geometryBufferRendererSceneComponent"; import "../iblCdfGeneratorSceneComponent"; +import { OpenPBRMaterial } from "core/Materials/PBR/openPbrMaterial"; interface IIblShadowsSettings { /** @@ -1115,7 +1116,7 @@ export class IblShadowsRenderPipeline extends PostProcessRenderPipeline { } protected _addShadowSupportToMaterial(material: Material) { - if (!(material instanceof PBRBaseMaterial) && !(material instanceof StandardMaterial)) { + if (!(material instanceof PBRBaseMaterial) && !(material instanceof StandardMaterial) && !(material instanceof OpenPBRMaterial)) { return; } let plugin = material.pluginManager?.getPlugin(IBLShadowsPluginMaterial.Name); diff --git a/packages/dev/core/src/Rendering/geometryBufferRenderer.ts b/packages/dev/core/src/Rendering/geometryBufferRenderer.ts index b735be41353..fbe6268b272 100644 --- a/packages/dev/core/src/Rendering/geometryBufferRenderer.ts +++ b/packages/dev/core/src/Rendering/geometryBufferRenderer.ts @@ -568,8 +568,8 @@ export class GeometryBufferRenderer { } // Normal map texture - if ((material.bumpTexture || material.normalTexture) && MaterialFlags.BumpTextureEnabled) { - const texture = material.bumpTexture || material.normalTexture; + if ((material.bumpTexture || material.normalTexture || material.geometryNormalTexture) && MaterialFlags.BumpTextureEnabled) { + const texture = material.bumpTexture || material.normalTexture || material.geometryNormalTexture; defines.push("#define BUMP"); defines.push(`#define BUMP_UV${texture.coordinatesIndex + 1}`); needUv = true; @@ -1110,8 +1110,12 @@ export class GeometryBufferRenderer { } // Bump - if ((material.bumpTexture || material.normalTexture) && scene.getEngine().getCaps().standardDerivatives && MaterialFlags.BumpTextureEnabled) { - const texture = material.bumpTexture || material.normalTexture; + if ( + (material.bumpTexture || material.normalTexture || material.geometryNormalTexture) && + scene.getEngine().getCaps().standardDerivatives && + MaterialFlags.BumpTextureEnabled + ) { + const texture = material.bumpTexture || material.normalTexture || material.geometryNormalTexture; effect.setFloat3("vBumpInfos", texture.coordinatesIndex, 1.0 / texture.level, material.parallaxScaleBias); effect.setMatrix("bumpMatrix", texture.getTextureMatrix()); effect.setTexture("bumpSampler", texture); diff --git a/packages/dev/core/src/Shaders/ShadersInclude/backgroundUboDeclaration.fx b/packages/dev/core/src/Shaders/ShadersInclude/backgroundUboDeclaration.fx index 549fcfa18e6..5a6f77003f3 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/backgroundUboDeclaration.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/backgroundUboDeclaration.fx @@ -5,10 +5,7 @@ uniform Material uniform vec4 vPrimaryColor; uniform vec4 vPrimaryColorShadow; uniform vec2 vDiffuseInfos; - uniform vec2 vReflectionInfos; uniform mat4 diffuseMatrix; - uniform mat4 reflectionMatrix; - uniform vec3 vReflectionMicrosurfaceInfos; uniform float fFovMultiplier; uniform float pointSize; @@ -17,6 +14,10 @@ uniform Material uniform vec3 vBackgroundCenter; uniform vec4 vReflectionControl; uniform vec2 projectedGroundInfos; + + uniform vec2 vReflectionInfos; + uniform mat4 reflectionMatrix; + uniform vec3 vReflectionMicrosurfaceInfos; }; #include diff --git a/packages/dev/core/src/Shaders/ShadersInclude/defaultUboDeclaration.fx b/packages/dev/core/src/Shaders/ShadersInclude/defaultUboDeclaration.fx index b68982042da..64a29678059 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/defaultUboDeclaration.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/defaultUboDeclaration.fx @@ -14,9 +14,6 @@ uniform Material vec2 vDiffuseInfos; vec2 vAmbientInfos; vec2 vOpacityInfos; - vec2 vReflectionInfos; - vec3 vReflectionPosition; - vec3 vReflectionSize; vec2 vEmissiveInfos; vec2 vLightmapInfos; vec2 vSpecularInfos; @@ -24,7 +21,6 @@ uniform Material mat4 diffuseMatrix; mat4 ambientMatrix; mat4 opacityMatrix; - mat4 reflectionMatrix; mat4 emissiveMatrix; mat4 lightmapMatrix; mat4 specularMatrix; @@ -42,6 +38,11 @@ uniform Material vec3 vAmbientColor; vec4 cameraInfo; + vec2 vReflectionInfos; + mat4 reflectionMatrix; + vec3 vReflectionPosition; + vec3 vReflectionSize; + #define ADDITIONAL_UBO_DECLARATION }; diff --git a/packages/dev/core/src/Shaders/ShadersInclude/openpbrBaseLayerData.fx b/packages/dev/core/src/Shaders/ShadersInclude/openpbrBaseLayerData.fx new file mode 100644 index 00000000000..a8eff6df01f --- /dev/null +++ b/packages/dev/core/src/Shaders/ShadersInclude/openpbrBaseLayerData.fx @@ -0,0 +1,138 @@ +// This code reads uniforms and samples textures to fill up the base and specular +// layer properties for OpenPBR + +// Base Layer Properties +// We don't include base_weight in our initial variables because it is multiplied +// into the base_color in this code snippet. +vec3 base_color = vec3(0.8); +float base_metalness = 0.0; +float base_diffuse_roughness = 0.0; + +// Specular Layer Properties +float specular_weight = 1.0; +float specular_roughness = 0.3; +vec3 specular_color = vec3(1.0); +float specular_roughness_anisotropy = 0.0; +float specular_ior = 1.5; +float alpha = 1.0; + +// Sample Base Layer properties from textures +#ifdef BASE_WEIGHT + vec4 baseWeightFromTexture = texture2D(baseWeightSampler, vBaseWeightUV + uvOffset); +#endif + +#ifdef BASE_COLOR + vec4 baseColorFromTexture = texture2D(baseColorSampler, vBaseColorUV + uvOffset); +#endif + +#ifdef METALLIC_ROUGHNESS + vec4 metallicRoughnessFromTexture = texture2D(baseMetalRoughSampler, vBaseMetalRoughUV + uvOffset); +#endif + +#ifdef BASE_DIFFUSE_ROUGHNESS + float baseDiffuseRoughnessFromTexture = texture2D(baseDiffuseRoughnessSampler, vBaseDiffuseRoughnessUV + uvOffset).r; +#endif + +#ifdef GEOMETRY_OPACITY + vec4 opacityFromTexture = texture2D(opacitySampler, vOpacityUV + uvOffset); +#endif + +#ifdef DECAL + vec4 decalFromTexture = texture2D(decalSampler, vDecalUV + uvOffset); +#endif + +#ifdef SPECULAR_COLOR + vec4 specularColorFromTexture = texture2D(specularColorSampler, vSpecularColorUV + uvOffset); + #ifdef SPECULAR_COLOR_GAMMA + specularColorFromTexture.rgb = toLinearSpace(specularColorFromTexture.rgb); + #endif +#endif + +#ifdef SPECULAR_WEIGHT + vec4 specularWeightFromTexture = texture2D(specularWeightSampler, vSpecularWeightUV + uvOffset); +#endif + +// Initalize base layer properties from uniforms +base_color = vBaseColor.rgb; +#if defined(VERTEXCOLOR) || defined(INSTANCESCOLOR) && defined(INSTANCES) + base_color *= vColor.rgb; +#endif +#if defined(VERTEXALPHA) || defined(INSTANCESCOLOR) && defined(INSTANCES) + alpha *= vColor.a; +#endif +base_color *= vec3(vBaseWeight); +alpha = vBaseColor.a; +base_metalness = vReflectanceInfo.x; +base_diffuse_roughness = vBaseDiffuseRoughness; +specular_roughness = vReflectanceInfo.y; +specular_color = vSpecularColor.rgb; +specular_weight = vReflectanceInfo.a; +specular_ior = vReflectanceInfo.z; + +// Apply texture values to base layer properties + +#ifdef BASE_COLOR + #if defined(ALPHAFROMALBEDO) || defined(ALPHATEST) + alpha *= baseColorFromTexture.a; + #endif + + #ifdef BASE_COLOR_GAMMA + base_color *= toLinearSpace(baseColorFromTexture.rgb); + #else + base_color *= baseColorFromTexture.rgb; + #endif + + base_color *= vBaseColorInfos.y; +#endif + +#ifdef BASE_WEIGHT + base_color *= baseWeightFromTexture.r; +#endif + +// _____________________________ Alpha Information _______________________________ +#ifdef GEOMETRY_OPACITY + alpha *= opacityFromTexture.a; + alpha *= vGeometryOpacityInfos.y; +#endif + +#ifdef ALPHATEST + #if DEBUGMODE != 88 + if (alpha < ALPHATESTVALUE) + discard; + #endif + + #ifndef ALPHABLEND + // Prevent to blend with the canvas. + alpha = 1.0; + #endif +#endif + +#ifdef METALLIC_ROUGHNESS + base_metalness *= metallicRoughnessFromTexture.b; + specular_roughness *= metallicRoughnessFromTexture.g; +#endif + +#ifdef BASE_DIFFUSE_ROUGHNESS + base_diffuse_roughness *= baseDiffuseRoughnessFromTexture * vBaseDiffuseRoughnessInfos.y; +#endif + +#ifdef SPECULAR_COLOR + specular_color *= specularColorFromTexture.rgb; +#endif + +#ifdef SPECULAR_WEIGHT + // If loaded from a glTF, the specular_weight is stored in the alpha channel. + // Otherwise, it's expected to just be a greyscale texture. + #ifdef SPECULAR_WEIGHT_USE_ALPHA_ONLY + specular_weight *= specularWeightFromTexture.a; + #else + specular_weight *= specularWeightFromTexture.r; + #endif +#endif + +#ifdef DETAIL + float detailRoughness = mix(0.5, detailColor.b, vDetailInfos.w); + float loLerp = mix(0., specular_roughness, detailRoughness * 2.); + float hiLerp = mix(specular_roughness, 1., (detailRoughness - 0.5) * 2.); + specular_roughness = mix(loLerp, hiLerp, step(detailRoughness, 0.5)); +#endif \ No newline at end of file diff --git a/packages/dev/core/src/Shaders/ShadersInclude/openpbrBlockAmbientOcclusion.fx b/packages/dev/core/src/Shaders/ShadersInclude/openpbrBlockAmbientOcclusion.fx new file mode 100644 index 00000000000..ed33ed5cc7b --- /dev/null +++ b/packages/dev/core/src/Shaders/ShadersInclude/openpbrBlockAmbientOcclusion.fx @@ -0,0 +1,34 @@ +struct ambientOcclusionOutParams +{ + vec3 ambientOcclusionColor; +#if DEBUGMODE > 0 && defined(AMBIENT_OCCLUSION) + vec3 ambientOcclusionColorMap; +#endif +}; + +ambientOcclusionOutParams ambientOcclusionBlock( +#ifdef AMBIENT_OCCLUSION + in vec3 ambientOcclusionColorMap_, + in vec2 ambientInfos +#endif +) +{ + ambientOcclusionOutParams outParams; + vec3 ambientOcclusionColor = vec3(1., 1., 1.); + + #ifdef AMBIENT_OCCLUSION + vec3 ambientOcclusionColorMap = ambientOcclusionColorMap_ * ambientInfos.y; + #ifdef AMBIENTINGRAYSCALE + ambientOcclusionColorMap = vec3(ambientOcclusionColorMap.r, ambientOcclusionColorMap.r, ambientOcclusionColorMap.r); + #endif + // ambientOcclusionColor = mix(ambientOcclusionColor, ambientOcclusionColorMap, ambientInfos.z); + + #if DEBUGMODE > 0 + outParams.ambientOcclusionColorMap = ambientOcclusionColorMap; + #endif + #endif + + outParams.ambientOcclusionColor = ambientOcclusionColor; + + return outParams; +} diff --git a/packages/dev/core/src/Shaders/ShadersInclude/openpbrBlockNormalFinal.fx b/packages/dev/core/src/Shaders/ShadersInclude/openpbrBlockNormalFinal.fx new file mode 100644 index 00000000000..62de4c4fb29 --- /dev/null +++ b/packages/dev/core/src/Shaders/ShadersInclude/openpbrBlockNormalFinal.fx @@ -0,0 +1,19 @@ +#if defined(FORCENORMALFORWARD) && defined(NORMAL) + vec3 faceNormal = normalize(cross(dFdx(vPositionW), dFdy(vPositionW))) * vEyePosition.w; + #if defined(TWOSIDEDLIGHTING) + faceNormal = gl_FrontFacing ? faceNormal : -faceNormal; + #endif + + normalW *= sign(dot(normalW, faceNormal)); + coatNormalW *= sign(dot(coatNormalW, faceNormal)); +#endif + +#if defined(TWOSIDEDLIGHTING) && defined(NORMAL) + #if defined(MIRRORED) + normalW = gl_FrontFacing ? -normalW : normalW; + coatNormalW = gl_FrontFacing ? -coatNormalW : coatNormalW; + #else + normalW = gl_FrontFacing ? normalW : -normalW; + coatNormalW = gl_FrontFacing ? coatNormalW : -coatNormalW; + #endif +#endif diff --git a/packages/dev/core/src/Shaders/ShadersInclude/openpbrCoatLayerData.fx b/packages/dev/core/src/Shaders/ShadersInclude/openpbrCoatLayerData.fx new file mode 100644 index 00000000000..114b84a29db --- /dev/null +++ b/packages/dev/core/src/Shaders/ShadersInclude/openpbrCoatLayerData.fx @@ -0,0 +1,65 @@ +// This code reads uniforms and samples textures to fill up the coat +// layer properties for OpenPBR + +float coat_weight = 0.0; +vec3 coat_color = vec3(1.0); +float coat_roughness = 0.0; +float coat_roughness_anisotropy = 0.0; +float coat_ior = 1.6; +float coat_darkening = 1.0; + +// Sample Coat Layer properties from textures +#ifdef COAT_WEIGHT + vec4 coatWeightFromTexture = texture2D(coatWeightSampler, vCoatWeightUV + uvOffset); +#endif + +#ifdef COAT_COLOR + vec4 coatColorFromTexture = texture2D(coatColorSampler, vCoatColorUV + uvOffset); +#endif + +#ifdef COAT_ROUGHNESS + vec4 coatRoughnessFromTexture = texture2D(coatRoughnessSampler, vCoatRoughnessUV + uvOffset); +#endif + +#ifdef COAT_ROUGHNESS_ANISOTROPY + float coatRoughnessAnisotropyFromTexture = texture2D(coatRoughnessAnisotropySampler, vCoatRoughnessAnisotropyUV + uvOffset).r; +#endif + +#ifdef COAT_DARKENING + vec4 coatDarkeningFromTexture = texture2D(coatDarkeningSampler, vCoatDarkeningUV + uvOffset); +#endif + +// Initalize coat layer properties from uniforms +coat_color = vCoatColor.rgb; +coat_weight = vCoatWeight; +coat_roughness = vCoatRoughness; +// coat_roughness_anisotropy = vCoatRoughnessAnisotropy; +coat_ior = vCoatIor; +coat_darkening = vCoatDarkening; + +// Apply texture values to coat layer properties +#ifdef COAT_WEIGHT + coat_weight *= coatWeightFromTexture.r; +#endif + +#ifdef COAT_COLOR + #ifdef COAT_COLOR_GAMMA + coat_color *= toLinearSpace(coatColorFromTexture.rgb); + #else + coat_color *= coatColorFromTexture.rgb; + #endif + + coat_color *= vCoatColorInfos.y; +#endif + +#ifdef COAT_ROUGHNESS + coat_roughness *= coatRoughnessFromTexture.r; +#endif + +#ifdef COAT_ROUGHNESS_ANISOTROPY + coat_roughness_anisotropy *= coatRoughnessAnisotropyFromTexture; +#endif + +#ifdef COAT_DARKENING + coat_darkening *= coatDarkeningFromTexture.r; +#endif diff --git a/packages/dev/core/src/Shaders/ShadersInclude/openpbrConductorReflectance.fx b/packages/dev/core/src/Shaders/ShadersInclude/openpbrConductorReflectance.fx new file mode 100644 index 00000000000..f823813c45b --- /dev/null +++ b/packages/dev/core/src/Shaders/ShadersInclude/openpbrConductorReflectance.fx @@ -0,0 +1,17 @@ + +#define pbr_inline +ReflectanceParams conductorReflectance(in vec3 baseColor, in vec3 specularColor, in float specularWeight) +{ + ReflectanceParams outParams; + + #if (CONDUCTOR_SPECULAR_MODEL == CONDUCTOR_SPECULAR_MODEL_OPENPBR) + outParams.coloredF0 = baseColor * specularWeight; + outParams.coloredF90 = specularColor * specularWeight; + #else + outParams.coloredF0 = baseColor; + outParams.coloredF90 = vec3(1.0); + #endif + outParams.F0 = 1.0; + outParams.F90 = 1.0; + return outParams; +} \ No newline at end of file diff --git a/packages/dev/core/src/Shaders/ShadersInclude/openpbrDielectricReflectance.fx b/packages/dev/core/src/Shaders/ShadersInclude/openpbrDielectricReflectance.fx new file mode 100644 index 00000000000..f37eb493bf0 --- /dev/null +++ b/packages/dev/core/src/Shaders/ShadersInclude/openpbrDielectricReflectance.fx @@ -0,0 +1,53 @@ +struct ReflectanceParams +{ + float F0; + float F90; + vec3 coloredF0; + vec3 coloredF90; +}; + +#define pbr_inline +ReflectanceParams dielectricReflectance( + in float insideIOR, in float outsideIOR, in vec3 specularColor, in float specularWeight +) +{ + ReflectanceParams outParams; + + float dielectricF0 = pow((insideIOR - outsideIOR) / (insideIOR + outsideIOR), 2.0); + + // Compute non-coloured reflectance. + // reflectanceF0 is the non-coloured reflectance used for blending between the diffuse and specular components. + // It represents the total percentage of light reflected by the specular lobe at normal incidence. + // In glTF's material model, the F0 value is multiplied by the maximum component of the specular colour. + #if DIELECTRIC_SPECULAR_MODEL == DIELECTRIC_SPECULAR_MODEL_GLTF + float maxF0 = max(specularColor.r, max(specularColor.g, specularColor.b)); + outParams.F0 = dielectricF0 * maxF0 * specularWeight; + #else + outParams.F0 = dielectricF0 * specularWeight; + #endif + + + // Scale the reflectanceF90 by the IOR for values less than 1.5. + // This is an empirical hack to account for the fact that Schlick is tuned for IOR = 1.5 + // and an IOR of 1.0 should result in no visible glancing specular. + float f90Scale = clamp(2.0 * abs(insideIOR - outsideIOR), 0.0, 1.0); + outParams.F90 = f90Scale * specularWeight; + + // Compute the coloured F0 reflectance. + // The coloured reflectance is the percentage of light reflected by the specular lobe at normal incidence. + // In glTF and OpenPBR, it is not the same thing as the percentage of light blocked from penetrating + // down to the layer below. The non-coloured F0 will be used for this (see below). + outParams.coloredF0 = vec3(dielectricF0 * specularWeight) * specularColor.rgb; + + // Now, compute the coloured reflectance at glancing angles based on the specular model. + #if (DIELECTRIC_SPECULAR_MODEL == DIELECTRIC_SPECULAR_MODEL_OPENPBR) + // In OpenPBR, the F90 is coloured using the specular colour for dielectrics. + vec3 dielectricColorF90 = specularColor.rgb * vec3(f90Scale) * specularWeight; + #else + // In glTF, the F90 is white for dielectrics. + vec3 dielectricColorF90 = vec3(f90Scale) * specularWeight; + #endif + outParams.coloredF90 = dielectricColorF90; + + return outParams; +} diff --git a/packages/dev/core/src/Shaders/ShadersInclude/openpbrDirectLighting.fx b/packages/dev/core/src/Shaders/ShadersInclude/openpbrDirectLighting.fx new file mode 100644 index 00000000000..4fa285fc1eb --- /dev/null +++ b/packages/dev/core/src/Shaders/ShadersInclude/openpbrDirectLighting.fx @@ -0,0 +1,116 @@ +#ifdef LIGHT{X} +{ + vec3 slab_diffuse = vec3(0., 0., 0.); + vec3 slab_subsurface = vec3(0., 0., 0.); + vec3 slab_translucent = vec3(0., 0., 0.); + vec3 slab_glossy = vec3(0., 0., 0.); + float specularFresnel = 0.0; + vec3 slab_metal = vec3(0., 0., 0.); + vec3 slab_coat = vec3(0., 0., 0.); + float coatFresnel = 0.0; + vec3 slab_fuzz = vec3(0., 0., 0.); + + // Diffuse Lobe + #ifdef HEMILIGHT{X} + slab_diffuse = computeHemisphericDiffuseLighting(preInfo{X}, lightColor{X}.rgb, light{X}.vLightGround); + #elif defined(AREALIGHT{X}) + slab_diffuse = computeAreaDiffuseLighting(preInfo{X}, lightColor{X}.rgb); + #else + slab_diffuse = computeDiffuseLighting(preInfo{X}, lightColor{X}.rgb); + #endif + + #ifdef PROJECTEDLIGHTTEXTURE{X} + slab_diffuse *= computeProjectionTextureDiffuseLighting(projectionLightTexture{X}, textureProjectionMatrix{X}, vPositionW); + #endif + + numLights += 1.0; + + // Specular Lobe + #if AREALIGHT{X} + slab_glossy = computeAreaSpecularLighting(preInfo{X}, light{X}.vLightSpecular.rgb, baseConductorReflectance.F0, baseConductorReflectance.F90); + #else + { + #ifdef ANISOTROPIC + slab_glossy = computeAnisotropicSpecularLighting(preInfo{X}, viewDirectionW, normalW, + anisotropicOut.anisotropicTangent, anisotropicOut.anisotropicBitangent, anisotropicOut.anisotropy, + vec3(baseDielectricReflectance.F0), vec3(baseDielectricReflectance.F90), baseGeoInfo.AARoughnessFactors.x, lightColor{X}.rgb); + #else + slab_glossy = computeSpecularLighting(preInfo{X}, normalW, baseDielectricReflectance.coloredF0, baseDielectricReflectance.coloredF90, specular_roughness, lightColor{X}.rgb); + #endif + + float NdotH = dot(normalW, preInfo{X}.H); + specularFresnel = fresnelSchlickGGX(NdotH, baseDielectricReflectance.F0, baseDielectricReflectance.F90); + } + #endif + + // Metal Lobe + #if AREALIGHT{X} + slab_metal = computeAreaSpecularLighting(preInfo{X}, light{X}.vLightSpecular.rgb, baseConductorReflectance.F0, baseConductorReflectance.F90); + #else + { + // For OpenPBR, we use the F82 specular model for metallic materials and mix with the + // usual Schlick lobe. + #if (CONDUCTOR_SPECULAR_MODEL == CONDUCTOR_SPECULAR_MODEL_OPENPBR) + vec3 coloredFresnel = specular_weight * getF82Specular(preInfo{X}.VdotH, baseConductorReflectance.coloredF0, baseConductorReflectance.coloredF90, specular_roughness); + #else + vec3 coloredFresnel = fresnelSchlickGGX(preInfo{X}.VdotH, baseConductorReflectance.coloredF0, baseConductorReflectance.coloredF90); + #endif + + #ifdef ANISOTROPIC + slab_metal = computeAnisotropicSpecularLighting(preInfo{X}, viewDirectionW, normalW, anisotropicOut.anisotropicTangent, anisotropicOut.anisotropicBitangent, anisotropicOut.anisotropy, specularEnvironmentR0, specularEnvironmentR90, baseGeoInfo.AARoughnessFactors.x, lightColor{X}.rgb); + #else + slab_metal = computeSpecularLighting(preInfo{X}, normalW, vec3(baseConductorReflectance.coloredF0), coloredFresnel, specular_roughness, lightColor{X}.rgb); + #endif + } + #endif + + // Coat Lobe + #if AREALIGHT{X} + slab_coat = computeAreaSpecularLighting(preInfoCoat{X}, light{X}.vLightSpecular.rgb, coatReflectance.F0, coatReflectance.F90); + #else + { + #ifdef ANISOTROPIC + slab_coat = computeAnisotropicSpecularLighting(preInfoCoat{X}, viewDirectionW, coatNormalW, + anisotropicOut.anisotropicTangent, anisotropicOut.anisotropicBitangent, anisotropicOut.anisotropy, + vec3(coatReflectance.F0), vec3(coatReflectance.F90), baseGeoInfo.AARoughnessFactors.x, lightColor{X}.rgb); + #else + slab_coat = computeSpecularLighting(preInfoCoat{X}, coatNormalW, vec3(coatReflectance.F0), vec3(1.0), coat_roughness, lightColor{X}.rgb); + #endif + + float NdotH = dot(coatNormalW, preInfoCoat{X}.H); + coatFresnel = fresnelSchlickGGX(NdotH, coatReflectance.F0, coatReflectance.F90); + } + #endif + + vec3 coatAbsorption = vec3(1.0); + if (coat_weight > 0.0) { + // __________ Coat Darkening _____________ + float cosTheta_view = max(preInfoCoat{X}.NdotV, 0.001); + float cosTheta_light = max(preInfoCoat{X}.NdotL, 0.001); + + // Fresnel reflectance for view direction + float fresnel_view = coatReflectance.F0 + (1.0 - coatReflectance.F0) * pow(1.0 - cosTheta_view, 5.0); + + // Fresnel reflectance for light direction + float fresnel_light = coatReflectance.F0 + (1.0 - coatReflectance.F0) * pow(1.0 - cosTheta_light, 5.0); + + // Average reflectance for the round trip (light in, view out) + float averageReflectance = (fresnel_view + fresnel_light) * 0.5; + + // Calculate transmission through multiple internal reflections + // This uses the geometric series for infinite reflections: + // T = (1-R) / (1 + R + R² + R³ + ...) = (1-R) / (1/(1-R)) = (1-R)² + float effectiveReflectance = averageReflectance * coat_weight; + float transmission = (1.0 - effectiveReflectance) / (1.0 + effectiveReflectance); + coatAbsorption = coat_color * mix(1.0, transmission, coat_darkening); + } + + slab_diffuse *= base_color.rgb; + vec3 material_opaque_base = mix(slab_diffuse, slab_subsurface, subsurface_weight); + vec3 material_dielectric_base = mix(material_opaque_base, slab_translucent, transmission_weight); + vec3 material_dielectric_gloss = layer(material_dielectric_base, slab_glossy, specularFresnel, vec3(1.0), specular_color); + vec3 material_base_substrate = mix(material_dielectric_gloss, slab_metal, base_metalness); + vec3 material_coated_base = layer(material_base_substrate, slab_coat, coatFresnel, coatAbsorption, vec3(1.0)); + material_surface_direct += mix(material_coated_base, slab_fuzz, fuzz_weight); +} +#endif \ No newline at end of file diff --git a/packages/dev/core/src/Shaders/ShadersInclude/openpbrDirectLightingInit.fx b/packages/dev/core/src/Shaders/ShadersInclude/openpbrDirectLightingInit.fx new file mode 100644 index 00000000000..5a41d74dc13 --- /dev/null +++ b/packages/dev/core/src/Shaders/ShadersInclude/openpbrDirectLightingInit.fx @@ -0,0 +1,93 @@ +#ifdef LIGHT{X} + preLightingInfo preInfo{X}; + preLightingInfo preInfoCoat{X}; + // openpbrLightingInfo info{X}; + vec4 lightColor{X} = light{X}.vLightDiffuse; + float shadow{X} = 1.; + #if defined(SHADOWONLY) || defined(LIGHTMAP) && defined(LIGHTMAPEXCLUDED{X}) && defined(LIGHTMAPNOSPECULAR{X}) + //No light calculation + #else + + #define CUSTOM_LIGHT{X}_COLOR // Use to modify light color. Currently only supports diffuse. + + // Compute Pre Lighting infos + #ifdef SPOTLIGHT{X} + preInfo{X} = computePointAndSpotPreLightingInfo(light{X}.vLightData, viewDirectionW, normalW, vPositionW); + preInfoCoat{X} = computePointAndSpotPreLightingInfo(light{X}.vLightData, viewDirectionW, coatNormalW, vPositionW); + #elif defined(POINTLIGHT{X}) + preInfo{X} = computePointAndSpotPreLightingInfo(light{X}.vLightData, viewDirectionW, normalW, vPositionW); + preInfoCoat{X} = computePointAndSpotPreLightingInfo(light{X}.vLightData, viewDirectionW, coatNormalW, vPositionW); + #elif defined(HEMILIGHT{X}) + preInfo{X} = computeHemisphericPreLightingInfo(light{X}.vLightData, viewDirectionW, normalW); + preInfoCoat{X} = computeHemisphericPreLightingInfo(light{X}.vLightData, viewDirectionW, coatNormalW); + #elif defined(DIRLIGHT{X}) + preInfo{X} = computeDirectionalPreLightingInfo(light{X}.vLightData, viewDirectionW, normalW); + preInfoCoat{X} = computeDirectionalPreLightingInfo(light{X}.vLightData, viewDirectionW, coatNormalW); + #elif defined(AREALIGHT{X}) && defined(AREALIGHTSUPPORTED) + preInfo{X} = computeAreaPreLightingInfo(areaLightsLTC1Sampler, areaLightsLTC2Sampler, viewDirectionW, normalW, vPositionW, light{X}.vLightData, light{X}.vLightWidth.xyz, light{X}.vLightHeight.xyz, specular_roughness); + preInfoCoat{X} = computeAreaPreLightingInfo(areaLightsLTC1Sampler, areaLightsLTC2Sampler, viewDirectionW, coatNormalW, vPositionW, light{X}.vLightData, light{X}.vLightWidth.xyz, light{X}.vLightHeight.xyz, coat_roughness); + #endif + + preInfo{X}.NdotV = baseGeoInfo.NdotV; + preInfoCoat{X}.NdotV = coatGeoInfo.NdotV; + + // Compute Attenuation infos + #ifdef SPOTLIGHT{X} + #ifdef LIGHT_FALLOFF_GLTF{X} + preInfo{X}.attenuation = computeDistanceLightFalloff_GLTF(preInfo{X}.lightDistanceSquared, light{X}.vLightFalloff.y); + #ifdef IESLIGHTTEXTURE{X} + preInfo{X}.attenuation *= computeDirectionalLightFalloff_IES(light{X}.vLightDirection.xyz, preInfo{X}.L, iesLightTexture{X}); + #else + preInfo{X}.attenuation *= computeDirectionalLightFalloff_GLTF(light{X}.vLightDirection.xyz, preInfo{X}.L, light{X}.vLightFalloff.z, light{X}.vLightFalloff.w); + #endif + #elif defined(LIGHT_FALLOFF_PHYSICAL{X}) + preInfo{X}.attenuation = computeDistanceLightFalloff_Physical(preInfo{X}.lightDistanceSquared); + #ifdef IESLIGHTTEXTURE{X} + preInfo{X}.attenuation *= computeDirectionalLightFalloff_IES(light{X}.vLightDirection.xyz, preInfo{X}.L, iesLightTexture{X}); + #else + preInfo{X}.attenuation *= computeDirectionalLightFalloff_Physical(light{X}.vLightDirection.xyz, preInfo{X}.L, light{X}.vLightDirection.w); + #endif + #elif defined(LIGHT_FALLOFF_STANDARD{X}) + preInfo{X}.attenuation = computeDistanceLightFalloff_Standard(preInfo{X}.lightOffset, light{X}.vLightFalloff.x); + #ifdef IESLIGHTTEXTURE{X} + preInfo{X}.attenuation *= computeDirectionalLightFalloff_IES(light{X}.vLightDirection.xyz, preInfo{X}.L, iesLightTexture{X}); + #else + preInfo{X}.attenuation *= computeDirectionalLightFalloff_Standard(light{X}.vLightDirection.xyz, preInfo{X}.L, light{X}.vLightDirection.w, light{X}.vLightData.w); + #endif + #else + preInfo{X}.attenuation = computeDistanceLightFalloff(preInfo{X}.lightOffset, preInfo{X}.lightDistanceSquared, light{X}.vLightFalloff.x, light{X}.vLightFalloff.y); + #ifdef IESLIGHTTEXTURE{X} + preInfo{X}.attenuation *= computeDirectionalLightFalloff_IES(light{X}.vLightDirection.xyz, preInfo{X}.L, iesLightTexture{X}); + #else + preInfo{X}.attenuation *= computeDirectionalLightFalloff(light{X}.vLightDirection.xyz, preInfo{X}.L, light{X}.vLightDirection.w, light{X}.vLightData.w, light{X}.vLightFalloff.z, light{X}.vLightFalloff.w); + #endif + #endif + #elif defined(POINTLIGHT{X}) + #ifdef LIGHT_FALLOFF_GLTF{X} + preInfo{X}.attenuation = computeDistanceLightFalloff_GLTF(preInfo{X}.lightDistanceSquared, light{X}.vLightFalloff.y); + #elif defined(LIGHT_FALLOFF_PHYSICAL{X}) + preInfo{X}.attenuation = computeDistanceLightFalloff_Physical(preInfo{X}.lightDistanceSquared); + #elif defined(LIGHT_FALLOFF_STANDARD{X}) + preInfo{X}.attenuation = computeDistanceLightFalloff_Standard(preInfo{X}.lightOffset, light{X}.vLightFalloff.x); + #else + preInfo{X}.attenuation = computeDistanceLightFalloff(preInfo{X}.lightOffset, preInfo{X}.lightDistanceSquared, light{X}.vLightFalloff.x, light{X}.vLightFalloff.y); + #endif + #else + preInfo{X}.attenuation = 1.0; + #endif + + preInfoCoat{X}.attenuation = preInfo{X}.attenuation; + + // Simulates Light radius for diffuse and spec term + // clear coat is using a dedicated roughness + #if defined(HEMILIGHT{X}) || defined(AREALIGHT{X}) + preInfo{X}.roughness = specular_roughness; + preInfoCoat{X}.roughness = coat_roughness; + #else + preInfo{X}.roughness = adjustRoughnessFromLightProperties(specular_roughness, light{X}.vLightSpecular.a, preInfo{X}.lightDistance); + preInfoCoat{X}.roughness = adjustRoughnessFromLightProperties(coat_roughness, light{X}.vLightSpecular.a, preInfoCoat{X}.lightDistance); + #endif + preInfo{X}.diffuseRoughness = base_diffuse_roughness; + preInfo{X}.surfaceAlbedo = base_color.rgb; + #endif +#endif \ No newline at end of file diff --git a/packages/dev/core/src/Shaders/ShadersInclude/openpbrDirectLightingShadow.fx b/packages/dev/core/src/Shaders/ShadersInclude/openpbrDirectLightingShadow.fx new file mode 100644 index 00000000000..1eb866fbc49 --- /dev/null +++ b/packages/dev/core/src/Shaders/ShadersInclude/openpbrDirectLightingShadow.fx @@ -0,0 +1,133 @@ +#ifdef LIGHT{X} + #ifdef SHADOW{X} + #ifdef SHADOWCSM{X} + for (int i = 0; i < SHADOWCSMNUM_CASCADES{X}; i++) + { + #ifdef SHADOWCSM_RIGHTHANDED{X} + diff{X} = viewFrustumZ{X}[i] + vPositionFromCamera{X}.z; + #else + diff{X} = viewFrustumZ{X}[i] - vPositionFromCamera{X}.z; + #endif + if (diff{X} >= 0.) { + index{X} = i; + break; + } + } + + #ifdef SHADOWCSMUSESHADOWMAXZ{X} + if (index{X} >= 0) + #endif + { + #if defined(SHADOWPCF{X}) + #if defined(SHADOWLOWQUALITY{X}) + shadow{X} = computeShadowWithCSMPCF1(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowTexture{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + #elif defined(SHADOWMEDIUMQUALITY{X}) + shadow{X} = computeShadowWithCSMPCF3(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowTexture{X}, light{X}.shadowsInfo.yz, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + #else + shadow{X} = computeShadowWithCSMPCF5(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowTexture{X}, light{X}.shadowsInfo.yz, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + #endif + #elif defined(SHADOWPCSS{X}) + #if defined(SHADOWLOWQUALITY{X}) + shadow{X} = computeShadowWithCSMPCSS16(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], depthTexture{X}, shadowTexture{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w, lightSizeUVCorrection{X}[index{X}], depthCorrection{X}[index{X}], penumbraDarkness{X}); + #elif defined(SHADOWMEDIUMQUALITY{X}) + shadow{X} = computeShadowWithCSMPCSS32(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], depthTexture{X}, shadowTexture{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w, lightSizeUVCorrection{X}[index{X}], depthCorrection{X}[index{X}], penumbraDarkness{X}); + #else + shadow{X} = computeShadowWithCSMPCSS64(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], depthTexture{X}, shadowTexture{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w, lightSizeUVCorrection{X}[index{X}], depthCorrection{X}[index{X}], penumbraDarkness{X}); + #endif + #else + shadow{X} = computeShadowCSM(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowTexture{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + #endif + + #ifdef SHADOWCSMDEBUG{X} + shadowDebug{X} = vec3(shadow{X}) * vCascadeColorsMultiplier{X}[index{X}]; + #endif + + #ifndef SHADOWCSMNOBLEND{X} + float frustumLength = frustumLengths{X}[index{X}]; + float diffRatio = clamp(diff{X} / frustumLength, 0., 1.) * cascadeBlendFactor{X}; + if (index{X} < (SHADOWCSMNUM_CASCADES{X} - 1) && diffRatio < 1.) + { + index{X} += 1; + float nextShadow = 0.; + #if defined(SHADOWPCF{X}) + #if defined(SHADOWLOWQUALITY{X}) + nextShadow = computeShadowWithCSMPCF1(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowTexture{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + #elif defined(SHADOWMEDIUMQUALITY{X}) + nextShadow = computeShadowWithCSMPCF3(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowTexture{X}, light{X}.shadowsInfo.yz, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + #else + nextShadow = computeShadowWithCSMPCF5(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowTexture{X}, light{X}.shadowsInfo.yz, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + #endif + #elif defined(SHADOWPCSS{X}) + #if defined(SHADOWLOWQUALITY{X}) + nextShadow = computeShadowWithCSMPCSS16(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], depthTexture{X}, shadowTexture{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w, lightSizeUVCorrection{X}[index{X}], depthCorrection{X}[index{X}], penumbraDarkness{X}); + #elif defined(SHADOWMEDIUMQUALITY{X}) + nextShadow = computeShadowWithCSMPCSS32(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], depthTexture{X}, shadowTexture{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w, lightSizeUVCorrection{X}[index{X}], depthCorrection{X}[index{X}], penumbraDarkness{X}); + #else + nextShadow = computeShadowWithCSMPCSS64(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], depthTexture{X}, shadowTexture{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w, lightSizeUVCorrection{X}[index{X}], depthCorrection{X}[index{X}], penumbraDarkness{X}); + #endif + #else + nextShadow = computeShadowCSM(float(index{X}), vPositionFromLight{X}[index{X}], vDepthMetric{X}[index{X}], shadowTexture{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + #endif + + shadow{X} = mix(nextShadow, shadow{X}, diffRatio); + #ifdef SHADOWCSMDEBUG{X} + shadowDebug{X} = mix(vec3(nextShadow) * vCascadeColorsMultiplier{X}[index{X}], shadowDebug{X}, diffRatio); + #endif + } + #endif + } + #elif defined(SHADOWCLOSEESM{X}) + #if defined(SHADOWCUBE{X}) + shadow{X} = computeShadowWithCloseESMCube(vPositionW, light{X}.vLightData.xyz, shadowTexture{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.z, light{X}.depthValues); + #else + shadow{X} = computeShadowWithCloseESM(vPositionFromLight{X}, vDepthMetric{X}, shadowTexture{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.z, light{X}.shadowsInfo.w); + #endif + #elif defined(SHADOWESM{X}) + #if defined(SHADOWCUBE{X}) + shadow{X} = computeShadowWithESMCube(vPositionW, light{X}.vLightData.xyz, shadowTexture{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.z, light{X}.depthValues); + #else + shadow{X} = computeShadowWithESM(vPositionFromLight{X}, vDepthMetric{X}, shadowTexture{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.z, light{X}.shadowsInfo.w); + #endif + #elif defined(SHADOWPOISSON{X}) + #if defined(SHADOWCUBE{X}) + shadow{X} = computeShadowWithPoissonSamplingCube(vPositionW, light{X}.vLightData.xyz, shadowTexture{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.x, light{X}.depthValues); + #else + shadow{X} = computeShadowWithPoissonSampling(vPositionFromLight{X}, vDepthMetric{X}, shadowTexture{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + #endif + #elif defined(SHADOWPCF{X}) + #if defined(SHADOWLOWQUALITY{X}) + shadow{X} = computeShadowWithPCF1(vPositionFromLight{X}, vDepthMetric{X}, shadowTexture{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + #elif defined(SHADOWMEDIUMQUALITY{X}) + shadow{X} = computeShadowWithPCF3(vPositionFromLight{X}, vDepthMetric{X}, shadowTexture{X}, light{X}.shadowsInfo.yz, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + #else + shadow{X} = computeShadowWithPCF5(vPositionFromLight{X}, vDepthMetric{X}, shadowTexture{X}, light{X}.shadowsInfo.yz, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + #endif + #elif defined(SHADOWPCSS{X}) + #if defined(SHADOWLOWQUALITY{X}) + shadow{X} = computeShadowWithPCSS16(vPositionFromLight{X}, vDepthMetric{X}, depthTexture{X}, shadowTexture{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + #elif defined(SHADOWMEDIUMQUALITY{X}) + shadow{X} = computeShadowWithPCSS32(vPositionFromLight{X}, vDepthMetric{X}, depthTexture{X}, shadowTexture{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + #else + shadow{X} = computeShadowWithPCSS64(vPositionFromLight{X}, vDepthMetric{X}, depthTexture{X}, shadowTexture{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.z, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + #endif + #else + #if defined(SHADOWCUBE{X}) + shadow{X} = computeShadowCube(vPositionW, light{X}.vLightData.xyz, shadowTexture{X}, light{X}.shadowsInfo.x, light{X}.depthValues); + #else + shadow{X} = computeShadow(vPositionFromLight{X}, vDepthMetric{X}, shadowTexture{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.w); + #endif + #endif + + #ifdef SHADOWONLY + #ifndef SHADOWINUSE + #define SHADOWINUSE + #endif + globalShadow += shadow{X}; + shadowLightCount += 1.0; + #endif + #else + shadow{X} = 1.; + #endif + + aggShadow += shadow{X}; +#endif \ No newline at end of file diff --git a/packages/dev/core/src/Shaders/ShadersInclude/openpbrEnvironmentLighting.fx b/packages/dev/core/src/Shaders/ShadersInclude/openpbrEnvironmentLighting.fx new file mode 100644 index 00000000000..e8cfa1a6a2f --- /dev/null +++ b/packages/dev/core/src/Shaders/ShadersInclude/openpbrEnvironmentLighting.fx @@ -0,0 +1,147 @@ +// _____________________________ Base Diffuse Layer IBL _______________________________________ +#ifdef REFLECTION + // Pass in a vector to sample teh irradiance with (to handle reflection or ) + vec3 baseDiffuseEnvironmentLight = sampleIrradiance( + normalW + #if defined(NORMAL) && defined(USESPHERICALINVERTEX) + , vEnvironmentIrradiance + #endif + #if (defined(USESPHERICALFROMREFLECTIONMAP) && (!defined(NORMAL) || !defined(USESPHERICALINVERTEX))) || (defined(USEIRRADIANCEMAP) && defined(REFLECTIONMAP_3D)) + , reflectionMatrix + #endif + #ifdef USEIRRADIANCEMAP + , irradianceSampler + #ifdef USE_IRRADIANCE_DOMINANT_DIRECTION + , vReflectionDominantDirection + #endif + #endif + #ifdef REALTIME_FILTERING + , vReflectionFilteringInfo + #ifdef IBL_CDF_FILTERING + , icdfSampler + #endif + #endif + , vReflectionInfos + , viewDirectionW + , base_diffuse_roughness + , base_color + ); + + // _____________________________ Base Specular Layer IBL _______________________________________ + + #ifdef REFLECTIONMAP_3D + vec3 reflectionCoords = vec3(0., 0., 0.); + #else + vec2 reflectionCoords = vec2(0., 0.); + #endif + reflectionCoords = createReflectionCoords(vPositionW, normalW); + float specularAlphaG = specular_roughness * specular_roughness; + vec3 baseSpecularEnvironmentLight = sampleRadiance(specularAlphaG, vReflectionMicrosurfaceInfos.rgb, vReflectionInfos + #if defined(LODINREFLECTIONALPHA) && !defined(REFLECTIONMAP_SKYBOX) + , baseGeoInfo.NdotVUnclamped + #endif + , reflectionSampler + , reflectionCoords + #ifdef REALTIME_FILTERING + , vReflectionFilteringInfo + #endif + ); + + baseSpecularEnvironmentLight = mix(baseSpecularEnvironmentLight.rgb, baseDiffuseEnvironmentLight, specularAlphaG); + + vec3 coatEnvironmentLight = vec3(0., 0., 0.); + if (coat_weight > 0.0) { + #ifdef REFLECTIONMAP_3D + vec3 reflectionCoords = vec3(0., 0., 0.); + #else + vec2 reflectionCoords = vec2(0., 0.); + #endif + reflectionCoords = createReflectionCoords(vPositionW, coatNormalW); + float coatAlphaG = coat_roughness * coat_roughness; + coatEnvironmentLight = sampleRadiance(coatAlphaG, vReflectionMicrosurfaceInfos.rgb, vReflectionInfos + #if defined(LODINREFLECTIONALPHA) && !defined(REFLECTIONMAP_SKYBOX) + , coatGeoInfo.NdotVUnclamped + #endif + , reflectionSampler + , reflectionCoords + #ifdef REALTIME_FILTERING + , vReflectionFilteringInfo + #endif + ); + } + + // ______________________________ IBL Fresnel Reflectance ____________________________ + + // Dielectric IBL Fresnel + // The colored fresnel represents the % of light reflected by the base specular lobe + // The non-colored fresnel represents the % of light that doesn't penetrate through + // the base specular lobe. i.e. the specular lobe isn't energy conserving for coloured specular. + float dielectricIblFresnel = getReflectanceFromBRDFLookup(vec3(baseDielectricReflectance.F0), vec3(baseDielectricReflectance.F90), baseGeoInfo.environmentBrdf).r; + vec3 dielectricIblColoredFresnel = getReflectanceFromBRDFLookup(baseDielectricReflectance.coloredF0, baseDielectricReflectance.coloredF90, baseGeoInfo.environmentBrdf); + + // Conductor IBL Fresnel + vec3 conductorIblFresnel = conductorIblFresnel(baseConductorReflectance, baseGeoInfo.NdotV, specular_roughness, baseGeoInfo.environmentBrdf); + + // Coat IBL Fresnel + float coatIblFresnel = 0.0; + if (coat_weight > 0.0) { + coatIblFresnel = getReflectanceFromBRDFLookup(vec3(coatReflectance.F0), vec3(coatReflectance.F90), coatGeoInfo.environmentBrdf).r; + } + + + vec3 slab_diffuse_ibl = vec3(0., 0., 0.); + vec3 slab_glossy_ibl = vec3(0., 0., 0.); + vec3 slab_metal_ibl = vec3(0., 0., 0.); + vec3 slab_coat_ibl = vec3(0., 0., 0.); + + slab_diffuse_ibl = baseDiffuseEnvironmentLight * vLightingIntensity.z; + slab_diffuse_ibl *= aoOut.ambientOcclusionColor; + + // Add the specular environment light + slab_glossy_ibl = baseSpecularEnvironmentLight * vLightingIntensity.z; + + // _____________________________ Metal Layer IBL ____________________________ + slab_metal_ibl = baseSpecularEnvironmentLight * conductorIblFresnel * vLightingIntensity.z; + + // _____________________________ Coat Layer IBL _____________________________ + // We will use this absorption value to darken the underlying layers. It includes both the + // abosorption of the coat layer and the darkening due to internal reflections. + vec3 coatAbsorption = vec3(1.0); + if (coat_weight > 0.0) { + slab_coat_ibl = coatEnvironmentLight * vLightingIntensity.z; + + // __________ Coat Darkening _____________ + // Hemisphere-averaged Fresnel (empirical approximation) + float hemisphere_avg_fresnel = coatReflectance.F0 + 0.5 * (1.0 - coatReflectance.F0); + float averageReflectance = (coatIblFresnel + hemisphere_avg_fresnel) * 0.5; + + // Account for roughness - rougher surfaces have more diffuse internal reflections + // This reduces the darkening effect as roughness increases + float roughnessFactor = 1.0 - coat_roughness * 0.5; + averageReflectance *= roughnessFactor; + + // Calculate transmission through multiple internal reflections + // This uses the geometric series for infinite reflections: + // T = (1-R) / (1 + R + R² + R³ + ...) = (1-R) / (1/(1-R)) = (1-R)² + float effectiveReflectance = averageReflectance * coat_weight; + float transmission = (1.0 - effectiveReflectance) * (1.0 - effectiveReflectance); + coatAbsorption = coat_color * mix(1.0, transmission, coat_darkening); + } + + // TEMP + vec3 slab_subsurface_ibl = vec3(0., 0., 0.); + vec3 slab_translucent_base_ibl = vec3(0., 0., 0.); + vec3 slab_fuzz_ibl = vec3(0., 0., 0.); + + slab_diffuse_ibl *= base_color.rgb; + + // _____________________________ IBL Material Layer Composition ______________________________________ + #define CUSTOM_FRAGMENT_BEFORE_IBLLAYERCOMPOSITION + vec3 material_opaque_base_ibl = mix(slab_diffuse_ibl, slab_subsurface_ibl, subsurface_weight); + vec3 material_dielectric_base_ibl = mix(material_opaque_base_ibl, slab_translucent_base_ibl, transmission_weight); + vec3 material_dielectric_gloss_ibl = layer(material_dielectric_base_ibl, slab_glossy_ibl, dielectricIblFresnel, vec3(1.0), specular_color); + vec3 material_base_substrate_ibl = mix(material_dielectric_gloss_ibl, slab_metal_ibl, base_metalness); + vec3 material_coated_base_ibl = layer(material_base_substrate_ibl, slab_coat_ibl, coatIblFresnel, coatAbsorption, vec3(1.0)); + material_surface_ibl = mix(material_coated_base_ibl, slab_fuzz_ibl, fuzz_weight); + +#endif \ No newline at end of file diff --git a/packages/dev/core/src/Shaders/ShadersInclude/openpbrFragmentDeclaration.fx b/packages/dev/core/src/Shaders/ShadersInclude/openpbrFragmentDeclaration.fx new file mode 100644 index 00000000000..f626698a83c --- /dev/null +++ b/packages/dev/core/src/Shaders/ShadersInclude/openpbrFragmentDeclaration.fx @@ -0,0 +1,129 @@ +uniform vec4 vEyePosition; + +uniform float vBaseWeight; +uniform vec4 vBaseColor; +uniform float vBaseDiffuseRoughness; +uniform vec4 vReflectanceInfo; +uniform vec4 vSpecularColor; +uniform float vCoatWeight; +uniform vec3 vCoatColor; +uniform float vCoatRoughness; +uniform float vCoatIor; +uniform float vCoatDarkening; +uniform vec3 vEmissionColor; + +// CUSTOM CONTROLS +uniform vec4 vLightingIntensity; +uniform float visibility; + +// Samplers +#ifdef BASE_COLOR +uniform vec2 vBaseColorInfos; +#endif + +#ifdef BASE_WEIGHT +uniform vec2 vBaseWeightInfos; +#endif + +#ifdef BASE_DIFFUSE_ROUGHNESS +uniform vec2 vBaseDiffuseRoughnessInfos; +#endif + +#ifdef AMBIENT_OCCLUSION +uniform vec4 vAmbientOcclusionInfos; +#endif + +#ifdef GEOMETRY_NORMAL +uniform vec2 vGeometryNormalInfos; +uniform vec2 vTangentSpaceParams; +#endif + +#ifdef GEOMETRY_COAT_NORMAL +uniform vec2 vGeometryCoatNormalInfos; +#endif + +#ifdef GEOMETRY_OPACITY +uniform vec2 vGeometryOpacityInfos; +#endif + +#ifdef EMISSION_COLOR +uniform vec2 vEmissionColorInfos; +#endif + +#ifdef METALLIC_ROUGHNESS +uniform vec2 vBaseMetalRoughInfos; +#endif + +#ifdef COAT_WEIGHT +uniform vec2 vCoatWeightInfos; +#endif + +#ifdef COAT_DARKENING +uniform vec2 vCoatDarkeningInfos; +#endif + +// Refraction Reflection +#if defined(REFLECTIONMAP_SPHERICAL) || defined(REFLECTIONMAP_PROJECTION) || defined(SS_REFRACTION) || defined(PREPASS) +uniform mat4 view; +#endif + +// Reflection +#ifdef REFLECTION + uniform vec2 vReflectionInfos; + + #ifdef REALTIME_FILTERING + uniform vec2 vReflectionFilteringInfo; + #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; + uniform vec3 vReflectionSize; + #endif +#endif + +#ifdef PREPASS + #ifdef SS_SCATTERING + uniform float scatteringDiffusionProfile; + #endif +#endif + +#if DEBUGMODE > 0 + uniform vec2 vDebugMode; +#endif + +#ifdef DETAIL + uniform vec4 vDetailInfos; +#endif + +#include + +#ifdef USESPHERICALFROMREFLECTIONMAP + #ifdef SPHERICAL_HARMONICS + uniform vec3 vSphericalL00; + uniform vec3 vSphericalL1_1; + uniform vec3 vSphericalL10; + uniform vec3 vSphericalL11; + uniform vec3 vSphericalL2_2; + uniform vec3 vSphericalL2_1; + uniform vec3 vSphericalL20; + uniform vec3 vSphericalL21; + uniform vec3 vSphericalL22; + #else + uniform vec3 vSphericalX; + uniform vec3 vSphericalY; + uniform vec3 vSphericalZ; + uniform vec3 vSphericalXX_ZZ; + uniform vec3 vSphericalYY_ZZ; + uniform vec3 vSphericalZZ; + uniform vec3 vSphericalXY; + uniform vec3 vSphericalYZ; + uniform vec3 vSphericalZX; + #endif +#endif + +#define ADDITIONAL_FRAGMENT_DECLARATION diff --git a/packages/dev/core/src/Shaders/ShadersInclude/openpbrFragmentSamplersDeclaration.fx b/packages/dev/core/src/Shaders/ShadersInclude/openpbrFragmentSamplersDeclaration.fx new file mode 100644 index 00000000000..452d2177d53 --- /dev/null +++ b/packages/dev/core/src/Shaders/ShadersInclude/openpbrFragmentSamplersDeclaration.fx @@ -0,0 +1,67 @@ +#include(_DEFINENAME_,BASE_COLOR,_VARYINGNAME_,BaseColor,_SAMPLERNAME_,baseColor) +#include(_DEFINENAME_,BASE_WEIGHT,_VARYINGNAME_,BaseWeight,_SAMPLERNAME_,baseWeight) +#include(_DEFINENAME_,BASE_DIFFUSE_ROUGHNESS,_VARYINGNAME_,BaseDiffuseRoughness,_SAMPLERNAME_,baseDiffuseRoughness) +#include(_DEFINENAME_,METALLIC_ROUGHNESS,_VARYINGNAME_,BaseMetalRough,_SAMPLERNAME_,baseMetalRough) +#include(_DEFINENAME_,SPECULAR_WEIGHT,_VARYINGNAME_,SpecularWeight,_SAMPLERNAME_,specularWeight) +#include(_DEFINENAME_,SPECULAR_COLOR,_VARYINGNAME_,SpecularColor,_SAMPLERNAME_,specularColor) +#include(_DEFINENAME_,COAT_WEIGHT,_VARYINGNAME_,CoatWeight,_SAMPLERNAME_,coatWeight) +#include(_DEFINENAME_,COAT_COLOR,_VARYINGNAME_,CoatColor,_SAMPLERNAME_,coatColor) +#include(_DEFINENAME_,COAT_ROUGHNESS,_VARYINGNAME_,CoatRoughness,_SAMPLERNAME_,coatRoughness) +// #include(_DEFINENAME_,COAT_ROUGHNESS_ANISOTROPY,_VARYINGNAME_,CoatRoughnessAnisotropy,_SAMPLERNAME_,coatRoughnessAnisotropy) +#include (_DEFINENAME_,COAT_DARKENING,_VARYINGNAME_,CoatDarkening,_SAMPLERNAME_,coatDarkening) +#include(_DEFINENAME_,GEOMETRY_OPACITY,_VARYINGNAME_,GeometryOpacity,_SAMPLERNAME_,geometryOpacity) +#include(_DEFINENAME_,EMISSION_COLOR,_VARYINGNAME_,EmissionColor,_SAMPLERNAME_,emissionColor) + +#include(_DEFINENAME_,AMBIENT_OCCLUSION,_VARYINGNAME_,AmbientOcclusion,_SAMPLERNAME_,ambientOcclusion) +#include(_DEFINENAME_,DECAL,_VARYINGNAME_,Decal,_SAMPLERNAME_,decal) + +// Reflection +#ifdef REFLECTION + #ifdef REFLECTIONMAP_3D + #define sampleReflection(s, c) textureCube(s, c) + + uniform samplerCube reflectionSampler; + + #ifdef LODBASEDMICROSFURACE + #define sampleReflectionLod(s, c, l) textureCubeLodEXT(s, c, l) + #else + uniform samplerCube reflectionSamplerLow; + uniform samplerCube reflectionSamplerHigh; + #endif + + #ifdef USEIRRADIANCEMAP + uniform samplerCube irradianceSampler; + #endif + #else + #define sampleReflection(s, c) texture2D(s, c) + + uniform sampler2D reflectionSampler; + + #ifdef LODBASEDMICROSFURACE + #define sampleReflectionLod(s, c, l) texture2DLodEXT(s, c, l) + #else + uniform sampler2D reflectionSamplerLow; + uniform sampler2D reflectionSamplerHigh; + #endif + + #ifdef USEIRRADIANCEMAP + uniform sampler2D irradianceSampler; + #endif + #endif + + #ifdef REFLECTIONMAP_SKYBOX + varying vec3 vPositionUVW; + #else + #if defined(REFLECTIONMAP_EQUIRECTANGULAR_FIXED) || defined(REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED) + varying vec3 vDirectionW; + #endif + #endif +#endif + +#ifdef ENVIRONMENTBRDF + uniform sampler2D environmentBrdfSampler; +#endif + +#ifdef IBL_CDF_FILTERING + uniform sampler2D icdfSampler; +#endif \ No newline at end of file diff --git a/packages/dev/core/src/Shaders/ShadersInclude/openpbrGeometryInfo.fx b/packages/dev/core/src/Shaders/ShadersInclude/openpbrGeometryInfo.fx new file mode 100644 index 00000000000..7bd351efa69 --- /dev/null +++ b/packages/dev/core/src/Shaders/ShadersInclude/openpbrGeometryInfo.fx @@ -0,0 +1,37 @@ +struct geometryInfoOutParams +{ + float NdotV; + float NdotVUnclamped; + vec3 environmentBrdf; + float horizonOcclusion; +}; + +#define pbr_inline +geometryInfoOutParams geometryInfo( + in vec3 normalW, in vec3 viewDirectionW, in float roughness, in vec3 geometricNormalW +) +{ + geometryInfoOutParams outParams; + outParams.NdotVUnclamped = dot(normalW, viewDirectionW); + // The order 1886 page 3. + outParams.NdotV = absEps(outParams.NdotVUnclamped); + + #if defined(ENVIRONMENTBRDF) + // BRDF Lookup + outParams.environmentBrdf = getBRDFLookup(outParams.NdotV, roughness); + #else + outParams.environmentBrdf = vec3(0.0); + #endif + + outParams.horizonOcclusion = 1.0; + #if defined(ENVIRONMENTBRDF) && !defined(REFLECTIONMAP_SKYBOX) + #ifdef HORIZONOCCLUSION + #if defined(GEOMETRY_NORMAL) || defined(GEOMETRY_COAT_NORMAL) + #ifdef REFLECTIONMAP_3D + outParams.horizonOcclusion = environmentHorizonOcclusion(-viewDirectionW, normalW, geometricNormalW); + #endif + #endif + #endif + #endif + return outParams; +} \ No newline at end of file diff --git a/packages/dev/core/src/Shaders/ShadersInclude/openpbrIblFunctions.fx b/packages/dev/core/src/Shaders/ShadersInclude/openpbrIblFunctions.fx new file mode 100644 index 00000000000..5943c4fc620 --- /dev/null +++ b/packages/dev/core/src/Shaders/ShadersInclude/openpbrIblFunctions.fx @@ -0,0 +1,216 @@ +#ifdef REFLECTION + // _____________________________ Irradiance ________________________________ + // surfaceNormal is the direction to sample. Pass in a refracted vector to sample + // diffusely refracted light. + vec3 sampleIrradiance( + in vec3 surfaceNormal + #if defined(NORMAL) && defined(USESPHERICALINVERTEX) + , in vec3 vEnvironmentIrradianceSH + #endif + #if (defined(USESPHERICALFROMREFLECTIONMAP) && (!defined(NORMAL) || !defined(USESPHERICALINVERTEX))) || (defined(USEIRRADIANCEMAP) && defined(REFLECTIONMAP_3D)) + , in mat4 iblMatrix + #endif + #ifdef USEIRRADIANCEMAP + #ifdef REFLECTIONMAP_3D + , in samplerCube irradianceSampler + #else + , in sampler2D irradianceSampler + #endif + #ifdef USE_IRRADIANCE_DOMINANT_DIRECTION + , in vec3 reflectionDominantDirection + #endif + #endif + #ifdef REALTIME_FILTERING + , in vec2 vReflectionFilteringInfo + #ifdef IBL_CDF_FILTERING + , in sampler2D icdfSampler + #endif + #endif + , in vec2 vReflectionInfos + , in vec3 viewDirectionW + , in float diffuseRoughness + , in vec3 surfaceAlbedo + ) { + vec3 environmentIrradiance = vec3(0., 0., 0.); + + #if (defined(USESPHERICALFROMREFLECTIONMAP) && (!defined(NORMAL) || !defined(USESPHERICALINVERTEX))) || (defined(USEIRRADIANCEMAP) && defined(REFLECTIONMAP_3D)) + vec3 irradianceVector = (iblMatrix * vec4(surfaceNormal, 0)).xyz; + vec3 irradianceView = (iblMatrix * 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 && BASE_DIFFUSE_MODEL != BRDF_DIFFUSE_MODEL_LEGACY + { + float NdotV = max(dot(surfaceNormal, viewDirectionW), 0.0); + irradianceVector = mix(irradianceVector, irradianceView, (0.5 * (1.0 - NdotV)) * diffuseRoughness); + } + #endif + #endif + + #ifdef REFLECTIONMAP_OPPOSITEZ + irradianceVector.z *= -1.0; + #endif + + #ifdef INVERTCUBICMAP + irradianceVector.y *= -1.0; + #endif + #endif + #ifdef USESPHERICALFROMREFLECTIONMAP + #if defined(NORMAL) && defined(USESPHERICALINVERTEX) + environmentIrradiance = vEnvironmentIrradianceSH; + #else + #if defined(REALTIME_FILTERING) + environmentIrradiance = irradiance(reflectionSampler, irradianceVector, vReflectionFilteringInfo, diffuseRoughness, surfaceAlbedo, irradianceView + #ifdef IBL_CDF_FILTERING + , icdfSampler + #endif + ); + #else + environmentIrradiance = computeEnvironmentIrradiance(irradianceVector); + #endif + #endif + #elif defined(USEIRRADIANCEMAP) + #ifdef REFLECTIONMAP_3D + vec4 environmentIrradianceFromTexture = sampleReflection(irradianceSampler, irradianceVector); + #else + // TODO: What kind of irradiance map isn't 3D? + vec4 environmentIrradianceFromTexture = sampleReflection(irradianceSampler, reflectionCoords); + #endif + + environmentIrradiance = environmentIrradianceFromTexture.rgb; + #ifdef RGBDREFLECTION + environmentIrradiance.rgb = fromRGBD(environmentIrradianceFromTexture); + #endif + + #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(clamp(mag * NoV, 0.0, 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 *= vReflectionInfos.x; + return environmentIrradiance; + } + + #define pbr_inline + #ifdef REFLECTIONMAP_3D + vec3 createReflectionCoords( + #else + vec2 createReflectionCoords( + #endif + in vec3 vPositionW + , in vec3 normalW + #ifdef ANISOTROPIC + , in anisotropicOutParams anisotropicOut + #endif + ) + { + #ifdef ANISOTROPIC + vec3 reflectionVector = computeReflectionCoords(vec4(vPositionW, 1.0), anisotropicOut.anisotropicNormal); + #else + vec3 reflectionVector = computeReflectionCoords(vec4(vPositionW, 1.0), normalW); + #endif + + #ifdef REFLECTIONMAP_OPPOSITEZ + reflectionVector.z *= -1.0; + #endif + + // _____________________________ 2D vs 3D Maps ________________________________ + #ifdef REFLECTIONMAP_3D + vec3 reflectionCoords = reflectionVector; + #else + vec2 reflectionCoords = reflectionVector.xy; + #ifdef REFLECTIONMAP_PROJECTION + reflectionCoords /= reflectionVector.z; + #endif + reflectionCoords.y = 1.0 - reflectionCoords.y; + #endif + return reflectionCoords; + } + + #define pbr_inline + #define inline + vec3 sampleRadiance( + in float alphaG + , in vec3 vReflectionMicrosurfaceInfos + , in vec2 vReflectionInfos + #if defined(LODINREFLECTIONALPHA) && !defined(REFLECTIONMAP_SKYBOX) + , in float NdotVUnclamped + #endif + #ifdef REFLECTIONMAP_3D + , in samplerCube reflectionSampler + , const vec3 reflectionCoords + #else + , in sampler2D reflectionSampler + , const vec2 reflectionCoords + #endif + #ifdef REALTIME_FILTERING + , in vec2 vReflectionFilteringInfo + #endif + ) + { + vec4 environmentRadiance = vec4(0., 0., 0., 0.); + // _____________________________ 2D vs 3D Maps ________________________________ + #if defined(LODINREFLECTIONALPHA) && !defined(REFLECTIONMAP_SKYBOX) + float reflectionLOD = getLodFromAlphaG(vReflectionMicrosurfaceInfos.x, alphaG, NdotVUnclamped); + #elif defined(LINEARSPECULARREFLECTION) + float reflectionLOD = getLinearLodFromRoughness(vReflectionMicrosurfaceInfos.x, roughness); + #else + float reflectionLOD = getLodFromAlphaG(vReflectionMicrosurfaceInfos.x, alphaG); + #endif + + // Apply environment convolution scale/offset filter tuning parameters to the mipmap LOD selection + reflectionLOD = reflectionLOD * vReflectionMicrosurfaceInfos.y + vReflectionMicrosurfaceInfos.z; + + #ifdef REALTIME_FILTERING + environmentRadiance = vec4(radiance(alphaG, reflectionSampler, reflectionCoords, vReflectionFilteringInfo), 1.0); + #else + environmentRadiance = sampleReflectionLod(reflectionSampler, reflectionCoords, reflectionLOD); + #endif + + #ifdef RGBDREFLECTION + environmentRadiance.rgb = fromRGBD(environmentRadiance); + #endif + + #ifdef GAMMAREFLECTION + environmentRadiance.rgb = toLinearSpace(environmentRadiance.rgb); + #endif + + // _____________________________ Levels _____________________________________ + environmentRadiance.rgb *= vec3(vReflectionInfos.x); + return environmentRadiance.rgb; + } + + #define pbr_inline + vec3 conductorIblFresnel(in ReflectanceParams reflectance, in float NdotV, in float roughness, in vec3 environmentBrdf) + { + #if (CONDUCTOR_SPECULAR_MODEL == CONDUCTOR_SPECULAR_MODEL_OPENPBR) + // This is an empirical hack to modify the F0 albedo based on roughness. It's not based on any paper + // or anything. Just trying to match results of rough metals in a pathtracer. + vec3 albedoF0 = mix(reflectance.coloredF0, pow(reflectance.coloredF0, vec3(1.4)), roughness); + return getF82Specular(NdotV, albedoF0, reflectance.coloredF90, roughness); + #else + return getReflectanceFromBRDFLookup(reflectance.coloredF0, reflectance.coloredF90, environmentBrdf); + #endif + } +#endif diff --git a/packages/dev/core/src/Shaders/ShadersInclude/openpbrNormalMapFragment.fx b/packages/dev/core/src/Shaders/ShadersInclude/openpbrNormalMapFragment.fx new file mode 100644 index 00000000000..3738d278e63 --- /dev/null +++ b/packages/dev/core/src/Shaders/ShadersInclude/openpbrNormalMapFragment.fx @@ -0,0 +1,80 @@ +vec2 uvOffset = vec2(0.0, 0.0); +#if defined(GEOMETRY_NORMAL) || defined(PARALLAX) || defined(DETAIL) + #ifdef NORMALXYSCALE + float normalScale = 1.0; + #elif defined(GEOMETRY_NORMAL) + float normalScale = vGeometryNormalInfos.y; + #else + float normalScale = 1.0; + #endif + + #if defined(TANGENT) && defined(NORMAL) + mat3 TBN = vTBN; + #elif defined(GEOMETRY_NORMAL) + // flip the uv for the backface + vec2 TBNUV = gl_FrontFacing ? vGeometryNormalUV : -vGeometryNormalUV; + mat3 TBN = cotangent_frame(normalW * normalScale, vPositionW, TBNUV, vTangentSpaceParams); + #else + // flip the uv for the backface + vec2 TBNUV = gl_FrontFacing ? vDetailUV : -vDetailUV; + mat3 TBN = cotangent_frame(normalW * normalScale, vPositionW, TBNUV, vec2(1., 1.)); + #endif +#elif defined(ANISOTROPIC) + #if defined(TANGENT) && defined(NORMAL) + mat3 TBN = vTBN; + #else + // flip the uv for the backface + vec2 TBNUV = gl_FrontFacing ? vMainUV1 : -vMainUV1; + mat3 TBN = cotangent_frame(normalW, vPositionW, TBNUV, vec2(1., 1.)); + #endif +#endif + +#ifdef PARALLAX + mat3 invTBN = transposeMat3(TBN); + + #ifdef PARALLAXOCCLUSION + // TODO: Implement parallax occlusion scale + // uvOffset = parallaxOcclusion(invTBN * -viewDirectionW, invTBN * normalW, vGeometryNormalUV, vGeometryNormalInfos.z); + #else + // uvOffset = parallaxOffset(invTBN * viewDirectionW, vGeometryNormalInfos.z); + #endif +#endif + +#ifdef DETAIL + vec4 detailColor = texture2D(detailSampler, vDetailUV + uvOffset); + vec2 detailNormalRG = detailColor.wy * 2.0 - 1.0; + float detailNormalB = sqrt(1. - saturate(dot(detailNormalRG, detailNormalRG))); + vec3 detailNormal = vec3(detailNormalRG, detailNormalB); +#endif + +#ifdef GEOMETRY_COAT_NORMAL + coatNormalW = perturbNormal(TBN, texture2D(geometryCoatNormalSampler, vGeometryCoatNormalUV + uvOffset).xyz, vGeometryCoatNormalInfos.y); +#endif + +#ifdef GEOMETRY_NORMAL + #ifdef OBJECTSPACE_NORMALMAP + + #define CUSTOM_FRAGMENT_BUMP_FRAGMENT + + normalW = normalize(texture2D(geometryNormalSampler, vGeometryNormalUV).xyz * 2.0 - 1.0); + normalW = normalize(mat3(normalMatrix) * normalW); + #elif !defined(DETAIL) + normalW = perturbNormal(TBN, texture2D(geometryNormalSampler, vGeometryNormalUV + uvOffset).xyz, vGeometryNormalInfos.y); + #else + vec3 sampledNormal = texture2D(geometryNormalSampler, vGeometryNormalUV + uvOffset).xyz * 2.0 - 1.0; + // Reference for normal blending: https://blog.selfshadow.com/publications/blending-in-detail/ + #if DETAIL_NORMALBLENDMETHOD == 0 // whiteout + detailNormal.xy *= vDetailInfos.z; + vec3 blendedNormal = normalize(vec3(sampledNormal.xy + detailNormal.xy, sampledNormal.z * detailNormal.z)); + #elif DETAIL_NORMALBLENDMETHOD == 1 // RNM + detailNormal.xy *= vDetailInfos.z; + sampledNormal += vec3(0.0, 0.0, 1.0); + detailNormal *= vec3(-1.0, -1.0, 1.0); + vec3 blendedNormal = sampledNormal * dot(sampledNormal, detailNormal) / sampledNormal.z - detailNormal; + #endif + normalW = perturbNormalBase(TBN, blendedNormal, vGeometryNormalInfos.y); + #endif +#elif defined(DETAIL) + detailNormal.xy *= vDetailInfos.z; + normalW = perturbNormalBase(TBN, detailNormal, vDetailInfos.z); +#endif diff --git a/packages/dev/core/src/Shaders/ShadersInclude/openpbrNormalMapFragmentFunctions.fx b/packages/dev/core/src/Shaders/ShadersInclude/openpbrNormalMapFragmentFunctions.fx new file mode 100644 index 00000000000..a3ff73a1fe9 --- /dev/null +++ b/packages/dev/core/src/Shaders/ShadersInclude/openpbrNormalMapFragmentFunctions.fx @@ -0,0 +1,83 @@ +#if defined(GEOMETRY_NORMAL) + #include(_DEFINENAME_,GEOMETRY_NORMAL,_VARYINGNAME_,GeometryNormal,_SAMPLERNAME_,geometryNormal) +#endif + +#if defined(GEOMETRY_COAT_NORMAL) + #include(_DEFINENAME_,GEOMETRY_COAT_NORMAL,_VARYINGNAME_,GeometryCoatNormal,_SAMPLERNAME_,geometryCoatNormal) +#endif + +#if defined(DETAIL) + #include(_DEFINENAME_,DETAIL,_VARYINGNAME_,Detail,_SAMPLERNAME_,detail) +#endif + +#if defined(GEOMETRY_NORMAL) && defined(PARALLAX) + const float minSamples = 4.; + const float maxSamples = 15.; + const int iMaxSamples = 15; + + // http://www.gamedev.net/page/resources/_/technical/graphics-programming-and-theory/a-closer-look-at-parallax-occlusion-mapping-r3262 + vec2 parallaxOcclusion(vec3 vViewDirCoT, vec3 vNormalCoT, vec2 texCoord, float parallaxScale) { + + float parallaxLimit = length(vViewDirCoT.xy) / vViewDirCoT.z; + parallaxLimit *= parallaxScale; + vec2 vOffsetDir = normalize(vViewDirCoT.xy); + vec2 vMaxOffset = vOffsetDir * parallaxLimit; + float numSamples = maxSamples + (dot(vViewDirCoT, vNormalCoT) * (minSamples - maxSamples)); + float stepSize = 1.0 / numSamples; + + // Initialize the starting view ray height and the texture offsets. + float currRayHeight = 1.0; + vec2 vCurrOffset = vec2(0, 0); + vec2 vLastOffset = vec2(0, 0); + + float lastSampledHeight = 1.0; + float currSampledHeight = 1.0; + + bool keepWorking = true; + for (int i = 0; i < iMaxSamples; i++) + { + currSampledHeight = texture2D(geometryNormalSampler, texCoord + vCurrOffset).w; + + // Test if the view ray has intersected the surface. + if (!keepWorking) + { + // do nothing + } + else if (currSampledHeight > currRayHeight) + { + float delta1 = currSampledHeight - currRayHeight; + float delta2 = (currRayHeight + stepSize) - lastSampledHeight; + float ratio = delta1 / (delta1 + delta2); + vCurrOffset = (ratio)* vLastOffset + (1.0 - ratio) * vCurrOffset; + + keepWorking = false; + } + else + { + currRayHeight -= stepSize; + vLastOffset = vCurrOffset; + #ifdef PARALLAX_RHS + vCurrOffset -= stepSize * vMaxOffset; + #else + vCurrOffset += stepSize * vMaxOffset; + #endif + + lastSampledHeight = currSampledHeight; + } + } + + return vCurrOffset; + } + + vec2 parallaxOffset(vec3 viewDir, float heightScale) + { + // calculate amount of offset for Parallax Mapping With Offset Limiting + float height = texture2D(geometryNormalSampler, vGeometryNormalUV).w; + vec2 texCoordOffset = heightScale * viewDir.xy * height; + #ifdef PARALLAX_RHS + return texCoordOffset; + #else + return -texCoordOffset; + #endif + } +#endif diff --git a/packages/dev/core/src/Shaders/ShadersInclude/openpbrNormalMapFragmentMainFunctions.fx b/packages/dev/core/src/Shaders/ShadersInclude/openpbrNormalMapFragmentMainFunctions.fx new file mode 100644 index 00000000000..98d26854602 --- /dev/null +++ b/packages/dev/core/src/Shaders/ShadersInclude/openpbrNormalMapFragmentMainFunctions.fx @@ -0,0 +1,108 @@ +#if defined(GEOMETRY_NORMAL) || defined(GEOMETRY_COAT_NORMAL) || defined(ANISOTROPIC) || defined(DETAIL) + #if defined(TANGENT) && defined(NORMAL) + varying mat3 vTBN; + #endif + + #ifdef OBJECTSPACE_NORMALMAP + uniform mat4 normalMatrix; + + #if defined(WEBGL2) || defined(WEBGPU) + mat4 toNormalMatrix(mat4 wMatrix) + { + mat4 ret = inverse(wMatrix); + ret = transpose(ret); + ret[0][3] = 0.; + ret[1][3] = 0.; + ret[2][3] = 0.; + ret[3] = vec4(0., 0., 0., 1.); + return ret; + } + #else + mat4 toNormalMatrix(mat4 m) + { + float + a00 = m[0][0], a01 = m[0][1], a02 = m[0][2], a03 = m[0][3], + a10 = m[1][0], a11 = m[1][1], a12 = m[1][2], a13 = m[1][3], + a20 = m[2][0], a21 = m[2][1], a22 = m[2][2], a23 = m[2][3], + a30 = m[3][0], a31 = m[3][1], a32 = m[3][2], a33 = m[3][3], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32, + + det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + + mat4 mi = mat4( + a11 * b11 - a12 * b10 + a13 * b09, + a02 * b10 - a01 * b11 - a03 * b09, + a31 * b05 - a32 * b04 + a33 * b03, + a22 * b04 - a21 * b05 - a23 * b03, + a12 * b08 - a10 * b11 - a13 * b07, + a00 * b11 - a02 * b08 + a03 * b07, + a32 * b02 - a30 * b05 - a33 * b01, + a20 * b05 - a22 * b02 + a23 * b01, + a10 * b10 - a11 * b08 + a13 * b06, + a01 * b08 - a00 * b10 - a03 * b06, + a30 * b04 - a31 * b02 + a33 * b00, + a21 * b02 - a20 * b04 - a23 * b00, + a11 * b07 - a10 * b09 - a12 * b06, + a00 * b09 - a01 * b07 + a02 * b06, + a31 * b01 - a30 * b03 - a32 * b00, + a20 * b03 - a21 * b01 + a22 * b00) / det; + + return mat4(mi[0][0], mi[1][0], mi[2][0], mi[3][0], + mi[0][1], mi[1][1], mi[2][1], mi[3][1], + mi[0][2], mi[1][2], mi[2][2], mi[3][2], + mi[0][3], mi[1][3], mi[2][3], mi[3][3]); + } + #endif + #endif + + vec3 perturbNormalBase(mat3 cotangentFrame, vec3 normal, float scale) + { + #ifdef NORMALXYSCALE + normal = normalize(normal * vec3(scale, scale, 1.0)); + #endif + + return normalize(cotangentFrame * normal); + } + + vec3 perturbNormal(mat3 cotangentFrame, vec3 textureSample, float scale) + { + return perturbNormalBase(cotangentFrame, textureSample * 2.0 - 1.0, scale); + } + + // Thanks to http://www.thetenthplanet.de/archives/1180 + mat3 cotangent_frame(vec3 normal, vec3 p, vec2 uv, vec2 tangentSpaceParams) + { + // get edge vectors of the pixel triangle + vec3 dp1 = dFdx(p); + vec3 dp2 = dFdy(p); + vec2 duv1 = dFdx(uv); + vec2 duv2 = dFdy(uv); + + // solve the linear system + vec3 dp2perp = cross(dp2, normal); + vec3 dp1perp = cross(normal, dp1); + vec3 tangent = dp2perp * duv1.x + dp1perp * duv2.x; + vec3 bitangent = dp2perp * duv1.y + dp1perp * duv2.y; + + // invert the tangent/bitangent if requested + tangent *= tangentSpaceParams.x; + bitangent *= tangentSpaceParams.y; + + // construct a scale-invariant frame + float det = max(dot(tangent, tangent), dot(bitangent, bitangent)); + float invmax = det == 0.0 ? 0.0 : inversesqrt(det); + return mat3(tangent * invmax, bitangent * invmax, normal); + } +#endif diff --git a/packages/dev/core/src/Shaders/ShadersInclude/openpbrNormalMapVertex.fx b/packages/dev/core/src/Shaders/ShadersInclude/openpbrNormalMapVertex.fx new file mode 100644 index 00000000000..66ea1a3a889 --- /dev/null +++ b/packages/dev/core/src/Shaders/ShadersInclude/openpbrNormalMapVertex.fx @@ -0,0 +1,8 @@ +#if defined(GEOMETRY_NORMAL) || defined(PARALLAX) || defined(GEOMETRY_COAT_NORMAL) || defined(ANISOTROPIC) + #if defined(TANGENT) && defined(NORMAL) + vec3 tbnNormal = normalize(normalUpdated); + vec3 tbnTangent = normalize(tangentUpdated.xyz); + vec3 tbnBitangent = cross(tbnNormal, tbnTangent) * tangentUpdated.w; + vTBN = mat3(finalWorld) * mat3(tbnTangent, tbnBitangent, tbnNormal); + #endif +#endif \ No newline at end of file diff --git a/packages/dev/core/src/Shaders/ShadersInclude/openpbrNormalMapVertexDeclaration.fx b/packages/dev/core/src/Shaders/ShadersInclude/openpbrNormalMapVertexDeclaration.fx new file mode 100644 index 00000000000..2441ca7d786 --- /dev/null +++ b/packages/dev/core/src/Shaders/ShadersInclude/openpbrNormalMapVertexDeclaration.fx @@ -0,0 +1,5 @@ +#if defined(GEOMETRY_NORMAL) || defined(PARALLAX) || defined(GEOMETRY_COAT_NORMAL) || defined(ANISOTROPIC) + #if defined(TANGENT) && defined(NORMAL) + varying mat3 vTBN; + #endif +#endif diff --git a/packages/dev/core/src/Shaders/ShadersInclude/openpbrUboDeclaration.fx b/packages/dev/core/src/Shaders/ShadersInclude/openpbrUboDeclaration.fx new file mode 100644 index 00000000000..fff485abbbd --- /dev/null +++ b/packages/dev/core/src/Shaders/ShadersInclude/openpbrUboDeclaration.fx @@ -0,0 +1,110 @@ +layout(std140, column_major) uniform; + +// layout(set = 0, binding = 0) uniform Harmonics +// { +// uniform vec3 vSphericalL00; +// uniform vec3 vSphericalL1_1; +// uniform vec3 vSphericalL10; +// uniform vec3 vSphericalL11; +// uniform vec3 vSphericalL2_2; +// uniform vec3 vSphericalL2_1; +// uniform vec3 vSphericalL20; +// uniform vec3 vSphericalL21; +// uniform vec3 vSphericalL22; +// uniform vec3 vSphericalX; +// uniform vec3 vSphericalY; +// uniform vec3 vSphericalZ; +// uniform vec3 vSphericalXX_ZZ; +// uniform vec3 vSphericalYY_ZZ; +// uniform vec3 vSphericalZZ; +// uniform vec3 vSphericalXY; +// uniform vec3 vSphericalYZ; +// uniform vec3 vSphericalZX; +// } + +uniform Material { + vec2 vTangentSpaceParams; + vec4 vLightingIntensity; + float pointSize; + + vec2 vDebugMode; + + vec4 cameraInfo; + + vec2 vReflectionInfos; + mat4 reflectionMatrix; + vec3 vReflectionMicrosurfaceInfos; + vec3 vReflectionPosition; + vec3 vReflectionSize; + vec2 vReflectionFilteringInfo; + vec3 vReflectionDominantDirection; + vec3 vReflectionColor; + + vec3 vSphericalL00; + vec3 vSphericalL1_1; + vec3 vSphericalL10; + vec3 vSphericalL11; + vec3 vSphericalL2_2; + vec3 vSphericalL2_1; + vec3 vSphericalL20; + vec3 vSphericalL21; + vec3 vSphericalL22; + + vec3 vSphericalX; + vec3 vSphericalY; + vec3 vSphericalZ; + vec3 vSphericalXX_ZZ; + vec3 vSphericalYY_ZZ; + vec3 vSphericalZZ; + vec3 vSphericalXY; + vec3 vSphericalYZ; + vec3 vSphericalZX; + + float vBaseWeight; + vec4 vBaseColor; + float vBaseDiffuseRoughness; + vec4 vReflectanceInfo; + vec4 vSpecularColor; + float vCoatWeight; + vec3 vCoatColor; + float vCoatRoughness; + float vCoatIor; + float vCoatDarkening; + vec3 vEmissionColor; + + vec2 vBaseWeightInfos; + mat4 baseWeightMatrix; + vec2 vBaseColorInfos; + mat4 baseColorMatrix; + vec2 vBaseDiffuseRoughnessInfos; + mat4 baseDiffuseRoughnessMatrix; + vec2 vSpecularWeightInfos; + mat4 specularWeightMatrix; + vec2 vSpecularColorInfos; + mat4 specularColorMatrix; + vec2 vBaseMetalRoughInfos; + mat4 baseMetalRoughMatrix; + vec2 vCoatWeightInfos; + mat4 coatWeightMatrix; + vec2 vCoatColorInfos; + mat4 coatColorMatrix; + vec2 vCoatRoughnessInfos; + mat4 coatRoughnessMatrix; + vec2 vCoatDarkeningInfos; + mat4 coatDarkeningMatrix; + vec2 vGeometryNormalInfos; + mat4 geometryNormalMatrix; + vec2 vGeometryCoatNormalInfos; + mat4 geometryCoatNormalMatrix; + vec2 vGeometryOpacityInfos; + mat4 geometryOpacityMatrix; + vec2 vEmissionColorInfos; + mat4 emissionColorMatrix; + vec2 vAmbientOcclusionInfos; + mat4 ambientOcclusionMatrix; + +#define ADDITIONAL_UBO_DECLARATION +}; + +#include +#include diff --git a/packages/dev/core/src/Shaders/ShadersInclude/openpbrVertexDeclaration.fx b/packages/dev/core/src/Shaders/ShadersInclude/openpbrVertexDeclaration.fx new file mode 100644 index 00000000000..a9a6e8da339 --- /dev/null +++ b/packages/dev/core/src/Shaders/ShadersInclude/openpbrVertexDeclaration.fx @@ -0,0 +1,146 @@ +uniform mat4 view; +uniform mat4 viewProjection; +uniform vec4 vEyePosition; +#ifdef MULTIVIEW + mat4 viewProjectionR; +#endif + +#ifdef BASE_COLOR +uniform vec2 vBaseColorInfos; +uniform mat4 baseColorMatrix; +#endif + +#ifdef BASE_WEIGHT +uniform mat4 baseWeightMatrix; +uniform vec2 vBaseWeightInfos; +#endif + +uniform float vBaseDiffuseRoughness; +#ifdef BASE_DIFFUSE_ROUGHNESS +uniform mat4 baseDiffuseRoughnessMatrix; +uniform vec2 vBaseDiffuseRoughnessInfos; +#endif + +#ifdef AMBIENT_OCCLUSION +uniform vec2 vAmbientOcclusionInfos; +uniform mat4 ambientOcclusionMatrix; +#endif + +#ifdef EMISSION_COLOR +uniform vec2 vEmissionColorInfos; +uniform mat4 emissionColorMatrix; +#endif + +#ifdef LIGHTMAP +uniform vec2 vLightmapInfos; +uniform mat4 lightmapMatrix; +#endif + +#ifdef METALLIC_ROUGHNESS +uniform vec2 vBaseMetalRoughInfos; +uniform mat4 baseMetalRoughMatrix; +#endif + +#ifdef SPECULAR_WEIGHT +uniform vec2 vSpecularWeightInfos; +uniform mat4 specularWeightMatrix; +#endif + +#ifdef SPECULAR_COLOR +uniform vec2 vSpecularColorInfos; +uniform mat4 specularColorMatrix; +#endif + +#ifdef COAT_WEIGHT +uniform vec2 vCoatWeightInfos; +uniform mat4 coatWeightMatrix; +#endif + +#ifdef COAT_COLOR +uniform vec2 vCoatColorInfos; +uniform mat4 coatColorMatrix; +#endif + +#ifdef COAT_ROUGHNESS +uniform vec2 vCoatRoughnessInfos; +uniform mat4 coatRoughnessMatrix; +#endif + +#ifdef COAT_ROUGHNESS_ANISOTROPY +uniform vec2 vCoatRoughnessAnisotropyInfos; +uniform mat4 coatRoughnessAnisotropyMatrix; +#endif + +#ifdef COAT_IOR +uniform vec2 vCoatIorInfos; +uniform mat4 coatIorMatrix; +#endif + +#ifdef COAT_DARKENING +uniform vec2 vCoatDarkeningInfos; +uniform mat4 coatDarkeningMatrix; +#endif + +#ifdef GEOMETRY_NORMAL +uniform vec2 vGeometryNormalInfos; +uniform mat4 geometryNormalMatrix; +#endif + +#ifdef GEOMETRY_COAT_NORMAL +uniform vec2 vGeometryCoatNormalInfos; +uniform mat4 geometryCoatNormalMatrix; +#endif + +#ifdef GEOMETRY_OPACITY +uniform mat4 geometryOpacityMatrix; +uniform vec2 vGeometryOpacityInfos; +#endif + +#ifdef POINTSIZE +uniform float pointSize; +#endif + +uniform vec4 cameraInfo; + +// Reflection +#ifdef REFLECTION + uniform vec2 vReflectionInfos; + uniform mat4 reflectionMatrix; +#endif + +#ifdef NORMAL + #if defined(USESPHERICALFROMREFLECTIONMAP) && defined(USESPHERICALINVERTEX) + #ifdef USESPHERICALFROMREFLECTIONMAP + #ifdef SPHERICAL_HARMONICS + uniform vec3 vSphericalL00; + uniform vec3 vSphericalL1_1; + uniform vec3 vSphericalL10; + uniform vec3 vSphericalL11; + uniform vec3 vSphericalL2_2; + uniform vec3 vSphericalL2_1; + uniform vec3 vSphericalL20; + uniform vec3 vSphericalL21; + uniform vec3 vSphericalL22; + #else + uniform vec3 vSphericalX; + uniform vec3 vSphericalY; + uniform vec3 vSphericalZ; + uniform vec3 vSphericalXX_ZZ; + uniform vec3 vSphericalYY_ZZ; + uniform vec3 vSphericalZZ; + uniform vec3 vSphericalXY; + uniform vec3 vSphericalYZ; + uniform vec3 vSphericalZX; + #endif + #endif + #endif +#endif + +#ifdef DETAIL +uniform vec4 vDetailInfos; +uniform mat4 detailMatrix; +#endif + +#include + +#define ADDITIONAL_VERTEX_DECLARATION diff --git a/packages/dev/core/src/Shaders/ShadersInclude/pbrBRDFFunctions.fx b/packages/dev/core/src/Shaders/ShadersInclude/pbrBRDFFunctions.fx index db6a8130573..a6a53279de4 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/pbrBRDFFunctions.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/pbrBRDFFunctions.fx @@ -9,7 +9,7 @@ #define CONDUCTOR_SPECULAR_MODEL_GLTF 0 #define CONDUCTOR_SPECULAR_MODEL_OPENPBR 1 -#ifndef PBR_VERTEX_SHADER +#if !defined(PBR_VERTEX_SHADER) && !defined(OPENPBR_VERTEX_SHADER) // ______________________________________________________________________ // diff --git a/packages/dev/core/src/Shaders/ShadersInclude/pbrUboDeclaration.fx b/packages/dev/core/src/Shaders/ShadersInclude/pbrUboDeclaration.fx index 47a2252ac9d..cddb7c45b7a 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/pbrUboDeclaration.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/pbrUboDeclaration.fx @@ -32,10 +32,6 @@ uniform Material { vec2 vLightmapInfos; vec3 vReflectivityInfos; vec2 vMicroSurfaceSamplerInfos; - vec2 vReflectionInfos; - vec2 vReflectionFilteringInfo; - vec3 vReflectionPosition; - vec3 vReflectionSize; vec3 vBumpInfos; mat4 albedoMatrix; mat4 baseWeightMatrix; @@ -48,14 +44,10 @@ uniform Material { mat4 microSurfaceSamplerMatrix; mat4 bumpMatrix; vec2 vTangentSpaceParams; - mat4 reflectionMatrix; - vec3 vReflectionColor; vec4 vAlbedoColor; float baseWeight; float baseDiffuseRoughness; vec4 vLightingIntensity; - vec3 vReflectionMicrosurfaceInfos; - vec3 vReflectionDominantDirection; float pointSize; vec4 vReflectivityColor; vec3 vEmissiveColor; @@ -68,7 +60,17 @@ uniform Material { mat4 metallicReflectanceMatrix; vec2 vReflectanceInfos; mat4 reflectanceMatrix; + vec4 cameraInfo; + vec2 vReflectionInfos; + mat4 reflectionMatrix; + vec3 vReflectionMicrosurfaceInfos; + vec3 vReflectionPosition; + vec3 vReflectionSize; + vec2 vReflectionFilteringInfo; + vec3 vReflectionDominantDirection; + vec3 vReflectionColor; + vec3 vSphericalL00; vec3 vSphericalL1_1; vec3 vSphericalL10; @@ -89,8 +91,6 @@ uniform Material { vec3 vSphericalYZ; vec3 vSphericalZX; - vec4 cameraInfo; - #define ADDITIONAL_UBO_DECLARATION }; diff --git a/packages/dev/core/src/Shaders/openpbr.fragment.fx b/packages/dev/core/src/Shaders/openpbr.fragment.fx new file mode 100644 index 00000000000..b4dfe4c6b4a --- /dev/null +++ b/packages/dev/core/src/Shaders/openpbr.fragment.fx @@ -0,0 +1,230 @@ +#define OPENPBR_FRAGMENT_SHADER + +#define CUSTOM_FRAGMENT_EXTENSION + +#if defined(GEOMETRY_NORMAL) || defined(GEOMETRY_COAT_NORMAL) || !defined(NORMAL) || defined(FORCENORMALFORWARD) || defined(SPECULARAA) +#extension GL_OES_standard_derivatives : enable +#endif + +#ifdef LODBASEDMICROSFURACE +#extension GL_EXT_shader_texture_lod : enable +#endif + +#define CUSTOM_FRAGMENT_BEGIN + +#ifdef LOGARITHMICDEPTH +#extension GL_EXT_frag_depth : enable +#endif + +#include[SCENE_MRT_COUNT] + +precision highp float; +#include + +// Forces linear space for image processing +#ifndef FROMLINEARSPACE + #define FROMLINEARSPACE +#endif + +// Declaration +#include<__decl__openpbrFragment> + +#include +#include<__decl__lightFragment>[0..maxSimultaneousLights] +#include +#include +#include +#include +#include + +// Helper Functions +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef REFLECTION + #include +#endif + +#define CUSTOM_FRAGMENT_DEFINITIONS + +#include +#include + +#include +#include +#include + +// Do a mix between layers with additional multipliers for each layer. +vec3 layer(vec3 slab_bottom, vec3 slab_top, float lerp_factor, vec3 bottom_multiplier, vec3 top_multiplier) { + + return mix(slab_bottom * bottom_multiplier, slab_top * top_multiplier, lerp_factor); +} + +// _____________________________ MAIN FUNCTION ____________________________ +void main(void) { + + #define CUSTOM_FRAGMENT_MAIN_BEGIN + + #include + + // _____________________________ Geometry Information ____________________________ + #include + vec3 coatNormalW = normalW; + + #include + + #include + + // ______________________ Read Base properties & Opacity ______________________________ + #include + + // _____________________________ Read Coat Layer properties ______________________ + #include + + // TEMP + float subsurface_weight = 0.0; + float transmission_weight = 0.0; + float fuzz_weight = 0.0; + + #define CUSTOM_FRAGMENT_UPDATE_ALPHA + + #include + + #define CUSTOM_FRAGMENT_BEFORE_LIGHTS + + // _____________________________ AO _______________________________ + ambientOcclusionOutParams aoOut; + +#ifdef AMBIENT_OCCLUSION + vec3 ambientOcclusionFromTexture = texture2D(ambientOcclusionSampler, vAmbientOcclusionUV + uvOffset).rgb; +#endif + + aoOut = ambientOcclusionBlock( + #ifdef AMBIENT_OCCLUSION + ambientOcclusionFromTexture, + vAmbientOcclusionInfos + #endif + ); + + // _____________________________ Compute Geometry info for coat layer _________________________ + geometryInfoOutParams coatGeoInfo = geometryInfo( + coatNormalW, viewDirectionW.xyz, coat_roughness, geometricNormalW + ); + + // _____________________________ Compute Geometry info for base layer _________________________ + // Adjust the base roughness to account for the coat layer. Equation 61 in OpenPBR spec. + specular_roughness = mix(specular_roughness, pow(min(1.0, pow(specular_roughness, 4.0) + 2.0 * pow(coat_roughness, 4.0)), 0.25), coat_weight); + geometryInfoOutParams baseGeoInfo = geometryInfo( + normalW, viewDirectionW.xyz, specular_roughness, geometricNormalW + ); + + // _______________________ F0 and F90 Reflectance _______________________________ + + // Coat + ReflectanceParams coatReflectance; + coatReflectance = dielectricReflectance( + coat_ior // inside IOR + , 1.0 // outside IOR is air + , vec3(1.0) + , coat_weight + ); + + // Base Dielectric + ReflectanceParams baseDielectricReflectance; + { + float effectiveCoatIor = mix(1.0, coat_ior, coat_weight); + baseDielectricReflectance = dielectricReflectance( + specular_ior // inside IOR + , effectiveCoatIor // outside IOR is coat + , specular_color + , specular_weight + ); + } + + // Base Metallic + ReflectanceParams baseConductorReflectance; + baseConductorReflectance = conductorReflectance(base_color, specular_color, specular_weight); + + // ________________________ Environment (IBL) Lighting ____________________________ + vec3 material_surface_ibl = vec3(0., 0., 0.); + #include + + // __________________________ Direct Lighting ____________________________ + vec3 material_surface_direct = vec3(0., 0., 0.); + #if defined(LIGHT0) + float aggShadow = 0.; + float numLights = 0.; + #include[0..maxSimultaneousLights] + #include[0..maxSimultaneousLights] + + #endif + + // _________________________ Emissive Lighting _______________________________ + vec3 material_surface_emission = vEmissionColor; + #ifdef EMISSION_COLOR + vec3 emissionColorTex = texture2D(emissionColorSampler, vEmissionColorUV + uvOffset).rgb; + #ifdef EMISSION_COLOR_GAMMA + material_surface_emission *= toLinearSpace(emissionColorTex.rgb); + #else + material_surface_emission *= emissionColorTex.rgb; + #endif + material_surface_emission *= vEmissionColorInfos.y; + #endif + material_surface_emission *= vLightingIntensity.y; + + // _____________________________ Final Color Composition ________________________ + #define CUSTOM_FRAGMENT_BEFORE_FINALCOLORCOMPOSITION + + + vec4 finalColor = vec4(material_surface_ibl + material_surface_direct + material_surface_emission, alpha); + + + #define CUSTOM_FRAGMENT_BEFORE_FOG + + // _____________________________ Finally ___________________________________________ + finalColor = max(finalColor, 0.0); + + #include + #include(color, finalColor) + #include + + #define CUSTOM_FRAGMENT_BEFORE_FRAGCOLOR + +#ifdef PREPASS + #include +#endif + +#if !defined(PREPASS) || defined(WEBGL2) + gl_FragColor = finalColor; +#endif + + #include + +#if ORDER_INDEPENDENT_TRANSPARENCY + if (fragDepth == nearestDepth) { + frontColor.rgb += finalColor.rgb * finalColor.a * alphaMultiplier; + // Cancels the 1 - a initial value operation + frontColor.a = 1.0 - alphaMultiplier * (1.0 - finalColor.a); + } else { + backColor += finalColor; + } +#endif + + #include + + #define CUSTOM_FRAGMENT_MAIN_END + +} diff --git a/packages/dev/core/src/Shaders/openpbr.vertex.fx b/packages/dev/core/src/Shaders/openpbr.vertex.fx new file mode 100644 index 00000000000..4a2613db5fe --- /dev/null +++ b/packages/dev/core/src/Shaders/openpbr.vertex.fx @@ -0,0 +1,265 @@ +#define OPENPBR_VERTEX_SHADER + +#define CUSTOM_VERTEX_EXTENSION + +precision highp float; + +#include<__decl__openpbrVertex> + +#define CUSTOM_VERTEX_BEGIN + +// Attributes +attribute vec3 position; +#ifdef NORMAL +attribute vec3 normal; +#endif +#ifdef TANGENT +attribute vec4 tangent; +#endif +#ifdef UV1 +attribute vec2 uv; +#endif +#include[2..7] +#include[1..7] +#ifdef VERTEXCOLOR +attribute vec4 color; +#endif + +#include +#include +#include +#include + +#include +#include + +#include(_DEFINENAME_,BASE_COLOR,_VARYINGNAME_,BaseColor) +#include(_DEFINENAME_,BASE_WEIGHT,_VARYINGNAME_,BaseWeight) +#include(_DEFINENAME_,BASE_DIFFUSE_ROUGHNESS,_VARYINGNAME_,BaseDiffuseRoughness) +#include(_DEFINENAME_,METALLIC_ROUGHNESS,_VARYINGNAME_,BaseMetalRough) +#include(_DEFINENAME_,SPECULAR_WEIGHT,_VARYINGNAME_,SpecularWeight) +#include(_DEFINENAME_,SPECULAR_COLOR,_VARYINGNAME_,SpecularColor) +#include(_DEFINENAME_,COAT_WEIGHT,_VARYINGNAME_,CoatWeight) +#include(_DEFINENAME_,COAT_COLOR,_VARYINGNAME_,CoatColor) +#include(_DEFINENAME_,COAT_ROUGHNESS,_VARYINGNAME_,CoatRoughness) +// #include(_DEFINENAME_,COAT_ROUGHNESS_ANISOTROPY,_VARYINGNAME_,CoatRoughnessAnisotropy) +#include(_DEFINENAME_,COAT_DARKENING,_VARYINGNAME_,CoatDarkening) +#include(_DEFINENAME_,GEOMETRY_NORMAL,_VARYINGNAME_,GeometryNormal) +#include(_DEFINENAME_,GEOMETRY_COAT_NORMAL,_VARYINGNAME_,GeometryCoatNormal) +#include(_DEFINENAME_,GEOMETRY_OPACITY,_VARYINGNAME_,GeometryOpacity) +#include(_DEFINENAME_,EMISSION_COLOR,_VARYINGNAME_,EmissionColor) + +#include(_DEFINENAME_,AMBIENT_OCCLUSION,_VARYINGNAME_,AmbientOcclusion) +#include(_DEFINENAME_,DECAL,_VARYINGNAME_,Decal) +#include(_DEFINENAME_,DETAIL,_VARYINGNAME_,Detail) + +// Output +varying vec3 vPositionW; +#if DEBUGMODE > 0 + varying vec4 vClipSpacePosition; +#endif + +#ifdef NORMAL + varying vec3 vNormalW; + #if defined(USESPHERICALFROMREFLECTIONMAP) && defined(USESPHERICALINVERTEX) + varying vec3 vEnvironmentIrradiance; + + #include + #endif +#endif + +#if defined(VERTEXCOLOR) || defined(INSTANCESCOLOR) && defined(INSTANCES) +varying vec4 vColor; +#endif + +#include +#include +#include +#include<__decl__lightVxFragment>[0..maxSimultaneousLights] + +#include +#include[0..maxSimultaneousMorphTargets] + +#ifdef REFLECTIONMAP_SKYBOX +varying vec3 vPositionUVW; +#endif + +#if defined(REFLECTIONMAP_EQUIRECTANGULAR_FIXED) || defined(REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED) +varying vec3 vDirectionW; +#endif + +#include +#define CUSTOM_VERTEX_DEFINITIONS + +void main(void) { + + #define CUSTOM_VERTEX_MAIN_BEGIN + + vec3 positionUpdated = position; +#ifdef NORMAL + vec3 normalUpdated = normal; +#endif +#ifdef TANGENT + vec4 tangentUpdated = tangent; +#endif +#ifdef UV1 + vec2 uvUpdated = uv; +#endif +#ifdef UV2 + vec2 uv2Updated = uv2; +#endif +#ifdef VERTEXCOLOR + vec4 colorUpdated = color; +#endif + +#include +#include[0..maxSimultaneousMorphTargets] + +#ifdef REFLECTIONMAP_SKYBOX + vPositionUVW = positionUpdated; +#endif + +#define CUSTOM_VERTEX_UPDATE_POSITION + +#define CUSTOM_VERTEX_UPDATE_NORMAL + +#include + +#if defined(PREPASS) && ((defined(PREPASS_VELOCITY) || defined(PREPASS_VELOCITY_LINEAR)) && !defined(BONES_VELOCITY_ENABLED) + // Compute velocity before bones computation + vCurrentPosition = viewProjection * finalWorld * vec4(positionUpdated, 1.0); + vPreviousPosition = previousViewProjection * finalPreviousWorld * vec4(positionUpdated, 1.0); +#endif + +#include +#include + + vec4 worldPos = finalWorld * vec4(positionUpdated, 1.0); + vPositionW = vec3(worldPos); + +#ifdef PREPASS + #include +#endif + +#ifdef NORMAL + mat3 normalWorld = mat3(finalWorld); + + #if defined(INSTANCES) && defined(THIN_INSTANCES) + vNormalW = normalUpdated / vec3(dot(normalWorld[0], normalWorld[0]), dot(normalWorld[1], normalWorld[1]), dot(normalWorld[2], normalWorld[2])); + vNormalW = normalize(normalWorld * vNormalW); + #else + #ifdef NONUNIFORMSCALING + normalWorld = transposeMat3(inverseMat3(normalWorld)); + #endif + + vNormalW = normalize(normalWorld * normalUpdated); + #endif + + #if defined(USESPHERICALFROMREFLECTIONMAP) && defined(USESPHERICALINVERTEX) + #if BASE_DIFFUSE_MODEL != BRDF_DIFFUSE_MODEL_LAMBERT && BASE_DIFFUSE_MODEL != BRDF_DIFFUSE_MODEL_LEGACY + // Bend the normal towards the viewer based on the diffuse roughness + vec3 viewDirectionW = normalize(vEyePosition.xyz - vPositionW); + + #if !defined(NATIVE) && !defined(WEBGPU) + // Next two lines fixes a flickering that occurs on some specific circumstances on MacOS/iOS + // See https://forum.babylonjs.com/t/needdepthprepass-creates-flickering-in-8-6-2/58421/12 + // Note that the variable passed to isnan doesn't matter... + bool bbb = any(isnan(position)); + if (bbb) { } + #endif + + float NdotV = max(dot(vNormalW, viewDirectionW), 0.0); + vec3 roughNormal = mix(vNormalW, viewDirectionW, (0.5 * (1.0 - NdotV)) * vBaseDiffuseRoughness); + 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 + vEnvironmentIrradiance = computeEnvironmentIrradiance(reflectionVector); + #endif +#endif + +#define CUSTOM_VERTEX_UPDATE_WORLDPOS + +#ifdef MULTIVIEW + if (gl_ViewID_OVR == 0u) { + gl_Position = viewProjection * worldPos; + } else { + gl_Position = viewProjectionR * worldPos; + } +#else + gl_Position = viewProjection * worldPos; +#endif + +#if DEBUGMODE > 0 + vClipSpacePosition = gl_Position; +#endif + +#if defined(REFLECTIONMAP_EQUIRECTANGULAR_FIXED) || defined(REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED) + vDirectionW = normalize(vec3(finalWorld * vec4(positionUpdated, 0.0))); +#endif + + // Texture coordinates +#ifndef UV1 + vec2 uvUpdated = vec2(0., 0.); +#endif +#ifndef UV2 + vec2 uv2Updated = vec2(0., 0.); +#endif +#ifdef MAINUV1 + vMainUV1 = uvUpdated; +#endif +#ifdef MAINUV2 + vMainUV2 = uv2Updated; +#endif + + #include[3..7] + + #include(_DEFINENAME_,BASE_COLOR,_VARYINGNAME_,BaseColor,_MATRIXNAME_,albedo,_INFONAME_,BaseColorInfos.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_,METALLIC_ROUGHNESS,_VARYINGNAME_,BaseMetalRough,_MATRIXNAME_,baseMetalRough,_INFONAME_,BaseMetalRoughInfos.x) + #include(_DEFINENAME_,SPECULAR_WEIGHT,_VARYINGNAME_,SpecularWeight,_MATRIXNAME_,specularWeight,_INFONAME_,SpecularWeightInfos.x) + #include(_DEFINENAME_,SPECULAR_COLOR,_VARYINGNAME_,SpecularColor,_MATRIXNAME_,specularColor,_INFONAME_,SpecularColorInfos.x) + #include(_DEFINENAME_,COAT_WEIGHT,_VARYINGNAME_,CoatWeight,_MATRIXNAME_,coatWeight,_INFONAME_,CoatWeightInfos.x) + #include(_DEFINENAME_,COAT_COLOR,_VARYINGNAME_,CoatColor,_MATRIXNAME_,coatColor,_INFONAME_,CoatColorInfos.x) + #include(_DEFINENAME_,COAT_ROUGHNESS,_VARYINGNAME_,CoatRoughness,_MATRIXNAME_,coatRoughness,_INFONAME_,CoatRoughnessInfos.x) + // #include(_DEFINENAME_,COAT_ROUGHNESS_ANISOTROPY,_VARYINGNAME_,CoatRoughnessAnisotropy,_MATRIXNAME_,coatRoughnessAnisotropy,_INFONAME_,CoatRoughnessAnisotropyInfos.x) + #include(_DEFINENAME_,COAT_DARKENING,_VARYINGNAME_,CoatDarkening,_MATRIXNAME_,coatDarkening,_INFONAME_,CoatDarkeningInfos.x) + #include(_DEFINENAME_,GEOMETRY_NORMAL,_VARYINGNAME_,GeometryNormal,_MATRIXNAME_,geometryNormal,_INFONAME_,GeometryNormalInfos.x) + #include(_DEFINENAME_,GEOMETRY_COAT_NORMAL,_VARYINGNAME_,GeometryCoatNormal,_MATRIXNAME_,geometryCoatNormal,_INFONAME_,GeometryCoatNormalInfos.x) + #include(_DEFINENAME_,GEOMETRY_OPACITY,_VARYINGNAME_,GeometryOpacity,_MATRIXNAME_,geometryOpacity,_INFONAME_,GeometryOpacityInfos.x) + #include(_DEFINENAME_,EMISSION_COLOR,_VARYINGNAME_,EmissionColor,_MATRIXNAME_,emissionColor,_INFONAME_,EmissionColorInfos.x) + + #include(_DEFINENAME_,AMBIENT_OCCLUSION,_VARYINGNAME_,AmbientOcclusion,_MATRIXNAME_,ambientOcclusion,_INFONAME_,AmbientOcclusionInfos.x) + #include(_DEFINENAME_,DECAL,_VARYINGNAME_,Decal,_MATRIXNAME_,decal,_INFONAME_,DecalInfos.x) + #include(_DEFINENAME_,DETAIL,_VARYINGNAME_,Detail,_MATRIXNAME_,detail,_INFONAME_,DetailInfos.x) + + // TBN +#include + + // Clip plane +#include + + // Fog +#include + + // Shadows +#include[0..maxSimultaneousLights] + + // Vertex color +#include + + // Point size +#if defined(POINTSIZE) && !defined(WEBGPU) + gl_PointSize = pointSize; +#endif + + // Log. depth +#include + +#define CUSTOM_VERTEX_MAIN_END + +} diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/backgroundUboDeclaration.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/backgroundUboDeclaration.fx index 98551a79444..f6750a15a3f 100644 --- a/packages/dev/core/src/ShadersWGSL/ShadersInclude/backgroundUboDeclaration.fx +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/backgroundUboDeclaration.fx @@ -1,10 +1,7 @@ uniform vPrimaryColor: vec4f; uniform vPrimaryColorShadow: vec4f; -uniform vDiffuseInfos: vec2f; -uniform vReflectionInfos: vec2f; -uniform diffuseMatrix: mat4x4f; -uniform reflectionMatrix: mat4x4f; -uniform vReflectionMicrosurfaceInfos: vec3f; +uniform vDiffuseInfos : vec2f; +uniform diffuseMatrix : mat4x4f; uniform fFovMultiplier: f32; uniform pointSize: f32; @@ -14,4 +11,8 @@ uniform vBackgroundCenter: vec3f; uniform vReflectionControl: vec4f; uniform projectedGroundInfos: vec2f; +uniform vReflectionInfos : vec2f; +uniform reflectionMatrix : mat4x4f; +uniform vReflectionMicrosurfaceInfos : vec3f; + #include diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/defaultUboDeclaration.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/defaultUboDeclaration.fx index 5aec9a38d81..fe0b782bc3c 100644 --- a/packages/dev/core/src/ShadersWGSL/ShadersInclude/defaultUboDeclaration.fx +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/defaultUboDeclaration.fx @@ -10,9 +10,6 @@ uniform emissiveRightColor: vec4f; uniform vDiffuseInfos: vec2f; uniform vAmbientInfos: vec2f; uniform vOpacityInfos: vec2f; -uniform vReflectionInfos: vec2f; -uniform vReflectionPosition: vec3f; -uniform vReflectionSize: vec3f; uniform vEmissiveInfos: vec2f; uniform vLightmapInfos: vec2f; uniform vSpecularInfos: vec2f; @@ -20,7 +17,6 @@ uniform vBumpInfos: vec3f; uniform diffuseMatrix: mat4x4f; uniform ambientMatrix: mat4x4f; uniform opacityMatrix: mat4x4f; -uniform reflectionMatrix: mat4x4f; uniform emissiveMatrix: mat4x4f; uniform lightmapMatrix: mat4x4f; uniform specularMatrix: mat4x4f; @@ -37,6 +33,10 @@ uniform vEmissiveColor: vec3f; uniform vDiffuseColor: vec4f; uniform vAmbientColor: vec3f; uniform cameraInfo: vec4f; +uniform vReflectionInfos: vec2f; +uniform reflectionMatrix: mat4x4f; +uniform vReflectionPosition: vec3f; +uniform vReflectionSize: vec3f; #define ADDITIONAL_UBO_DECLARATION diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrBaseLayerData.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrBaseLayerData.fx new file mode 100644 index 00000000000..c94d77c43f4 --- /dev/null +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrBaseLayerData.fx @@ -0,0 +1,138 @@ +// This code reads uniforms and samples textures to fill up the base and specular +// layer properties for OpenPBR + +// Base Layer Properties +// We don't include base_weight in our initial variables because it is multiplied +// into the base_color in this code snippet. +var base_color = vec3f(0.8); +var base_metalness: f32 = 0.0; +var base_diffuse_roughness: f32 = 0.0; + +// Specular Layer Properties +var specular_weight: f32 = 1.0; +var specular_roughness: f32 = 0.3; +var specular_color: vec3f = vec3f(1.0); +var specular_roughness_anisotropy: f32 = 0.0; +var specular_ior: f32 = 1.5; +var alpha: f32 = 1.0; + +// Sample Base Layer properties from textures +#ifdef BASE_WEIGHT + var baseWeightFromTexture: vec4f = textureSample(baseWeightSampler, baseWeightSamplerSampler, fragmentInputs.vBaseWeightUV + uvOffset); +#endif + +#ifdef BASE_COLOR + var baseColorFromTexture: vec4f = textureSample(baseColorSampler, baseColorSamplerSampler, fragmentInputs.vBaseColorUV + uvOffset); +#endif + +#ifdef METALLIC_ROUGHNESS + var metallicRoughnessFromTexture: vec4f = textureSample(baseMetalRoughSampler, baseMetalRoughSamplerSampler, fragmentInputs.vBaseMetalRoughUV + uvOffset); +#endif + +#ifdef BASE_DIFFUSE_ROUGHNESS + var baseDiffuseRoughnessFromTexture: f32 = textureSample(baseDiffuseRoughnessSampler, baseDiffuseRoughnessSamplerSampler, fragmentInputs.vBaseDiffuseRoughnessUV + uvOffset).r; +#endif + +#ifdef GEOMETRY_OPACITY + var opacityFromTexture: vec4f = textureSample(opacitySampler, opacitySamplerSampler, fragmentInputs.vOpacityUV + uvOffset); +#endif + +#ifdef DECAL + var decalFromTexture: vec4f = textureSample(decalSampler, decalSamplerSampler, fragmentInputs.vDecalUV + uvOffset); +#endif + +#ifdef SPECULAR_COLOR + var specularColorFromTexture: vec4f = textureSample(specularColorSampler, specularColorSamplerSampler, fragmentInputs.vSpecularColorUV + uvOffset); + #ifdef SPECULAR_COLOR_GAMMA + specularColorFromTexture.rgb = toLinearSpace(specularColorFromTexture.rgb); + #endif +#endif + +#ifdef SPECULAR_WEIGHT + var specularWeightFromTexture: vec4f = textureSample(specularWeightSampler, specularWeightSamplerSampler, fragmentInputs.vSpecularWeightUV + uvOffset); +#endif + +// Initalize base layer properties from uniforms +base_color = uniforms.vBaseColor.rgb; +#if defined(VERTEXCOLOR) || defined(INSTANCESCOLOR) && defined(INSTANCES) + base_color *= uniforms.vColor.rgb; +#endif +#if defined(VERTEXALPHA) || defined(INSTANCESCOLOR) && defined(INSTANCES) + alpha *= uniforms.vColor.a; +#endif +base_color *= vec3(uniforms.vBaseWeight); +alpha = uniforms.vBaseColor.a; +base_metalness = uniforms.vReflectanceInfo.x; +base_diffuse_roughness = uniforms.vBaseDiffuseRoughness; +specular_roughness = uniforms.vReflectanceInfo.y; +specular_color = uniforms.vSpecularColor.rgb; +specular_weight = uniforms.vReflectanceInfo.a; +specular_ior = uniforms.vReflectanceInfo.z; + +// Apply texture values to base layer properties + +#ifdef BASE_COLOR + #if defined(ALPHAFROMALBEDO) || defined(ALPHATEST) + alpha *= baseColorFromTexture.a; + #endif + + #ifdef BASE_COLOR_GAMMA + base_color *= toLinearSpace(baseColorFromTexture.rgb); + #else + base_color *= baseColorFromTexture.rgb; + #endif + + base_color *= uniforms.vBaseColorInfos.y; +#endif + +#ifdef BASE_WEIGHT + base_color *= baseWeightFromTexture.r; +#endif + +// _____________________________ Alpha Information _______________________________ +#ifdef GEOMETRY_OPACITY + alpha *= opacityFromTexture.a; + alpha *= uniforms.vGeometryOpacityInfos.y; +#endif + +#ifdef ALPHATEST + #if DEBUGMODE != 88 + if (alpha < ALPHATESTVALUE) + discard; + #endif + + #ifndef ALPHABLEND + // Prevent to blend with the canvas. + alpha = 1.0; + #endif +#endif + +#ifdef METALLIC_ROUGHNESS + base_metalness *= metallicRoughnessFromTexture.b; + specular_roughness *= metallicRoughnessFromTexture.g; +#endif + +#ifdef BASE_DIFFUSE_ROUGHNESS + base_diffuse_roughness *= baseDiffuseRoughnessFromTexture * uniforms.vBaseDiffuseRoughnessInfos.y; +#endif + +#ifdef SPECULAR_COLOR + specular_color *= specularColorFromTexture.rgb; +#endif + +#ifdef SPECULAR_WEIGHT + // If loaded from a glTF, the specular_weight is stored in the alpha channel. + // Otherwise, it's expected to just be a greyscale texture. + #ifdef SPECULAR_WEIGHT_USE_ALPHA_ONLY + specular_weight *= specularWeightFromTexture.a; + #else + specular_weight *= specularWeightFromTexture.r; + #endif +#endif + +#ifdef DETAIL + let detailRoughness: f32 = mix(0.5f, detailColor.b, vDetailInfos.w); + let loLerp: f32 = mix(0.f, specular_roughness, detailRoughness * 2.f); + let hiLerp: f32 = mix(specular_roughness, 1.f, (detailRoughness - 0.5f) * 2.f); + specular_roughness = mix(loLerp, hiLerp, step(detailRoughness, 0.5f)); +#endif \ No newline at end of file diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrBlockAmbientOcclusion.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrBlockAmbientOcclusion.fx new file mode 100644 index 00000000000..a514b930308 --- /dev/null +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrBlockAmbientOcclusion.fx @@ -0,0 +1,35 @@ +struct ambientOcclusionOutParams +{ + ambientOcclusionColor: vec3f, +#if DEBUGMODE > 0 && defined(AMBIENT_OCCLUSION) + ambientOcclusionColorMap: vec3f +#endif +}; + +#define pbr_inline +fn ambientOcclusionBlock( +#ifdef AMBIENT_OCCLUSION + ambientOcclusionColorMap_: vec3f, + ambientInfos: vec2f +#endif +) -> ambientOcclusionOutParams +{ + var outParams: ambientOcclusionOutParams; + var ambientOcclusionColor: vec3f = vec3f(1., 1., 1.); + + #ifdef AMBIENT_OCCLUSION + var ambientOcclusionColorMap: vec3f = ambientOcclusionColorMap_ * ambientInfos.y; + #ifdef AMBIENTINGRAYSCALE + ambientOcclusionColorMap = vec3f(ambientOcclusionColorMap.r, ambientOcclusionColorMap.r, ambientOcclusionColorMap.r); + #endif + // ambientOcclusionColor = mix(ambientOcclusionColor, ambientOcclusionColorMap, ambientInfos.z); + + #if DEBUGMODE > 0 + outParams.ambientOcclusionColorMap = ambientOcclusionColorMap; + #endif + #endif + + outParams.ambientOcclusionColor = ambientOcclusionColor; + + return outParams; +} diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrBlockNormalFinal.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrBlockNormalFinal.fx new file mode 100644 index 00000000000..534ee45d5f4 --- /dev/null +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrBlockNormalFinal.fx @@ -0,0 +1,19 @@ +#if defined(FORCENORMALFORWARD) && defined(NORMAL) + var faceNormal: vec3f = normalize(cross(dpdx(fragmentInputs.vPositionW), dpdy(fragmentInputs.vPositionW))) * scene.vEyePosition.w; + #if defined(TWOSIDEDLIGHTING) + faceNormal = select(-faceNormal, faceNormal, fragmentInputs.frontFacing); + #endif + + normalW *= sign(dot(normalW, faceNormal)); + coatNormalW *= sign(dot(coatNormalW, faceNormal)); +#endif + +#if defined(TWOSIDEDLIGHTING) && defined(NORMAL) + #if defined(MIRRORED) + normalW = select(normalW, -normalW, fragmentInputs.frontFacing); + coatNormalW = select(coatNormalW, -coatNormalW, fragmentInputs.frontFacing); + #else + normalW = select(-normalW, normalW, fragmentInputs.frontFacing); + coatNormalW = select(-coatNormalW, coatNormalW, fragmentInputs.frontFacing); + #endif +#endif diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrCoatLayerData.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrCoatLayerData.fx new file mode 100644 index 00000000000..f93442838b4 --- /dev/null +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrCoatLayerData.fx @@ -0,0 +1,65 @@ +// This code reads uniforms and samples textures to fill up the coat +// layer properties for OpenPBR + +var coat_weight: f32 = 0.0f; +var coat_color: vec3f = vec3f(1.0f); +var coat_roughness: f32 = 0.0f; +var coat_roughness_anisotropy: f32 = 0.0f; +var coat_ior: f32 = 1.6f; +var coat_darkening: f32 = 1.0f; + +// Sample Coat Layer properties from textures +#ifdef COAT_WEIGHT + var coatWeightFromTexture: vec4f = textureSample(coatWeightSampler, coatWeightSamplerSampler, fragmentInputs.vCoatWeightUV + uvOffset); +#endif + +#ifdef COAT_COLOR + var coatColorFromTexture: vec4f = textureSample(coatColorSampler, coatColorSamplerSampler, fragmentInputs.vCoatColorUV + uvOffset); +#endif + +#ifdef COAT_ROUGHNESS + var coatRoughnessFromTexture: vec4f = textureSample(coatRoughnessSampler, coatRoughnessSamplerSampler, fragmentInputs.vCoatRoughnessUV + uvOffset); +#endif + +#ifdef COAT_ROUGHNESS_ANISOTROPY + var coatRoughnessAnisotropyFromTexture: f32 = textureSample(coatRoughnessAnisotropySampler, coatRoughnessAnisotropySamplerSampler, fragmentInputs.vCoatRoughnessAnisotropyUV + uvOffset).r; +#endif + +#ifdef COAT_DARKENING + var coatDarkeningFromTexture: vec4f = textureSample(coatDarkeningSampler, coatDarkeningSamplerSampler, fragmentInputs.vCoatDarkeningUV + uvOffset); +#endif + +// Initalize coat layer properties from uniforms +coat_color = uniforms.vCoatColor.rgb; +coat_weight = uniforms.vCoatWeight; +coat_roughness = uniforms.vCoatRoughness; +// coat_roughness_anisotropy = uniforms.vCoatRoughnessAnisotropy; +coat_ior = uniforms.vCoatIor; +coat_darkening = uniforms.vCoatDarkening; + +// Apply texture values to coat layer properties +#ifdef COAT_WEIGHT + coat_weight *= coatWeightFromTexture.r; +#endif + +#ifdef COAT_COLOR + #ifdef COAT_COLOR_GAMMA + coat_color *= toLinearSpace(coatColorFromTexture.rgb); + #else + coat_color *= coatColorFromTexture.rgb; + #endif + + coat_color *= uniforms.vCoatColorInfos.y; +#endif + +#ifdef COAT_ROUGHNESS + coat_roughness *= coatRoughnessFromTexture.r; +#endif + +#ifdef COAT_ROUGHNESS_ANISOTROPY + coat_roughness_anisotropy *= coatRoughnessAnisotropyFromTexture; +#endif + +#ifdef COAT_DARKENING + coat_darkening *= coatDarkeningFromTexture.r; +#endif diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrConductorReflectance.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrConductorReflectance.fx new file mode 100644 index 00000000000..2bedc060fb5 --- /dev/null +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrConductorReflectance.fx @@ -0,0 +1,17 @@ + +#define pbr_inline +fn conductorReflectance(baseColor: vec3f, specularColor: vec3f, specularWeight: f32) -> ReflectanceParams +{ + var outParams: ReflectanceParams; + + #if (CONDUCTOR_SPECULAR_MODEL == CONDUCTOR_SPECULAR_MODEL_OPENPBR) + outParams.coloredF0 = baseColor * specularWeight; + outParams.coloredF90 = specularColor * specularWeight; + #else + outParams.coloredF0 = baseColor; + outParams.coloredF90 = vec3f(1.0f); + #endif + outParams.F0 = 1.0f; + outParams.F90 = 1.0f; + return outParams; +} \ No newline at end of file diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrDielectricReflectance.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrDielectricReflectance.fx new file mode 100644 index 00000000000..55812b8895d --- /dev/null +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrDielectricReflectance.fx @@ -0,0 +1,53 @@ +struct ReflectanceParams +{ + F0: f32, + F90: f32, + coloredF0: vec3f, + coloredF90: vec3f, +}; + +#define pbr_inline +fn dielectricReflectance( + insideIOR: f32, outsideIOR: f32, specularColor: vec3f, specularWeight: f32 +) -> ReflectanceParams +{ + var outParams: ReflectanceParams; + + let dielectricF0 = pow((insideIOR - outsideIOR) / (insideIOR + outsideIOR), 2.0); + + // Compute non-coloured reflectance. + // reflectanceF0 is the non-coloured reflectance used for blending between the diffuse and specular components. + // It represents the total percentage of light reflected by the specular lobe at normal incidence. + // In glTF's material model, the F0 value is multiplied by the maximum component of the specular colour. + #if DIELECTRIC_SPECULAR_MODEL == DIELECTRIC_SPECULAR_MODEL_GLTF + let maxF0 = max(specularColor.r, max(specularColor.g, specularColor.b)); + outParams.F0 = dielectricF0 * maxF0 * specularWeight; + #else + outParams.F0 = dielectricF0 * specularWeight; + #endif + + + // Scale the reflectanceF90 by the IOR for values less than 1.5. + // This is an empirical hack to account for the fact that Schlick is tuned for IOR = 1.5 + // and an IOR of 1.0 should result in no visible glancing specular. + let f90Scale = clamp(2.0f * abs(insideIOR - outsideIOR), 0.0f, 1.0f); + outParams.F90 = f90Scale * specularWeight; + + // Compute the coloured F0 reflectance. + // The coloured reflectance is the percentage of light reflected by the specular lobe at normal incidence. + // In glTF and OpenPBR, it is not the same thing as the percentage of light blocked from penetrating + // down to the layer below. The non-coloured F0 will be used for this (see below). + outParams.coloredF0 = vec3f(dielectricF0 * specularWeight) * specularColor.rgb; + + // Now, compute the coloured reflectance at glancing angles based on the specular model. + #if (DIELECTRIC_SPECULAR_MODEL == DIELECTRIC_SPECULAR_MODEL_OPENPBR) + // In OpenPBR, the F90 is coloured using the specular colour for dielectrics. + let dielectricColorF90: vec3f = specularColor.rgb * vec3f(f90Scale) * specularWeight; + #else + // In glTF, the F90 is white for dielectrics. + let dielectricColorF90: vec3f = vec3f(f90Scale) * specularWeight; + #endif + outParams.coloredF90 = dielectricColorF90; + + return outParams; +} diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrDirectLighting.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrDirectLighting.fx new file mode 100644 index 00000000000..7f2b8ec7f4a --- /dev/null +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrDirectLighting.fx @@ -0,0 +1,116 @@ +#ifdef LIGHT{X} +{ + var slab_diffuse: vec3f = vec3f(0.f, 0.f, 0.f); + var slab_subsurface: vec3f = vec3f(0.f, 0.f, 0.f); + var slab_translucent: vec3f = vec3f(0.f, 0.f, 0.f); + var slab_glossy: vec3f = vec3f(0.f, 0.f, 0.f); + var specularFresnel: f32 = 0.0f; + var slab_metal: vec3f = vec3f(0.f, 0.f, 0.f); + var slab_coat: vec3f = vec3f(0.f, 0.f, 0.f); + var coatFresnel: f32 = 0.0f; + var slab_fuzz: vec3f = vec3f(0.f, 0.f, 0.f); + + // Diffuse Lobe + #ifdef HEMILIGHT{X} + slab_diffuse = computeHemisphericDiffuseLighting(preInfo{X}, lightColor{X}.rgb, light{X}.vLightGround); + #elif defined(AREALIGHT{X}) + slab_diffuse = computeAreaDiffuseLighting(preInfo{X}, lightColor{X}.rgb); + #else + slab_diffuse = computeDiffuseLighting(preInfo{X}, lightColor{X}.rgb); + #endif + + #ifdef PROJECTEDLIGHTTEXTURE{X} + slab_diffuse *= computeProjectionTextureDiffuseLighting(projectionLightTexture{X}, textureProjectionMatrix{X}, vPositionW); + #endif + + numLights += 1.0f; + + // Specular Lobe + #if AREALIGHT{X} + slab_glossy = computeAreaSpecularLighting(preInfo{X}, light{X}.vLightSpecular.rgb, baseConductorReflectance.F0, baseConductorReflectance.F90); + #else + { + #ifdef ANISOTROPIC + slab_glossy = computeAnisotropicSpecularLighting(preInfo{X}, viewDirectionW, normalW, + anisotropicOut.anisotropicTangent, anisotropicOut.anisotropicBitangent, anisotropicOut.anisotropy, + vec3f(baseDielectricReflectance.F0), vec3f(baseDielectricReflectance.F90), baseGeoInfo.AARoughnessFactors.x, lightColor{X}.rgb); + #else + slab_glossy = computeSpecularLighting(preInfo{X}, normalW, baseDielectricReflectance.coloredF0, baseDielectricReflectance.coloredF90, specular_roughness, lightColor{X}.rgb); + #endif + + let NdotH: f32 = dot(normalW, preInfo{X}.H); + specularFresnel = fresnelSchlickGGX(NdotH, baseDielectricReflectance.F0, baseDielectricReflectance.F90); + } + #endif + + // Metal Lobe + #if AREALIGHT{X} + slab_metal = computeAreaSpecularLighting(preInfo{X}, light{X}.vLightSpecular.rgb, baseConductorReflectance.F0, baseConductorReflectance.F90); + #else + { + // For OpenPBR, we use the F82 specular model for metallic materials and mix with the + // usual Schlick lobe. + #if (CONDUCTOR_SPECULAR_MODEL == CONDUCTOR_SPECULAR_MODEL_OPENPBR) + let coloredFresnel: vec3f = specular_weight * getF82Specular(preInfo{X}.VdotH, baseConductorReflectance.coloredF0, baseConductorReflectance.coloredF90, specular_roughness); + #else + let coloredFresnel: vec3f = fresnelSchlickGGX(preInfo{X}.VdotH, baseConductorReflectance.coloredF0, baseConductorReflectance.coloredF90); + #endif + + #ifdef ANISOTROPIC + slab_metal = computeAnisotropicSpecularLighting(preInfo{X}, viewDirectionW, normalW, anisotropicOut.anisotropicTangent, anisotropicOut.anisotropicBitangent, anisotropicOut.anisotropy, specularEnvironmentR0, specularEnvironmentR90, baseGeoInfo.AARoughnessFactors.x, lightColor{X}.rgb); + #else + slab_metal = computeSpecularLighting(preInfo{X}, normalW, vec3f(baseConductorReflectance.coloredF0), coloredFresnel, specular_roughness, lightColor{X}.rgb); + #endif + } + #endif + + // Coat Lobe + #if AREALIGHT{X} + slab_coat = computeAreaSpecularLighting(preInfoCoat{X}, light{X}.vLightSpecular.rgb, coatReflectance.F0, coatReflectance.F90); + #else + { + #ifdef ANISOTROPIC + slab_coat = computeAnisotropicSpecularLighting(preInfoCoat{X}, viewDirectionW, coatNormalW, + anisotropicOut.anisotropicTangent, anisotropicOut.anisotropicBitangent, anisotropicOut.anisotropy, + vec3f(coatReflectance.F0), vec3f(coatReflectance.F90), baseGeoInfo.AARoughnessFactors.x, lightColor{X}.rgb); + #else + slab_coat = computeSpecularLighting(preInfoCoat{X}, coatNormalW, vec3f(coatReflectance.F0), vec3f(1.0f), coat_roughness, lightColor{X}.rgb); + #endif + + let NdotH: f32 = dot(coatNormalW, preInfoCoat{X}.H); + coatFresnel = fresnelSchlickGGX(NdotH, coatReflectance.F0, coatReflectance.F90); + } + #endif + + var coatAbsorption = vec3f(1.0f); + if (coat_weight > 0.0) { + // __________ Coat Darkening _____________ + let cosTheta_view: f32 = max(preInfoCoat{X}.NdotV, 0.001f); + let cosTheta_light: f32 = max(preInfoCoat{X}.NdotL, 0.001f); + + // Fresnel reflectance for view direction + let fresnel_view: f32 = coatReflectance.F0 + (1.0f - coatReflectance.F0) * pow(1.0f - cosTheta_view, 5.0); + + // Fresnel reflectance for light direction + let fresnel_light: f32 = coatReflectance.F0 + (1.0f - coatReflectance.F0) * pow(1.0f - cosTheta_light, 5.0); + + // Average reflectance for the round trip (light in, view out) + let averageReflectance: f32 = (fresnel_view + fresnel_light) * 0.5; + + // Calculate transmission through multiple internal reflections + // This uses the geometric series for infinite reflections: + // T = (1-R) / (1 + R + R² + R³ + ...) = (1-R) / (1/(1-R)) = (1-R)² + let effectiveReflectance: f32 = averageReflectance * coat_weight; + let transmission: f32 = (1.0f - effectiveReflectance) / (1.0f + effectiveReflectance); + coatAbsorption = coat_color * mix(1.0f, transmission, coat_darkening); + } + + slab_diffuse *= base_color.rgb; + let material_opaque_base: vec3f = mix(slab_diffuse, slab_subsurface, subsurface_weight); + let material_dielectric_base: vec3f = mix(material_opaque_base, slab_translucent, transmission_weight); + let material_dielectric_gloss: vec3f = layer(material_dielectric_base, slab_glossy, specularFresnel, vec3f(1.0), specular_color); + let material_base_substrate: vec3f = mix(material_dielectric_gloss, slab_metal, base_metalness); + let material_coated_base: vec3f = layer(material_base_substrate, slab_coat, coatFresnel, coatAbsorption, vec3f(1.0)); + material_surface_direct += mix(material_coated_base, slab_fuzz, fuzz_weight); +} +#endif \ No newline at end of file diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrDirectLightingInit.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrDirectLightingInit.fx new file mode 100644 index 00000000000..529ed5b74ac --- /dev/null +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrDirectLightingInit.fx @@ -0,0 +1,93 @@ +#ifdef LIGHT{X} + var preInfo{X}: preLightingInfo; + var preInfoCoat{X}: preLightingInfo; + + let lightColor{X}: vec4f = light{X}.vLightDiffuse; + var shadow{X}: f32 = 1.0f; + #if defined(SHADOWONLY) || defined(LIGHTMAP) && defined(LIGHTMAPEXCLUDED{X}) && defined(LIGHTMAPNOSPECULAR{X}) + //No light calculation + #else + + #define CUSTOM_LIGHT{X}_COLOR // Use to modify light color. Currently only supports diffuse. + + // Compute Pre Lighting infos + #ifdef SPOTLIGHT{X} + preInfo{X} = computePointAndSpotPreLightingInfo(light{X}.vLightData, viewDirectionW, normalW, vPositionW); + preInfoCoat{X} = computePointAndSpotPreLightingInfo(light{X}.vLightData, viewDirectionW, coatNormalW, vPositionW); + #elif defined(POINTLIGHT{X}) + preInfo{X} = computePointAndSpotPreLightingInfo(light{X}.vLightData, viewDirectionW, normalW, vPositionW); + preInfoCoat{X} = computePointAndSpotPreLightingInfo(light{X}.vLightData, viewDirectionW, coatNormalW, vPositionW); + #elif defined(HEMILIGHT{X}) + preInfo{X} = computeHemisphericPreLightingInfo(light{X}.vLightData, viewDirectionW, normalW); + preInfoCoat{X} = computeHemisphericPreLightingInfo(light{X}.vLightData, viewDirectionW, coatNormalW); + #elif defined(DIRLIGHT{X}) + preInfo{X} = computeDirectionalPreLightingInfo(light{X}.vLightData, viewDirectionW, normalW); + preInfoCoat{X} = computeDirectionalPreLightingInfo(light{X}.vLightData, viewDirectionW, coatNormalW); + #elif defined(AREALIGHT{X}) && defined(AREALIGHTSUPPORTED) + preInfo{X} = computeAreaPreLightingInfo(areaLightsLTC1Sampler, areaLightsLTC2Sampler, viewDirectionW, normalW, vPositionW, light{X}.vLightData, light{X}.vLightWidth.xyz, light{X}.vLightHeight.xyz, specular_roughness); + preInfoCoat{X} = computeAreaPreLightingInfo(areaLightsLTC1Sampler, areaLightsLTC2Sampler, viewDirectionW, coatNormalW, vPositionW, light{X}.vLightData, light{X}.vLightWidth.xyz, light{X}.vLightHeight.xyz, coat_roughness); + #endif + + preInfo{X}.NdotV = baseGeoInfo.NdotV; + preInfoCoat{X}.NdotV = coatGeoInfo.NdotV; + + // Compute Attenuation infos + #ifdef SPOTLIGHT{X} + #ifdef LIGHT_FALLOFF_GLTF{X} + preInfo{X}.attenuation = computeDistanceLightFalloff_GLTF(preInfo{X}.lightDistanceSquared, light{X}.vLightFalloff.y); + #ifdef IESLIGHTTEXTURE{X} + preInfo{X}.attenuation *= computeDirectionalLightFalloff_IES(light{X}.vLightDirection.xyz, preInfo{X}.L, iesLightTexture{X}); + #else + preInfo{X}.attenuation *= computeDirectionalLightFalloff_GLTF(light{X}.vLightDirection.xyz, preInfo{X}.L, light{X}.vLightFalloff.z, light{X}.vLightFalloff.w); + #endif + #elif defined(LIGHT_FALLOFF_PHYSICAL{X}) + preInfo{X}.attenuation = computeDistanceLightFalloff_Physical(preInfo{X}.lightDistanceSquared); + #ifdef IESLIGHTTEXTURE{X} + preInfo{X}.attenuation *= computeDirectionalLightFalloff_IES(light{X}.vLightDirection.xyz, preInfo{X}.L, iesLightTexture{X}); + #else + preInfo{X}.attenuation *= computeDirectionalLightFalloff_Physical(light{X}.vLightDirection.xyz, preInfo{X}.L, light{X}.vLightDirection.w); + #endif + #elif defined(LIGHT_FALLOFF_STANDARD{X}) + preInfo{X}.attenuation = computeDistanceLightFalloff_Standard(preInfo{X}.lightOffset, light{X}.vLightFalloff.x); + #ifdef IESLIGHTTEXTURE{X} + preInfo{X}.attenuation *= computeDirectionalLightFalloff_IES(light{X}.vLightDirection.xyz, preInfo{X}.L, iesLightTexture{X}); + #else + preInfo{X}.attenuation *= computeDirectionalLightFalloff_Standard(light{X}.vLightDirection.xyz, preInfo{X}.L, light{X}.vLightDirection.w, light{X}.vLightData.w); + #endif + #else + preInfo{X}.attenuation = computeDistanceLightFalloff(preInfo{X}.lightOffset, preInfo{X}.lightDistanceSquared, light{X}.vLightFalloff.x, light{X}.vLightFalloff.y); + #ifdef IESLIGHTTEXTURE{X} + preInfo{X}.attenuation *= computeDirectionalLightFalloff_IES(light{X}.vLightDirection.xyz, preInfo{X}.L, iesLightTexture{X}); + #else + preInfo{X}.attenuation *= computeDirectionalLightFalloff(light{X}.vLightDirection.xyz, preInfo{X}.L, light{X}.vLightDirection.w, light{X}.vLightData.w, light{X}.vLightFalloff.z, light{X}.vLightFalloff.w); + #endif + #endif + #elif defined(POINTLIGHT{X}) + #ifdef LIGHT_FALLOFF_GLTF{X} + preInfo{X}.attenuation = computeDistanceLightFalloff_GLTF(preInfo{X}.lightDistanceSquared, light{X}.vLightFalloff.y); + #elif defined(LIGHT_FALLOFF_PHYSICAL{X}) + preInfo{X}.attenuation = computeDistanceLightFalloff_Physical(preInfo{X}.lightDistanceSquared); + #elif defined(LIGHT_FALLOFF_STANDARD{X}) + preInfo{X}.attenuation = computeDistanceLightFalloff_Standard(preInfo{X}.lightOffset, light{X}.vLightFalloff.x); + #else + preInfo{X}.attenuation = computeDistanceLightFalloff(preInfo{X}.lightOffset, preInfo{X}.lightDistanceSquared, light{X}.vLightFalloff.x, light{X}.vLightFalloff.y); + #endif + #else + preInfo{X}.attenuation = 1.0f; + #endif + + preInfoCoat{X}.attenuation = preInfo{X}.attenuation; + + // Simulates Light radius for diffuse and spec term + // clear coat is using a dedicated roughness + #if defined(HEMILIGHT{X}) || defined(AREALIGHT{X}) + preInfo{X}.roughness = specular_roughness; + preInfoCoat{X}.roughness = coat_roughness; + #else + preInfo{X}.roughness = adjustRoughnessFromLightProperties(specular_roughness, light{X}.vLightSpecular.a, preInfo{X}.lightDistance); + preInfoCoat{X}.roughness = adjustRoughnessFromLightProperties(coat_roughness, light{X}.vLightSpecular.a, preInfoCoat{X}.lightDistance); + #endif + preInfo{X}.diffuseRoughness = base_diffuse_roughness; + preInfo{X}.surfaceAlbedo = base_color.rgb; + #endif +#endif \ No newline at end of file diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrEnvironmentLighting.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrEnvironmentLighting.fx new file mode 100644 index 00000000000..b912f3c7a6c --- /dev/null +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrEnvironmentLighting.fx @@ -0,0 +1,149 @@ +// _____________________________ Base Diffuse Layer IBL _______________________________________ +#ifdef REFLECTION + // Pass in a vector to sample teh irradiance with (to handle reflection or ) + var baseDiffuseEnvironmentLight: vec3f = sampleIrradiance( + normalW + #if defined(NORMAL) && defined(USESPHERICALINVERTEX) + , vEnvironmentIrradiance //SH + #endif + #if (defined(USESPHERICALFROMREFLECTIONMAP) && (!defined(NORMAL) || !defined(USESPHERICALINVERTEX))) || (defined(USEIRRADIANCEMAP) && defined(REFLECTIONMAP_3D)) + , uniforms.reflectionMatrix + #endif + #ifdef USEIRRADIANCEMAP + , irradianceSampler + , irradianceSamplerSampler + #ifdef USE_IRRADIANCE_DOMINANT_DIRECTION + , uniforms.vReflectionDominantDirection + #endif + #endif + #ifdef REALTIME_FILTERING + , uniforms.vReflectionFilteringInfo + #ifdef IBL_CDF_FILTERING + , icdfSampler + , icdfSamplerSampler + #endif + #endif + , uniforms.vReflectionInfos + , viewDirectionW + , base_diffuse_roughness + , base_color + ); + + // _____________________________ Base Specular Layer IBL _______________________________________ + + #ifdef REFLECTIONMAP_3D + var reflectionCoords: vec3f = vec3f(0.f, 0.f, 0.f); + #else + var reflectionCoords: vec2f = vec2f(0.f, 0.f); + #endif + reflectionCoords = createReflectionCoords(fragmentInputs.vPositionW, normalW); + let specularAlphaG: f32 = specular_roughness * specular_roughness; + var baseSpecularEnvironmentLight: vec3f = sampleRadiance(specularAlphaG, uniforms.vReflectionMicrosurfaceInfos.rgb, uniforms.vReflectionInfos + #if defined(LODINREFLECTIONALPHA) && !defined(REFLECTIONMAP_SKYBOX) + , baseGeoInfo.NdotVUnclamped + #endif + , reflectionSampler + , reflectionSamplerSampler + , reflectionCoords + #ifdef REALTIME_FILTERING + , uniforms.vReflectionFilteringInfo + #endif + ); + + baseSpecularEnvironmentLight = mix(baseSpecularEnvironmentLight.rgb, baseDiffuseEnvironmentLight, specularAlphaG); + + var coatEnvironmentLight: vec3f = vec3f(0.f, 0.f, 0.f); + if (coat_weight > 0.0) { + #ifdef REFLECTIONMAP_3D + var reflectionCoords: vec3f = vec3f(0.f, 0.f, 0.f); + #else + var reflectionCoords: vec2f = vec2f(0.f, 0.f); + #endif + reflectionCoords = createReflectionCoords(fragmentInputs.vPositionW, coatNormalW); + var coatAlphaG: f32 = coat_roughness * coat_roughness; + coatEnvironmentLight = sampleRadiance(coatAlphaG, uniforms.vReflectionMicrosurfaceInfos.rgb, uniforms.vReflectionInfos + #if defined(LODINREFLECTIONALPHA) && !defined(REFLECTIONMAP_SKYBOX) + , coatGeoInfo.NdotVUnclamped + #endif + , reflectionSampler + , reflectionSamplerSampler + , reflectionCoords + #ifdef REALTIME_FILTERING + , uniforms.vReflectionFilteringInfo + #endif + ); + } + + // ______________________________ IBL Fresnel Reflectance ____________________________ + + // Dielectric IBL Fresnel + // The colored fresnel represents the % of light reflected by the base specular lobe + // The non-colored fresnel represents the % of light that doesn't penetrate through + // the base specular lobe. i.e. the specular lobe isn't energy conserving for coloured specular. + let dielectricIblFresnel: f32 = getReflectanceFromBRDFWithEnvLookup(vec3f(baseDielectricReflectance.F0), vec3f(baseDielectricReflectance.F90), baseGeoInfo.environmentBrdf).r; + let dielectricIblColoredFresnel: vec3f = getReflectanceFromBRDFWithEnvLookup(baseDielectricReflectance.coloredF0, baseDielectricReflectance.coloredF90, baseGeoInfo.environmentBrdf); + + // Conductor IBL Fresnel + let conductorIblFresnel: vec3f = conductorIblFresnel(baseConductorReflectance, baseGeoInfo.NdotV, specular_roughness, baseGeoInfo.environmentBrdf); + + // Coat IBL Fresnel + var coatIblFresnel: f32 = 0.0; + if (coat_weight > 0.0) { + coatIblFresnel = getReflectanceFromBRDFWithEnvLookup(vec3f(coatReflectance.F0), vec3f(coatReflectance.F90), coatGeoInfo.environmentBrdf).r; + } + + + var slab_diffuse_ibl: vec3f = vec3f(0., 0., 0.); + var slab_glossy_ibl: vec3f = vec3f(0., 0., 0.); + var slab_metal_ibl: vec3f = vec3f(0., 0., 0.); + var slab_coat_ibl: vec3f = vec3f(0., 0., 0.); + + slab_diffuse_ibl = baseDiffuseEnvironmentLight * uniforms.vLightingIntensity.z; + slab_diffuse_ibl *= aoOut.ambientOcclusionColor; + + // Add the specular environment light + slab_glossy_ibl = baseSpecularEnvironmentLight * uniforms.vLightingIntensity.z; + + // _____________________________ Metal Layer IBL ____________________________ + slab_metal_ibl = baseSpecularEnvironmentLight * conductorIblFresnel * uniforms.vLightingIntensity.z; + + // _____________________________ Coat Layer IBL _____________________________ + var coatAbsorption = vec3f(1.0); + if (coat_weight > 0.0) { + slab_coat_ibl = coatEnvironmentLight * uniforms.vLightingIntensity.z; + + // __________ Coat Darkening _____________ + // Hemisphere-averaged Fresnel (empirical approximation) + let hemisphere_avg_fresnel: f32 = coatReflectance.F0 + 0.5f * (1.0f - coatReflectance.F0); + var averageReflectance = (coatIblFresnel + hemisphere_avg_fresnel) * 0.5f; + + // Account for roughness - rougher surfaces have more diffuse internal reflections + // This reduces the darkening effect as roughness increases + let roughnessFactor = 1.0f - coat_roughness * 0.5f; + averageReflectance *= roughnessFactor; + + // Calculate transmission through multiple internal reflections + // This uses the geometric series for infinite reflections: + // T = (1-R) / (1 + R + R² + R³ + ...) = (1-R) / (1/(1-R)) = (1-R)² + let effectiveReflectance = averageReflectance * coat_weight; + let transmission = (1.0f - effectiveReflectance) * (1.0f - effectiveReflectance); + coatAbsorption = coat_color * mix(1.0f, transmission, coat_darkening); + } + + // TEMP + var slab_subsurface_ibl: vec3f = vec3f(0., 0., 0.); + var slab_translucent_base_ibl: vec3f = vec3f(0., 0., 0.); + var slab_fuzz_ibl: vec3f = vec3f(0., 0., 0.); + + slab_diffuse_ibl *= base_color.rgb; + + // _____________________________ IBL Material Layer Composition ______________________________________ + #define CUSTOM_FRAGMENT_BEFORE_IBLLAYERCOMPOSITION + let material_opaque_base_ibl: vec3f = mix(slab_diffuse_ibl, slab_subsurface_ibl, subsurface_weight); + let material_dielectric_base_ibl: vec3f = mix(material_opaque_base_ibl, slab_translucent_base_ibl, transmission_weight); + let material_dielectric_gloss_ibl: vec3f = layer(material_dielectric_base_ibl, slab_glossy_ibl, dielectricIblFresnel, vec3f(1.0f), specular_color); + let material_base_substrate_ibl: vec3f = mix(material_dielectric_gloss_ibl, slab_metal_ibl, base_metalness); + let material_coated_base_ibl: vec3f = layer(material_base_substrate_ibl, slab_coat_ibl, coatIblFresnel, coatAbsorption, vec3f(1.0f)); + material_surface_ibl = mix(material_coated_base_ibl, slab_fuzz_ibl, fuzz_weight); + +#endif \ No newline at end of file diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrFragmentSamplersDeclaration.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrFragmentSamplersDeclaration.fx new file mode 100644 index 00000000000..b2d3a715bef --- /dev/null +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrFragmentSamplersDeclaration.fx @@ -0,0 +1,72 @@ +#include(_DEFINENAME_,BASE_COLOR,_VARYINGNAME_,BaseColor,_SAMPLERNAME_,baseColor) +#include(_DEFINENAME_,BASE_WEIGHT,_VARYINGNAME_,BaseWeight,_SAMPLERNAME_,baseWeight) +#include(_DEFINENAME_,BASE_DIFFUSE_ROUGHNESS,_VARYINGNAME_,BaseDiffuseRoughness,_SAMPLERNAME_,baseDiffuseRoughness) +#include(_DEFINENAME_,METALLIC_ROUGHNESS,_VARYINGNAME_,BaseMetalRough,_SAMPLERNAME_,baseMetalRough) +#include(_DEFINENAME_,SPECULAR_WEIGHT,_VARYINGNAME_,SpecularWeight,_SAMPLERNAME_,specularWeight) +#include(_DEFINENAME_,SPECULAR_COLOR,_VARYINGNAME_,SpecularColor,_SAMPLERNAME_,specularColor) +#include(_DEFINENAME_,COAT_WEIGHT,_VARYINGNAME_,CoatWeight,_SAMPLERNAME_,coatWeight) +#include(_DEFINENAME_,COAT_COLOR,_VARYINGNAME_,CoatColor,_SAMPLERNAME_,coatColor) +#include(_DEFINENAME_,COAT_ROUGHNESS,_VARYINGNAME_,CoatRoughness,_SAMPLERNAME_,coatRoughness) +// #include(_DEFINENAME_,COAT_ROUGHNESS_ANISOTROPY,_VARYINGNAME_,CoatRoughnessAnisotropy,_SAMPLERNAME_,coatRoughnessAnisotropy) +#include (_DEFINENAME_,COAT_DARKENING,_VARYINGNAME_,CoatDarkening,_SAMPLERNAME_,coatDarkening) +#include(_DEFINENAME_,GEOMETRY_OPACITY,_VARYINGNAME_,GeometryOpacity,_SAMPLERNAME_,geometryOpacity) +#include(_DEFINENAME_,EMISSION_COLOR,_VARYINGNAME_,EmissionColor,_SAMPLERNAME_,emissionColor) + +#include(_DEFINENAME_,AMBIENT_OCCLUSION,_VARYINGNAME_,AmbientOcclusion,_SAMPLERNAME_,ambientOcclusion) +#include(_DEFINENAME_,DECAL,_VARYINGNAME_,Decal,_SAMPLERNAME_,decal) + +// Reflection +#ifdef REFLECTION + #ifdef REFLECTIONMAP_3D + var reflectionSamplerSampler: sampler; + var reflectionSampler: texture_cube; + + #ifdef LODBASEDMICROSFURACE + #else + var reflectionLowSamplerSampler: sampler; + var reflectionLowSampler: texture_cube; + var reflectionHighSamplerSampler: sampler; + var reflectionHighSampler: texture_cube; + #endif + + #ifdef USEIRRADIANCEMAP + var irradianceSamplerSampler: sampler; + var irradianceSampler: texture_cube; + #endif + #else + + var reflectionSamplerSampler: sampler; + var reflectionSampler: texture_2d; + + #ifdef LODBASEDMICROSFURACE + #else + var reflectionLowSamplerSampler: sampler; + var reflectionLowSampler: texture_2d; + var reflectionHighSamplerSampler: sampler; + var reflectionHighSampler: texture_2d; + #endif + + #ifdef USEIRRADIANCEMAP + var irradianceSamplerSampler: sampler; + var irradianceSampler: texture_2d; + #endif + #endif + + #ifdef REFLECTIONMAP_SKYBOX + varying vPositionUVW: vec3f; + #else + #if defined(REFLECTIONMAP_EQUIRECTANGULAR_FIXED) || defined(REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED) + varying vDirectionW: vec3f; + #endif + #endif +#endif + +#ifdef ENVIRONMENTBRDF + var environmentBrdfSamplerSampler: sampler; + var environmentBrdfSampler: texture_2d; +#endif + +#ifdef IBL_CDF_FILTERING + var icdfSamplerSampler: sampler; + var icdfSampler: texture_2d; +#endif \ No newline at end of file diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrGeometryInfo.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrGeometryInfo.fx new file mode 100644 index 00000000000..3a778ec483c --- /dev/null +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrGeometryInfo.fx @@ -0,0 +1,37 @@ +struct geometryInfoOutParams +{ + NdotV: f32, + NdotVUnclamped: f32, + environmentBrdf: vec3f, + horizonOcclusion: f32, +}; + +#define pbr_inline +fn geometryInfo( + normalW: vec3f, viewDirectionW: vec3f, roughness: f32, geometricNormalW: vec3f +) -> geometryInfoOutParams +{ + var outParams: geometryInfoOutParams; + outParams.NdotVUnclamped = dot(normalW, viewDirectionW); + // The order 1886 page 3. + outParams.NdotV = absEps(outParams.NdotVUnclamped); + + #if defined(ENVIRONMENTBRDF) + // BRDF Lookup + outParams.environmentBrdf = getBRDFLookup(outParams.NdotV, roughness); + #else + outParams.environmentBrdf = vec3f(0.0); + #endif + + outParams.horizonOcclusion = 1.0f; + #if defined(ENVIRONMENTBRDF) && !defined(REFLECTIONMAP_SKYBOX) + #ifdef HORIZONOCCLUSION + #if defined(GEOMETRY_NORMAL) || defined(GEOMETRY_COAT_NORMAL) + #ifdef REFLECTIONMAP_3D + outParams.horizonOcclusion = environmentHorizonOcclusion(-viewDirectionW, normalW, geometricNormalW); + #endif + #endif + #endif + #endif + return outParams; +} \ No newline at end of file diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrIblFunctions.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrIblFunctions.fx new file mode 100644 index 00000000000..3eb5c1b61f8 --- /dev/null +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrIblFunctions.fx @@ -0,0 +1,207 @@ +#ifdef REFLECTION + // _____________________________ Irradiance ________________________________ + // surfaceNormal is the direction to sample. Pass in a refracted vector to sample + // diffusely refracted light. + fn sampleIrradiance( + surfaceNormal: vec3f + #if defined(NORMAL) && defined(USESPHERICALINVERTEX) + , vEnvironmentIrradianceSH: vec3f + #endif + #if (defined(USESPHERICALFROMREFLECTIONMAP) && (!defined(NORMAL) || !defined(USESPHERICALINVERTEX))) || (defined(USEIRRADIANCEMAP) && defined(REFLECTIONMAP_3D)) + , iblMatrix: mat4x4f + #endif + #ifdef USEIRRADIANCEMAP + #ifdef REFLECTIONMAP_3D + , irradianceSampler: texture_cube + , irradianceSamplerSampler: sampler + #else + , irradianceSampler: texture_2d + , irradianceSamplerSampler: sampler + #endif + #ifdef USE_IRRADIANCE_DOMINANT_DIRECTION + , reflectionDominantDirection: vec3f + #endif + #endif + #ifdef REALTIME_FILTERING + , reflectionFilteringInfo: vec2f + #ifdef IBL_CDF_FILTERING + , icdfSampler: texture_2d + , icdfSamplerSampler: sampler + #endif + #endif + , reflectionInfos: vec2f + , viewDirectionW: vec3f + , diffuseRoughness: f32 + , surfaceAlbedo: vec3f + ) -> vec3f { + var environmentIrradiance = vec3f(0., 0., 0.); + + #if (defined(USESPHERICALFROMREFLECTIONMAP) && (!defined(NORMAL) || !defined(USESPHERICALINVERTEX))) || (defined(USEIRRADIANCEMAP) && defined(REFLECTIONMAP_3D)) + var irradianceVector = (iblMatrix * vec4f(surfaceNormal, 0.0f)).xyz; + let irradianceView = (iblMatrix * vec4f(viewDirectionW, 0.0f)).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 && BASE_DIFFUSE_MODEL != BRDF_DIFFUSE_MODEL_LEGACY + { + let NdotV = max(dot(surfaceNormal, viewDirectionW), 0.0f); + irradianceVector = mix(irradianceVector, irradianceView, (0.5f * (1.0f - NdotV)) * diffuseRoughness); + } + #endif + #endif + + #ifdef REFLECTIONMAP_OPPOSITEZ + irradianceVector.z *= -1.0f; + #endif + + #ifdef INVERTCUBICMAP + irradianceVector.y *= -1.0f; + #endif + #endif + #ifdef USESPHERICALFROMREFLECTIONMAP + #if defined(NORMAL) && defined(USESPHERICALINVERTEX) + environmentIrradiance = vEnvironmentIrradianceSH; + #else + #if defined(REALTIME_FILTERING) + environmentIrradiance = irradiance(reflectionSampler, reflectionSamplerSampler, irradianceVector, reflectionFilteringInfo, diffuseRoughness, surfaceAlbedo, irradianceView + #ifdef IBL_CDF_FILTERING + , icdfSampler + , icdfSamplerSampler + #endif + ); + #else + environmentIrradiance = computeEnvironmentIrradiance(irradianceVector); + #endif + #endif + #elif defined(USEIRRADIANCEMAP) + #ifdef REFLECTIONMAP_3D + let environmentIrradianceFromTexture: vec4f = textureSample(irradianceSampler, irradianceSamplerSampler, irradianceVector); + #else + // TODO: What kind of irradiance map isn't 3D? + let environmentIrradianceFromTexture: vec4f = textureSample(irradianceSampler, irradianceSamplerSampler, reflectionCoords); + #endif + + environmentIrradiance = environmentIrradianceFromTexture.rgb; + #ifdef RGBDREFLECTION + environmentIrradiance.rgb = fromRGBD(environmentIrradianceFromTexture); + #endif + + #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 + let Ls: vec3f = normalize(reflectionDominantDirection); + let NoL: f32 = dot(irradianceVector, Ls); + let NoV: f32 = dot(irradianceVector, irradianceView); + + var diffuseRoughnessTerm = vec3f(1.0f); + #if BASE_DIFFUSE_MODEL == BRDF_DIFFUSE_MODEL_EON + let LoV: f32 = dot (Ls, irradianceView); + let mag: f32 = length(reflectionDominantDirection) * 2.0f; + let clampedAlbedo: vec3f = clamp(surfaceAlbedo, vec3f(0.1f), vec3f(1.0f)); + diffuseRoughnessTerm = diffuseBRDF_EON(clampedAlbedo, diffuseRoughness, NoL, NoV, LoV) * PI; + diffuseRoughnessTerm = diffuseRoughnessTerm / clampedAlbedo; + diffuseRoughnessTerm = mix(vec3f(1.0f), diffuseRoughnessTerm, sqrt(clamp(mag * NoV, 0.0f, 1.0f))); + #elif BASE_DIFFUSE_MODEL == BRDF_DIFFUSE_MODEL_BURLEY + let H: vec3f = (irradianceView + Ls) * 0.5f; + let VoH: f32 = dot(irradianceView, H); + diffuseRoughnessTerm = vec3f(diffuseBRDF_Burley(NoL, NoV, VoH, diffuseRoughness) * PI); + #endif + environmentIrradiance = environmentIrradiance.rgb * diffuseRoughnessTerm; + #endif + #endif + + environmentIrradiance *= reflectionInfos.x; + return environmentIrradiance; + } + + #ifdef REFLECTIONMAP_3D + fn createReflectionCoords(vPositionW: vec3f, normalW: vec3f) -> vec3f + #else + fn createReflectionCoords(vPositionW: vec3f, normalW: vec3f) -> vec2f + #endif + { + var reflectionVector: vec3f = computeReflectionCoords(vec4f(vPositionW, 1.0f), normalW); + + #ifdef REFLECTIONMAP_OPPOSITEZ + reflectionVector.z *= -1.0; + #endif + + // _____________________________ 2D vs 3D Maps ________________________________ + #ifdef REFLECTIONMAP_3D + var reflectionCoords: vec3f = reflectionVector; + #else + var reflectionCoords: vec2f = reflectionVector.xy; + #ifdef REFLECTIONMAP_PROJECTION + reflectionCoords /= reflectionVector.z; + #endif + reflectionCoords.y = 1.0f - reflectionCoords.y; + #endif + return reflectionCoords; + } + + fn sampleRadiance( + alphaG: f32 + , reflectionMicrosurfaceInfos: vec3f + , reflectionInfos: vec2f + #if defined(LODINREFLECTIONALPHA) && !defined(REFLECTIONMAP_SKYBOX) + , NdotVUnclamped: f32 + #endif + #ifdef REFLECTIONMAP_3D + , reflectionSampler: texture_cube + , reflectionSamplerSampler: sampler + , reflectionCoords: vec3f + #else + , reflectionSampler: texture_2d + , reflectionSamplerSampler: sampler + , reflectionCoords: vec2f + #endif + #ifdef REALTIME_FILTERING + , reflectionFilteringInfo: vec2f + #endif + ) -> vec3f { + var environmentRadiance: vec4f = vec4f(0.f, 0.f, 0.f, 0.f); + // _____________________________ 2D vs 3D Maps ________________________________ + #if defined(LODINREFLECTIONALPHA) && !defined(REFLECTIONMAP_SKYBOX) + var reflectionLOD: f32 = getLodFromAlphaG(reflectionMicrosurfaceInfos.x, alphaG, NdotVUnclamped); + #elif defined(LINEARSPECULARREFLECTION) + var reflectionLOD: f32 = getLinearLodFromRoughness(reflectionMicrosurfaceInfos.x, roughness); + #else + var reflectionLOD: f32 = getLodFromAlphaG(reflectionMicrosurfaceInfos.x, alphaG); + #endif + + // Apply environment convolution scale/offset filter tuning parameters to the mipmap LOD selection + reflectionLOD = reflectionLOD * reflectionMicrosurfaceInfos.y + reflectionMicrosurfaceInfos.z; + + #ifdef REALTIME_FILTERING + environmentRadiance = vec4f(radiance(alphaG, reflectionSampler, reflectionSamplerSampler, reflectionCoords, reflectionFilteringInfo), 1.0f); + #else + environmentRadiance = textureSampleLevel(reflectionSampler, reflectionSamplerSampler, reflectionCoords, reflectionLOD); + #endif + + #ifdef RGBDREFLECTION + environmentRadiance.rgb = fromRGBD(environmentRadiance); + #endif + + #ifdef GAMMAREFLECTION + environmentRadiance.rgb = toLinearSpace(environmentRadiance.rgb); + #endif + + // _____________________________ Levels _____________________________________ + // environmentRadiance.rgb *= reflectionInfos.xxx; + return environmentRadiance.rgb; + } + + fn conductorIblFresnel(reflectance: ReflectanceParams, NdotV: f32, roughness: f32, environmentBrdf: vec3f) -> vec3f + { + #if (CONDUCTOR_SPECULAR_MODEL == CONDUCTOR_SPECULAR_MODEL_OPENPBR) + // This is an empirical hack to modify the F0 albedo based on roughness. It's not based on any paper + // or anything. Just trying to match results of rough metals in a pathtracer. + let albedoF0: vec3f = mix(reflectance.coloredF0, pow(reflectance.coloredF0, vec3f(1.4f)), roughness); + return getF82Specular(NdotV, albedoF0, reflectance.coloredF90, roughness); + #else + return getReflectanceFromBRDFLookup(reflectance.coloredF0, reflectance.coloredF90, environmentBrdf); + #endif + } +#endif diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrNormalMapFragment.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrNormalMapFragment.fx new file mode 100644 index 00000000000..df58b0dcbc6 --- /dev/null +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrNormalMapFragment.fx @@ -0,0 +1,77 @@ +var uvOffset: vec2f = vec2f(0.0, 0.0); + +#if defined(GEOMETRY_NORMAL) || defined(PARALLAX) || defined(DETAIL) + #ifdef NORMALXYSCALE + var normalScale: f32 = 1.0; + #elif defined(GEOMETRY_NORMAL) + var normalScale: f32 = uniforms.vGeometryNormalInfos.y; + #else + var normalScale: f32 = 1.0; + #endif + + #if defined(TANGENT) && defined(NORMAL) + var TBN: mat3x3f = mat3x3(input.vTBN0, input.vTBN1, input.vTBN2); + #elif defined(GEOMETRY_NORMAL) + // flip the uv for the backface + var TBNUV: vec2f = select(-fragmentInputs.vGeometryNormalUV, fragmentInputs.vGeometryNormalUV, fragmentInputs.frontFacing); + var TBN: mat3x3f = cotangent_frame(normalW * normalScale, input.vPositionW, TBNUV, uniforms.vTangentSpaceParams); + #else + // flip the uv for the backface + var TBNUV: vec2f = select(-fragmentInputs.vDetailUV, fragmentInputs.vDetailUV, fragmentInputs.frontFacing); + var TBN: mat3x3f = cotangent_frame(normalW * normalScale, input.vPositionW, TBNUV, vec2f(1., 1.)); + #endif +#elif defined(ANISOTROPIC) + #if defined(TANGENT) && defined(NORMAL) + var TBN: mat3x3f = mat3x3(input.vTBN0, input.vTBN1, input.vTBN2); + #else + // flip the uv for the backface + var TBNUV: vec2f = select( -fragmentInputs.vMainUV1, fragmentInputs.vMainUV1, fragmentInputs.frontFacing); + var TBN: mat3x3f = cotangent_frame(normalW, input.vPositionW, TBNUV, vec2f(1., 1.)); + #endif +#endif + +#ifdef PARALLAX + var invTBN: mat3x3f = transposeMat3(TBN); + + #ifdef PARALLAXOCCLUSION + // TODO: Implement parallax occlusion scale + // uvOffset = parallaxOcclusion(invTBN * -viewDirectionW, invTBN * normalW, fragmentInputs.vGeometryNormalUV, uniforms.vGeometryNormalInfos.z); + #else + // uvOffset = parallaxOffset(invTBN * viewDirectionW, uniforms.vGeometryNormalInfos.z); + #endif +#endif + +#ifdef DETAIL + var detailColor: vec4f = textureSample(detailSampler, detailSamplerSampler, fragmentInputs.vDetailUV + uvOffset); + var detailNormalRG: vec2f = detailColor.wy * 2.0 - 1.0; + var detailNormalB: f32 = sqrt(1. - saturate(dot(detailNormalRG, detailNormalRG))); + var detailNormal: vec3f = vec3f(detailNormalRG, detailNormalB); +#endif + +#ifdef GEOMETRY_NORMAL + #ifdef OBJECTSPACE_NORMALMAP + + #define CUSTOM_FRAGMENT_BUMP_FRAGMENT + + normalW = normalize(textureSample(geometryNormalSampler, geometryNormalSamplerSampler, fragmentInputs.vGeometryNormalUV).xyz * 2.0 - 1.0); + normalW = normalize(mat3x3f(uniforms.normalMatrix[0].xyz, uniforms.normalMatrix[1].xyz, uniforms.normalMatrix[2].xyz) * normalW); + #elif !defined(DETAIL) + normalW = perturbNormal(TBN, textureSample(geometryNormalSampler, geometryNormalSamplerSampler, fragmentInputs.vGeometryNormalUV + uvOffset).xyz, uniforms.vGeometryNormalInfos.y); + #else + var sampledNormal: vec3f = textureSample(geometryNormalSampler, geometryNormalSamplerSampler, fragmentInputs.vGeometryNormalUV + uvOffset).xyz * 2.0 - 1.0; + // Reference for normal blending: https://blog.selfshadow.com/publications/blending-in-detail/ + #if DETAIL_NORMALBLENDMETHOD == 0 // whiteout + detailNormal = vec3f(detailNormal.xy * uniforms.vDetailInfos.z, detailNormal.z); + var blendedNormal: vec3f = normalize( vec3f(sampledNormal.xy + detailNormal.xy, sampledNormal.z * detailNormal.z)); + #elif DETAIL_NORMALBLENDMETHOD == 1 // RNM + detailNormal = vec3f(detailNormal.xy * uniforms.vDetailInfos.z, detailNormal.z); + sampledNormal += vec3f(0.0, 0.0, 1.0); + detailNormal *= vec3f(-1.0, -1.0, 1.0); + var blendedNormal: vec3f = sampledNormal * dot(sampledNormal, detailNormal) / sampledNormal.z - detailNormal; + #endif + normalW = perturbNormalBase(TBN, blendedNormal, uniforms.vGeometryNormalInfos.y); + #endif +#elif defined(DETAIL) + detailNormal = vec3f(detailNormal.xy * uniforms.vDetailInfos.z, detailNormal.z); + normalW = perturbNormalBase(TBN, detailNormal, uniforms.vDetailInfos.z); +#endif diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrNormalMapFragmentFunctions.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrNormalMapFragmentFunctions.fx new file mode 100644 index 00000000000..22aaa1a5e60 --- /dev/null +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrNormalMapFragmentFunctions.fx @@ -0,0 +1,83 @@ +#if defined(GEOMETRY_NORMAL) + #include(_DEFINENAME_,GEOMETRY_NORMAL,_VARYINGNAME_,GeometryNormal,_SAMPLERNAME_,geometryNormal) +#endif + +#if defined(GEOMETRY_COAT_NORMAL) + #include(_DEFINENAME_,GEOMETRY_COAT_NORMAL,_VARYINGNAME_,GeometryCoatNormal,_SAMPLERNAME_,geometryCoatNormal) +#endif + +#if defined(DETAIL) + #include(_DEFINENAME_,DETAIL,_VARYINGNAME_,Detail,_SAMPLERNAME_,detail) +#endif + +#if defined(GEOMETRY_NORMAL) && defined(PARALLAX) + const minSamples: f32 = 4.; + const maxSamples: f32 = 15.; + const iMaxSamples: i32 = 15; + + // http://www.gamedev.net/page/resources/_/technical/graphics-programming-and-theory/a-closer-look-at-parallax-occlusion-mapping-r3262 + fn parallaxOcclusion(vViewDirCoT: vec3f, vNormalCoT: vec3f, texCoord: vec2f, parallaxScale: f32) -> vec2f { + + var parallaxLimit: f32 = length(vViewDirCoT.xy) / vViewDirCoT.z; + parallaxLimit *= parallaxScale; + var vOffsetDir: vec2f = normalize(vViewDirCoT.xy); + var vMaxOffset: vec2f = vOffsetDir * parallaxLimit; + var numSamples: f32 = maxSamples + (dot(vViewDirCoT, vNormalCoT) * (minSamples - maxSamples)); + var stepSize: f32 = 1.0 / numSamples; + + // Initialize the starting view ray height and the texture offsets. + var currRayHeight: f32 = 1.0; + var vCurrOffset: vec2f = vec2f(0, 0); + var vLastOffset: vec2f = vec2f(0, 0); + + var lastSampledHeight: f32 = 1.0; + var currSampledHeight: f32 = 1.0; + + var keepWorking: bool = true; + for (var i: i32 = 0; i < iMaxSamples; i++) + { + currSampledHeight = textureSample(geometryNormalSampler, geometryNormalSamplerSampler, texCoord + vCurrOffset).w; + + // Test if the view ray has intersected the surface. + if (!keepWorking) + { + // do nothing + } + else if (currSampledHeight > currRayHeight) + { + var delta1: f32 = currSampledHeight - currRayHeight; + var delta2: f32 = (currRayHeight + stepSize) - lastSampledHeight; + var ratio: f32 = delta1 / (delta1 + delta2); + vCurrOffset = (ratio)* vLastOffset + (1.0 - ratio) * vCurrOffset; + + keepWorking = false; + } + else + { + currRayHeight -= stepSize; + vLastOffset = vCurrOffset; + #ifdef PARALLAX_RHS + vCurrOffset -= stepSize * vMaxOffset; + #else + vCurrOffset += stepSize * vMaxOffset; + #endif + + lastSampledHeight = currSampledHeight; + } + } + + return vCurrOffset; + } + + fn parallaxOffset(viewDir: vec3f, heightScale: f32) -> vec2f + { + // calculate amount of offset for Parallax Mapping With Offset Limiting + var height: f32 = textureSample(geometryNormalSampler, geometryNormalSamplerSampler, fragmentInputs.vGeometryNormalUV).w; + var texCoordOffset: vec2f = heightScale * viewDir.xy * height; + #ifdef PARALLAX_RHS + return texCoordOffset; + #else + return -texCoordOffset; + #endif + } +#endif diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrNormalMapFragmentMainFunctions.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrNormalMapFragmentMainFunctions.fx new file mode 100644 index 00000000000..6036dc2be86 --- /dev/null +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrNormalMapFragmentMainFunctions.fx @@ -0,0 +1,109 @@ +#if defined(GEOMETRY_NORMAL) || defined(CLEARCOAT_BUMP) || defined(ANISOTROPIC) || defined(DETAIL) + #if defined(TANGENT) && defined(NORMAL) + varying vTBN0: vec3f; + varying vTBN1: vec3f; + varying vTBN2: vec3f; + #endif + + #ifdef OBJECTSPACE_NORMALMAP + uniform normalMatrix: mat4x4f; + + fn toNormalMatrix(m: mat4x4f) -> mat4x4f + { + var a00 = m[0][0]; + var a01 = m[0][1]; + var a02 = m[0][2]; + var a03 = m[0][3]; + var a10 = m[1][0]; + var a11 = m[1][1]; + var a12 = m[1][2]; + var a13 = m[1][3]; + var a20 = m[2][0]; + var a21 = m[2][1]; + var a22 = m[2][2]; + var a23 = m[2][3]; + var a30 = m[3][0]; + var a31 = m[3][1]; + var a32 = m[3][2]; + var a33 = m[3][3]; + + var b00 = a00 * a11 - a01 * a10; + var b01 = a00 * a12 - a02 * a10; + var b02 = a00 * a13 - a03 * a10; + var b03 = a01 * a12 - a02 * a11; + var b04 = a01 * a13 - a03 * a11; + var b05 = a02 * a13 - a03 * a12; + var b06 = a20 * a31 - a21 * a30; + var b07 = a20 * a32 - a22 * a30; + var b08 = a20 * a33 - a23 * a30; + var b09 = a21 * a32 - a22 * a31; + var b10 = a21 * a33 - a23 * a31; + var b11 = a22 * a33 - a23 * a32; + + var det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + + var mi = mat4x4( + (a11 * b11 - a12 * b10 + a13 * b09) / det, + (a02 * b10 - a01 * b11 - a03 * b09) / det, + (a31 * b05 - a32 * b04 + a33 * b03) / det, + (a22 * b04 - a21 * b05 - a23 * b03) / det, + (a12 * b08 - a10 * b11 - a13 * b07) / det, + (a00 * b11 - a02 * b08 + a03 * b07) / det, + (a32 * b02 - a30 * b05 - a33 * b01) / det, + (a20 * b05 - a22 * b02 + a23 * b01) / det, + (a10 * b10 - a11 * b08 + a13 * b06) / det, + (a01 * b08 - a00 * b10 - a03 * b06) / det, + (a30 * b04 - a31 * b02 + a33 * b00) / det, + (a21 * b02 - a20 * b04 - a23 * b00) / det, + (a11 * b07 - a10 * b09 - a12 * b06) / det, + (a00 * b09 - a01 * b07 + a02 * b06) / det, + (a31 * b01 - a30 * b03 - a32 * b00) / det, + (a20 * b03 - a21 * b01 + a22 * b00) / det); + + return mat4x4(mi[0][0], mi[1][0], mi[2][0], mi[3][0], + mi[0][1], mi[1][1], mi[2][1], mi[3][1], + mi[0][2], mi[1][2], mi[2][2], mi[3][2], + mi[0][3], mi[1][3], mi[2][3], mi[3][3]); + } + #endif + + fn perturbNormalBase(cotangentFrame: mat3x3f, normal: vec3f, scale: f32) -> vec3f + { + var output = normal; + #ifdef NORMALXYSCALE + output = normalize(output * vec3f(scale, scale, 1.0)); + #endif + + return normalize(cotangentFrame * output); + } + + fn perturbNormal(cotangentFrame: mat3x3f, textureSample: vec3f, scale: f32) -> vec3f + { + return perturbNormalBase(cotangentFrame, textureSample * 2.0 - 1.0, scale); + } + + // Thanks to http://www.thetenthplanet.de/archives/1180 + fn cotangent_frame(normal: vec3f, p: vec3f, uv: vec2f, tangentSpaceParams: vec2f) -> mat3x3f + { + // get edge vectors of the pixel triangle + var dp1: vec3f = dpdx(p); + var dp2: vec3f = dpdy(p); + var duv1: vec2f = dpdx(uv); + var duv2: vec2f = dpdy(uv); + + // solve the linear system + var dp2perp: vec3f = cross(dp2, normal); + var dp1perp: vec3f = cross(normal, dp1); + var tangent: vec3f = dp2perp * duv1.x + dp1perp * duv2.x; + var bitangent: vec3f = dp2perp * duv1.y + dp1perp * duv2.y; + + // invert the tangent/bitangent if requested + tangent *= tangentSpaceParams.x; + bitangent *= tangentSpaceParams.y; + + // construct a scale-invariant frame + var det: f32 = max(dot(tangent, tangent), dot(bitangent, bitangent)); + var invmax: f32 = select(inverseSqrt(det), 0.0, det == 0.0); + return mat3x3f(tangent * invmax, bitangent * invmax, normal); + } +#endif diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrNormalMapVertex.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrNormalMapVertex.fx new file mode 100644 index 00000000000..504955cd129 --- /dev/null +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrNormalMapVertex.fx @@ -0,0 +1,11 @@ +#if defined(GEOMETRY_NORMAL) || defined(PARALLAX) || defined(CLEARCOAT_BUMP) || defined(ANISOTROPIC) + #if defined(TANGENT) && defined(NORMAL) + var tbnNormal: vec3f = normalize(normalUpdated); + var tbnTangent: vec3f = normalize(tangentUpdated.xyz); + var tbnBitangent: vec3f = cross(tbnNormal, tbnTangent) * tangentUpdated.w; + var matTemp = mat3x3f(finalWorld[0].xyz, finalWorld[1].xyz, finalWorld[2].xyz) * mat3x3f(tbnTangent, tbnBitangent, tbnNormal); + vertexOutputs.vTBN0 = matTemp[0]; + vertexOutputs.vTBN1 = matTemp[1]; + vertexOutputs.vTBN2 = matTemp[2]; + #endif +#endif \ No newline at end of file diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrNormalMapVertexDeclaration.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrNormalMapVertexDeclaration.fx new file mode 100644 index 00000000000..11b840ed96f --- /dev/null +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrNormalMapVertexDeclaration.fx @@ -0,0 +1,7 @@ +#if defined(GEOMETRY_NORMAL) || defined(PARALLAX) || defined(CLEARCOAT_BUMP) || defined(ANISOTROPIC) + #if defined(TANGENT) && defined(NORMAL) + varying vTBN0: vec3f; + varying vTBN1: vec3f; + varying vTBN2: vec3f; + #endif +#endif diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrUboDeclaration.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrUboDeclaration.fx new file mode 100644 index 00000000000..94d19221652 --- /dev/null +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrUboDeclaration.fx @@ -0,0 +1,87 @@ + +uniform vTangentSpaceParams: vec2f; +uniform vLightingIntensity: vec4f; +uniform pointSize: f32; + +uniform vDebugMode: vec2f; + +uniform cameraInfo: vec4f; + +uniform vReflectionInfos: vec2f; +uniform reflectionMatrix: mat4x4f; +uniform vReflectionMicrosurfaceInfos: vec3f; +uniform vReflectionPosition: vec3f; +uniform vReflectionSize: vec3f; +uniform vReflectionFilteringInfo: vec2f; +uniform vReflectionDominantDirection: vec3f; +uniform vReflectionColor: vec3f; + +uniform vSphericalL00: vec3f; +uniform vSphericalL1_1: vec3f; +uniform vSphericalL10: vec3f; +uniform vSphericalL11: vec3f; +uniform vSphericalL2_2: vec3f; +uniform vSphericalL2_1: vec3f; +uniform vSphericalL20: vec3f; +uniform vSphericalL21: vec3f; +uniform vSphericalL22: vec3f; + +uniform vSphericalX: vec3f; +uniform vSphericalY: vec3f; +uniform vSphericalZ: vec3f; +uniform vSphericalXX_ZZ: vec3f; +uniform vSphericalYY_ZZ: vec3f; +uniform vSphericalZZ: vec3f; +uniform vSphericalXY: vec3f; +uniform vSphericalYZ: vec3f; +uniform vSphericalZX: vec3f; + +uniform vBaseWeight: f32; +uniform vBaseColor: vec4f; +uniform vBaseDiffuseRoughness: f32; +uniform vReflectanceInfo: vec4f; +uniform vSpecularColor: vec4f; +uniform vCoatWeight: f32; +uniform vCoatColor: vec3f; +uniform vCoatRoughness: f32; +uniform vCoatIor: f32; +uniform vCoatDarkening : f32; +uniform vEmissionColor: vec3f; + +uniform vBaseWeightInfos: vec2f; +uniform baseWeightMatrix: mat4x4f; +uniform vBaseColorInfos: vec2f; +uniform baseColorMatrix: mat4x4f; +uniform vBaseDiffuseRoughnessInfos: vec2f; +uniform baseDiffuseRoughnessMatrix: mat4x4f; +uniform vSpecularWeightInfos: vec2f; +uniform specularWeightMatrix: mat4x4f; +uniform vSpecularColorInfos: vec2f; +uniform specularColorMatrix: mat4x4f; +uniform vBaseMetalRoughInfos: vec2f; +uniform baseMetalRoughMatrix: mat4x4f; +uniform vCoatWeightInfos: vec2f; +uniform coatWeightMatrix: mat4x4f; +uniform vCoatColorInfos: vec2f; +uniform coatColorMatrix: mat4x4f; +uniform vCoatRoughnessInfos: vec2f; +uniform coatRoughnessMatrix: mat4x4f; +uniform vCoatDarkeningInfos : vec2f; +uniform coatDarkeningMatrix : mat4x4f; +uniform vGeometryNormalInfos: vec2f; +uniform geometryNormalMatrix: mat4x4f; +uniform vGeometryCoatNormalInfos: vec2f; +uniform geometryCoatNormalMatrix: mat4x4f; +uniform vGeometryOpacityInfos: vec2f; +uniform geometryOpacityMatrix: mat4x4f; +uniform vEmissionInfos: vec2f; +uniform emissionMatrix: mat4x4f; +uniform vAmbientOcclusionInfos: vec2f; +uniform ambientOcclusionMatrix: mat4x4f; + +#define ADDITIONAL_UBO_DECLARATION + + +#include +#include + diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBRDFFunctions.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBRDFFunctions.fx index 58f5fc763e1..e993e06e80e 100644 --- a/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBRDFFunctions.fx +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrBRDFFunctions.fx @@ -9,7 +9,7 @@ #define CONDUCTOR_SPECULAR_MODEL_GLTF 0 #define CONDUCTOR_SPECULAR_MODEL_OPENPBR 1 -#ifndef PBR_VERTEX_SHADER +#if !defined(PBR_VERTEX_SHADER) && !defined(OPENPBR_VERTEX_SHADER) // ______________________________________________________________________ // diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrUboDeclaration.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrUboDeclaration.fx index e0922402feb..6eacae512da 100644 --- a/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrUboDeclaration.fx +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrUboDeclaration.fx @@ -7,10 +7,6 @@ uniform vEmissiveInfos: vec2f; uniform vLightmapInfos: vec2f; uniform vReflectivityInfos: vec3f; uniform vMicroSurfaceSamplerInfos: vec2f; -uniform vReflectionInfos: vec2f; -uniform vReflectionFilteringInfo: vec2f; -uniform vReflectionPosition: vec3f; -uniform vReflectionSize: vec3f; uniform vBumpInfos: vec3f; uniform albedoMatrix: mat4x4f; uniform baseWeightMatrix: mat4x4f; @@ -23,14 +19,10 @@ uniform reflectivityMatrix: mat4x4f; uniform microSurfaceSamplerMatrix: mat4x4f; uniform bumpMatrix: mat4x4f; uniform vTangentSpaceParams: vec2f; -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; @@ -43,6 +35,16 @@ uniform vMetallicReflectanceInfos: vec2f; uniform metallicReflectanceMatrix: mat4x4f; uniform vReflectanceInfos: vec2f; uniform reflectanceMatrix: mat4x4f; +uniform cameraInfo: vec4f; + +uniform vReflectionInfos: vec2f; +uniform reflectionMatrix: mat4x4f; +uniform vReflectionMicrosurfaceInfos: vec3f; +uniform vReflectionPosition: vec3f; +uniform vReflectionSize: vec3f; +uniform vReflectionFilteringInfo: vec2f; +uniform vReflectionDominantDirection: vec3f; +uniform vReflectionColor: vec3f; uniform vSphericalL00: vec3f; uniform vSphericalL1_1: vec3f; @@ -64,8 +66,6 @@ uniform vSphericalXY: vec3f; uniform vSphericalYZ: vec3f; uniform vSphericalZX: vec3f; -uniform cameraInfo: vec4f; - #define ADDITIONAL_UBO_DECLARATION diff --git a/packages/dev/core/src/ShadersWGSL/openpbr.fragment.fx b/packages/dev/core/src/ShadersWGSL/openpbr.fragment.fx new file mode 100644 index 00000000000..a023a0f1953 --- /dev/null +++ b/packages/dev/core/src/ShadersWGSL/openpbr.fragment.fx @@ -0,0 +1,205 @@ +#define OPENPBR_FRAGMENT_SHADER + +#define CUSTOM_FRAGMENT_BEGIN + +#include[SCENE_MRT_COUNT] +#include + +// Forces linear space for image processing +#ifndef FROMLINEARSPACE + #define FROMLINEARSPACE +#endif + +// Declaration +#include + +#include +#include[0..maxSimultaneousLights] +#include +#include +#include +#include +#include + +// Helper Functions +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef REFLECTION + #include +#endif + +#define CUSTOM_FRAGMENT_DEFINITIONS + +#include +#include + +#include +#include +#include + +// Do a mix between layers with additional multipliers for each layer. +fn layer(slab_bottom: vec3f, slab_top: vec3f, lerp_factor: f32, bottom_multiplier: vec3f, top_multiplier: vec3f) -> vec3f { + + return mix(slab_bottom * bottom_multiplier, slab_top * top_multiplier, lerp_factor); +} + +// _____________________________ MAIN FUNCTION ____________________________ +@fragment +fn main(input: FragmentInputs) -> FragmentOutputs { + + #define CUSTOM_FRAGMENT_MAIN_BEGIN + + #include + + // _____________________________ Geometry Information ____________________________ + #include + var coatNormalW: vec3f = normalW; + + #include + + #include + + // ______________________ Read Base properties & Opacity ______________________________ + #include + + // _____________________________ Read Coat Layer properties ______________________ + #include + + // TEMP + var subsurface_weight: f32 = 0.0f; + var transmission_weight: f32 = 0.0f; + var fuzz_weight: f32 = 0.0f; + + #define CUSTOM_FRAGMENT_UPDATE_ALPHA + + #include + + #define CUSTOM_FRAGMENT_BEFORE_LIGHTS + + // _____________________________ AO _______________________________ + var aoOut: ambientOcclusionOutParams; + +#ifdef AMBIENT_OCCLUSION + var ambientOcclusionFromTexture: vec3f = textureSample(ambientOcclusionSampler, ambientOcclusionSamplerSampler, fragmentInputs.vAmbientOcclusionUV + uvOffset).rgb; +#endif + + aoOut = ambientOcclusionBlock( + #ifdef AMBIENT_OCCLUSION + ambientOcclusionFromTexture, + uniforms.vAmbientOcclusionInfos + #endif + ); + + // _____________________________ Compute Geometry info for coat layer _________________________ + let coatGeoInfo: geometryInfoOutParams = geometryInfo( + coatNormalW, viewDirectionW.xyz, coat_roughness, geometricNormalW + ); + + // _____________________________ Compute Geometry info for base layer _________________________ + // Adjust the base roughness to account for the coat layer. Equation 61 in OpenPBR spec. + specular_roughness = mix(specular_roughness, pow(min(1.0f, pow(specular_roughness, 4.0f) + 2.0f * pow(coat_roughness, 4.0f)), 0.25f), coat_weight); + let baseGeoInfo: geometryInfoOutParams = geometryInfo( + normalW, viewDirectionW.xyz, specular_roughness, geometricNormalW + ); + + // _______________________ F0 and F90 Reflectance _______________________________ + + // Coat + let coatReflectance: ReflectanceParams = dielectricReflectance( + coat_ior // inside IOR + , 1.0f // outside IOR is air + , vec3f(1.0f) + , coat_weight + ); + + // Base Dielectric + let baseDielectricReflectance: ReflectanceParams = dielectricReflectance( + specular_ior // inside IOR + , mix(1.0f, coat_ior, coat_weight) // outside IOR is coat + , specular_color + , specular_weight + ); + + // Base Metallic + let baseConductorReflectance: ReflectanceParams = conductorReflectance(base_color, specular_color, specular_weight); + + // ________________________ Environment (IBL) Lighting ____________________________ + var material_surface_ibl: vec3f = vec3f(0.f, 0.f, 0.f); + #include + + // __________________________ Direct Lighting ____________________________ + var material_surface_direct: vec3f = vec3f(0.f, 0.f, 0.f); + #if defined(LIGHT0) + var aggShadow: f32 = 0.f; + var numLights: f32 = 0.f; + #include[0..maxSimultaneousLights] + #include[0..maxSimultaneousLights] + + #endif + + // _________________________ Emissive Lighting _______________________________ + var material_surface_emission: vec3f = uniforms.vEmissionColor; + #ifdef EMISSION_COLOR + let emissionColorTex: vec3f = textureSample(emissionColorSampler, emissionColorSamplerSampler, uniforms.vEmissionColorUV + uvOffset).rgb; + #ifdef EMISSION_COLOR_GAMMA + material_surface_emission *= toLinearSpace(emissionColorTex.rgb); + #else + material_surface_emission *= emissionColorTex.rgb; + #endif + material_surface_emission *= uniforms.vEmissionColorInfos.y; + #endif + material_surface_emission *= uniforms.vLightingIntensity.y; + + // _____________________________ Final Color Composition ________________________ + #define CUSTOM_FRAGMENT_BEFORE_FINALCOLORCOMPOSITION + + var finalColor: vec4f = vec4f(material_surface_ibl + material_surface_direct + material_surface_emission, alpha); + + #define CUSTOM_FRAGMENT_BEFORE_FOG + + // _____________________________ Finally ___________________________________________ + finalColor = max(finalColor, vec4f(0.0)); + + #include + #include(color, finalColor) + #include + + #define CUSTOM_FRAGMENT_BEFORE_FRAGCOLOR + +#ifdef PREPASS + #include +#endif + +#if !defined(PREPASS) && !defined(ORDER_INDEPENDENT_TRANSPARENCY) + fragmentOutputs.color = finalColor; +#endif + + #include + +#if ORDER_INDEPENDENT_TRANSPARENCY + if (fragDepth == nearestDepth) { + fragmentOutputs.frontColor = vec4f(fragmentOutputs.frontColor.rgb + finalColor.rgb * finalColor.a * alphaMultiplier, 1.0 - alphaMultiplier * (1.0 - finalColor.a)); + } else { + fragmentOutputs.backColor += finalColor; + } +#endif + + #include + + #define CUSTOM_FRAGMENT_MAIN_END + +} diff --git a/packages/dev/core/src/ShadersWGSL/openpbr.vertex.fx b/packages/dev/core/src/ShadersWGSL/openpbr.vertex.fx new file mode 100644 index 00000000000..027c4717a0a --- /dev/null +++ b/packages/dev/core/src/ShadersWGSL/openpbr.vertex.fx @@ -0,0 +1,248 @@ +#define OPENPBR_VERTEX_SHADER + +#include + +#define CUSTOM_VERTEX_BEGIN + +// Attributes +attribute position: vec3f; +#ifdef NORMAL +attribute normal: vec3f; +#endif +#ifdef TANGENT +attribute tangent: vec4f; +#endif +#ifdef UV1 +attribute uv: vec2f; +#endif +#include[2..7] +#include[1..7] +#ifdef VERTEXCOLOR +attribute color: vec4f; +#endif + +#include +#include +#include +#include + +#include +#include + +#include(_DEFINENAME_,BASE_COLOR,_VARYINGNAME_,Albedo) +#include(_DEFINENAME_,BASE_WEIGHT,_VARYINGNAME_,BaseWeight) +#include(_DEFINENAME_,BASE_DIFFUSE_ROUGHNESS,_VARYINGNAME_,BaseDiffuseRoughness) +#include(_DEFINENAME_,METALLIC_ROUGHNESS,_VARYINGNAME_,BaseMetalRough) +#include(_DEFINENAME_,SPECULAR_WEIGHT,_VARYINGNAME_,SpecularWeight) +#include(_DEFINENAME_,SPECULAR_COLOR,_VARYINGNAME_,SpecularColor) +#include(_DEFINENAME_,COAT_WEIGHT,_VARYINGNAME_,CoatWeight) +#include(_DEFINENAME_,COAT_COLOR,_VARYINGNAME_,CoatColor) +#include(_DEFINENAME_,COAT_ROUGHNESS,_VARYINGNAME_,CoatRoughness) +// #include(_DEFINENAME_,COAT_ROUGHNESS_ANISOTROPY,_VARYINGNAME_,CoatRoughnessAnisotropy +#include(_DEFINENAME_,COAT_DARKENING,_VARYINGNAME_,CoatDarkening) +#include(_DEFINENAME_,GEOMETRY_NORMAL,_VARYINGNAME_,GeometryNormal) +#include(_DEFINENAME_,GEOMETRY_COAT_NORMAL,_VARYINGNAME_,GeometryCoatNormal) +#include(_DEFINENAME_,GEOMETRY_OPACITY,_VARYINGNAME_,GeometryOpacity) +#include(_DEFINENAME_,EMISSION_COLOR,_VARYINGNAME_,EmissionColor) + +#include(_DEFINENAME_,AMBIENT_OCCLUSION,_VARYINGNAME_,AmbientOcclusion) +#include(_DEFINENAME_,DECAL,_VARYINGNAME_,Decal) +#include(_DEFINENAME_,DETAIL,_VARYINGNAME_,Detail) + +// Output +varying vPositionW: vec3f; +#if DEBUGMODE > 0 + varying vClipSpacePosition: vec4f; +#endif +#ifdef NORMAL + varying vNormalW: vec3f; + #if defined(USESPHERICALFROMREFLECTIONMAP) && defined(USESPHERICALINVERTEX) + varying vEnvironmentIrradiance: vec3f; + + #include + #endif +#endif + +#if defined(VERTEXCOLOR) || defined(INSTANCESCOLOR) && defined(INSTANCES) +varying vColor: vec4f; +#endif + +#include +#include +#include +#include[0..maxSimultaneousLights] + +#include +#include[0..maxSimultaneousMorphTargets] + +#ifdef REFLECTIONMAP_SKYBOX +varying vPositionUVW: vec3f; +#endif + +#if defined(REFLECTIONMAP_EQUIRECTANGULAR_FIXED) || defined(REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED) +varying vDirectionW: vec3f; +#endif + +#include +#define CUSTOM_VERTEX_DEFINITIONS + +@vertex +fn main(input : VertexInputs) -> FragmentInputs { + + #define CUSTOM_VERTEX_MAIN_BEGIN + + var positionUpdated: vec3f = vertexInputs.position; +#ifdef NORMAL + var normalUpdated: vec3f = vertexInputs.normal; +#endif +#ifdef TANGENT + var tangentUpdated: vec4f = vertexInputs.tangent; +#endif +#ifdef UV1 + var uvUpdated: vec2f = vertexInputs.uv; +#endif +#ifdef UV2 + var uv2Updated: vec2f = vertexInputs.uv2; +#endif +#ifdef VERTEXCOLOR + var colorUpdated: vec4f = vertexInputs.color; +#endif + +#include +#include[0..maxSimultaneousMorphTargets] + +#ifdef REFLECTIONMAP_SKYBOX + vertexOutputs.vPositionUVW = positionUpdated; +#endif + +#define CUSTOM_VERTEX_UPDATE_POSITION + +#define CUSTOM_VERTEX_UPDATE_NORMAL + +#include + +#if defined(PREPASS) && ((defined(PREPASS_VELOCITY) || defined(PREPASS_VELOCITY_LINEAR)) && !defined(BONES_VELOCITY_ENABLED) + // Compute velocity before bones computation + vertexOutputs.vCurrentPosition = scene.viewProjection * finalWorld * vec4f(positionUpdated, 1.0); + vertexOutputs.vPreviousPosition = uniforms.previousViewProjection * finalPreviousWorld * vec4f(positionUpdated, 1.0); +#endif + +#include +#include + + var worldPos: vec4f = finalWorld * vec4f(positionUpdated, 1.0); + vertexOutputs.vPositionW = worldPos.xyz; + +#ifdef PREPASS + #include +#endif + +#ifdef NORMAL + var normalWorld: mat3x3f = mat3x3f(finalWorld[0].xyz, finalWorld[1].xyz, finalWorld[2].xyz); + + #if defined(INSTANCES) && defined(THIN_INSTANCES) + vertexOutputs.vNormalW = normalUpdated / vec3f(dot(normalWorld[0], normalWorld[0]), dot(normalWorld[1], normalWorld[1]), dot(normalWorld[2], normalWorld[2])); + vertexOutputs.vNormalW = normalize(normalWorld * vertexOutputs.vNormalW); + #else + #ifdef NONUNIFORMSCALING + normalWorld = transposeMat3(inverseMat3(normalWorld)); + #endif + + vertexOutputs.vNormalW = normalize(normalWorld * normalUpdated); + #endif + + #if defined(USESPHERICALFROMREFLECTIONMAP) && defined(USESPHERICALINVERTEX) + #if BASE_DIFFUSE_MODEL != BRDF_DIFFUSE_MODEL_LAMBERT && BASE_DIFFUSE_MODEL != BRDF_DIFFUSE_MODEL_LEGACY + // 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.vBaseDiffuseRoughness); + 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 + +#define CUSTOM_VERTEX_UPDATE_WORLDPOS + +#ifdef MULTIVIEW + if (gl_ViewID_OVR == 0u) { + vertexOutputs.position = scene.viewProjection * worldPos; + } else { + vertexOutputs.position = scene.viewProjectionR * worldPos; + } +#else + vertexOutputs.position = scene.viewProjection * worldPos; +#endif + +#if DEBUGMODE > 0 + vertexOutputs.vClipSpacePosition = vertexOutputs.position; +#endif + +#if defined(REFLECTIONMAP_EQUIRECTANGULAR_FIXED) || defined(REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED) + vertexOutputs.vDirectionW = normalize((finalWorld * vec4f(positionUpdated, 0.0)).xyz); +#endif + + // Texture coordinates +#ifndef UV1 + var uvUpdated: vec2f = vec2f(0., 0.); +#endif +#ifdef MAINUV1 + vertexOutputs.vMainUV1 = uvUpdated; +#endif +#ifndef UV2 + var uv2Updated: vec2f = vec2f(0., 0.); +#endif +#ifdef MAINUV2 + vertexOutputs.vMainUV2 = uv2Updated; +#endif + + #include[3..7] + + #include(_DEFINENAME_,BASE_COLOR,_VARYINGNAME_,BaseColor,_MATRIXNAME_,baseColor,_INFONAME_,BaseColorInfos.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_,METALLIC_ROUGHNESS,_VARYINGNAME_,BaseMetalRough,_MATRIXNAME_,baseMetalRough,_INFONAME_,BaseMetalRoughInfos.x) + #include(_DEFINENAME_,SPECULAR_WEIGHT,_VARYINGNAME_,SpecularWeight,_MATRIXNAME_,specularWeight,_INFONAME_,SpecularWeightInfos.x) + #include(_DEFINENAME_,SPECULAR_COLOR,_VARYINGNAME_,SpecularColor,_MATRIXNAME_,specularColor,_INFONAME_,SpecularColorInfos.x) + #include(_DEFINENAME_,COAT_WEIGHT,_VARYINGNAME_,CoatWeight,_MATRIXNAME_,coatWeight,_INFONAME_,CoatWeightInfos.x) + #include(_DEFINENAME_,COAT_COLOR,_VARYNAME_,CoatColor,_MATRIXNAME_,coatColor,_INFONAME_,CoatColorInfos.x) + #include(_DEFINENAME_,COAT_ROUGHNESS,_VARYINGNAME_,CoatRoughness,_MATRIXNAME_,coatRoughness,_INFONAME_,CoatRoughnessInfos.x) + // #include(_DEFINENAME_,COAT_ROUGHNESS_ANISOTROPY,_VARYINGNAME_,CoatRoughnessAnisotropy,_MATRIXNAME_,coatRoughnessAnisotropy,_INFONAME_,CoatRoughnessAnisotropyInfos.x) + #include(_DEFINENAME_,COAT_DARKENING,_VARYINGNAME_,CoatDarkening,_MATRIXNAME_,coatDarkening,_INFONAME_,CoatDarkeningInfos.x) + #include(_DEFINENAME_,GEOMETRY_NORMAL,_VARYINGNAME_,GeometryNormal,_MATRIXNAME_,geometryNormal,_INFONAME_,GeometryNormalInfos.x) + #include(_DEFINENAME_,GEOMETRY_COAT_NORMAL,_VARYINGNAME_,GeometryCoatNormal,_MATRIXNAME_,geometryCoatNormal,_INFONAME_,GeometryCoatNormalInfos.x) + #include(_DEFINENAME_,GEOMETRY_OPACITY,_VARYINGNAME_,GeometryOpacity,_MATRIXNAME_,geometryOpacity,_INFONAME_,GeometryOpacityInfos.x) + #include(_DEFINENAME_,EMISSION_COLOR,_VARYINGNAME_,EmissionColor,_MATRIXNAME_,emissionColor,_INFONAME_,EmissionColorInfos.x) + + #include(_DEFINENAME_,AMBIENT_OCCLUSION,_VARYINGNAME_,AmbientOcclusion,_MATRIXNAME_,ambientOcclusion,_INFONAME_,AmbientOcclusionInfos.x) + #include(_DEFINENAME_,DECAL,_VARYINGNAME_,Decal,_MATRIXNAME_,decal,_INFONAME_,DecalInfos.x) + #include(_DEFINENAME_,DETAIL,_VARYINGNAME_,Detail,_MATRIXNAME_,detail,_INFONAME_,DetailInfos.x) + + // TBN +#include + + // Clip plane +#include + + // Fog +#include + + // Shadows +#include[0..maxSimultaneousLights] + + // Vertex color +#include + + // Log. depth +#include + +#define CUSTOM_VERTEX_MAIN_END + +} \ No newline at end of file diff --git a/packages/dev/inspector/src/components/actionTabs/tabs/propertyGridTabComponent.tsx b/packages/dev/inspector/src/components/actionTabs/tabs/propertyGridTabComponent.tsx index d67eb582f9c..de6da686d63 100644 --- a/packages/dev/inspector/src/components/actionTabs/tabs/propertyGridTabComponent.tsx +++ b/packages/dev/inspector/src/components/actionTabs/tabs/propertyGridTabComponent.tsx @@ -21,6 +21,7 @@ import { MaterialPropertyGridComponent } from "./propertyGrids/materials/materia import { StandardMaterialPropertyGridComponent } from "./propertyGrids/materials/standardMaterialPropertyGridComponent"; import { TexturePropertyGridComponent } from "./propertyGrids/materials/texturePropertyGridComponent"; import { PBRMaterialPropertyGridComponent } from "./propertyGrids/materials/pbrMaterialPropertyGridComponent"; +import { OpenPBRMaterialPropertyGridComponent } from "./propertyGrids/materials/openpbrMaterialPropertyGridComponent"; import { ScenePropertyGridComponent } from "./propertyGrids/scenePropertyGridComponent"; import { HemisphericLightPropertyGridComponent } from "./propertyGrids/lights/hemisphericLightPropertyGridComponent"; import { PointLightPropertyGridComponent } from "./propertyGrids/lights/pointLightPropertyGridComponent"; @@ -117,6 +118,7 @@ import { Tags } from "core/Misc/tags"; import { LineContainerComponent } from "shared-ui-components/lines/lineContainerComponent"; import type { RectAreaLight } from "core/Lights/rectAreaLight"; import { FluentToolWrapper } from "shared-ui-components/fluent/hoc/fluentToolWrapper"; +import type { OpenPBRMaterial } from "core/Materials/PBR/openPbrMaterial"; export class PropertyGridTabComponent extends PaneComponent { private _timerIntervalId: number; @@ -398,6 +400,19 @@ export class PropertyGridTabComponent extends PaneComponent { ); } + if (className === "OpenPBRMaterial") { + const material = entity as OpenPBRMaterial; + return ( + + ); + } + if (className === "PBRMetallicRoughnessMaterial") { const material = entity as PBRMetallicRoughnessMaterial; return ( diff --git a/packages/dev/inspector/src/components/actionTabs/tabs/propertyGrids/materials/openpbrMaterialPropertyGridComponent.tsx b/packages/dev/inspector/src/components/actionTabs/tabs/propertyGrids/materials/openpbrMaterialPropertyGridComponent.tsx new file mode 100644 index 00000000000..6ef79c75fa3 --- /dev/null +++ b/packages/dev/inspector/src/components/actionTabs/tabs/propertyGrids/materials/openpbrMaterialPropertyGridComponent.tsx @@ -0,0 +1,576 @@ +import * as React from "react"; + +import { Observable } from "core/Misc/observable"; +import { Constants } from "core/Engines/constants"; + +import type { PropertyChangedEvent } from "../../../../propertyChangedEvent"; +import { LineContainerComponent } from "shared-ui-components/lines/lineContainerComponent"; +import { Color3LineComponent } from "shared-ui-components/lines/color3LineComponent"; +import { CheckBoxLineComponent } from "shared-ui-components/lines/checkBoxLineComponent"; +import { SliderLineComponent } from "shared-ui-components/lines/sliderLineComponent"; +import { OptionsLine } from "shared-ui-components/lines/optionsLineComponent"; +import { CommonMaterialPropertyGridComponent } from "./commonMaterialPropertyGridComponent"; +import { TextureLinkLineComponent } from "../../../lines/textureLinkLineComponent"; +import type { LockObject } from "shared-ui-components/tabs/propertyGrids/lockObject"; +import type { GlobalState } from "../../../../globalState"; + +import "core/Materials/material.decalMap"; +import "core/Rendering/prePassRendererSceneComponent"; +import "core/Rendering/subSurfaceSceneComponent"; +import type { OpenPBRMaterial } from "core/Materials/PBR/openPbrMaterial"; + +interface IOpenPBRMaterialPropertyGridComponentProps { + globalState: GlobalState; + material: OpenPBRMaterial; + lockObject: LockObject; + onSelectionChangedObservable?: Observable; + onPropertyChangedObservable?: Observable; +} + +/** + * @internal + */ +export class OpenPBRMaterialPropertyGridComponent extends React.Component { + private _onDebugSelectionChangeObservable = new Observable(); + constructor(props: IOpenPBRMaterialPropertyGridComponentProps) { + super(props); + } + + switchAmbientMode(state: boolean) { + this.props.material.debugMode = state ? 21 : 0; + } + + renderTextures(onDebugSelectionChangeObservable: Observable) { + const material = this.props.material; + + return ( + + + + + + + + + + + + + + + + ); + } + + override render() { + const material = this.props.material; + + const debugMode = [ + { label: "None", value: 0 }, + // Geometry + { label: "Normalized position", value: 1 }, + { label: "Normals", value: 2 }, + { label: "Tangents", value: 3 }, + { label: "Bitangents", value: 4 }, + { label: "Bump Normals", value: 5 }, + { label: "UV1", value: 6 }, + { label: "UV2", value: 7 }, + { label: "ClearCoat Normals", value: 8 }, + { label: "ClearCoat Tangents", value: 9 }, + { label: "ClearCoat Bitangents", value: 10 }, + { label: "Anisotropic Normals", value: 11 }, + { label: "Anisotropic Tangents", value: 12 }, + { label: "Anisotropic Bitangents", value: 13 }, + // Maps + { label: "Albedo Map", value: 20 }, + { label: "Ambient Map", value: 21 }, + { label: "Opacity Map", value: 22 }, + { label: "Emissive Map", value: 23 }, + { label: "Light Map", value: 24 }, + { label: "Metallic Map", value: 25 }, + { label: "Reflectivity Map", value: 26 }, + { label: "ClearCoat Map", value: 27 }, + { label: "ClearCoat Tint Map", value: 28 }, + { label: "Sheen Map", value: 29 }, + { label: "Anisotropic Map", value: 30 }, + { label: "Thickness Map", value: 31 }, + { label: "Bump Map", value: 32 }, + // Env + { label: "Env Refraction", value: 40 }, + { label: "Env Reflection", value: 41 }, + { label: "Env Clear Coat", value: 42 }, + // Lighting + { label: "Direct Diffuse", value: 50 }, + { label: "Direct Specular", value: 51 }, + { label: "Direct Clear Coat", value: 52 }, + { label: "Direct Sheen", value: 53 }, + { label: "Env Irradiance", value: 54 }, + // Lighting Params + { label: "Surface Albedo", value: 60 }, + { label: "Reflectance 0", value: 61 }, + { label: "Metallic", value: 62 }, + { label: "Metallic F0", value: 71 }, + { label: "Roughness", value: 63 }, + { label: "AlphaG", value: 64 }, + { label: "NdotV", value: 65 }, + { label: "ClearCoat Color", value: 66 }, + { label: "ClearCoat Roughness", value: 67 }, + { label: "ClearCoat NdotV", value: 68 }, + { label: "Transmittance", value: 69 }, + { label: "Refraction Transmittance", value: 70 }, + { label: "Glossiness", value: 72 }, + { label: "Base Color", value: 73 }, + { label: "Specular Color", value: 74 }, + { label: "Emissive Color", value: 75 }, + // Misc + { label: "SEO", value: 80 }, + { label: "EHO", value: 81 }, + { label: "Energy Factor", value: 82 }, + { label: "Specular Reflectance", value: 83 }, + { label: "Clear Coat Reflectance", value: 84 }, + { label: "Sheen Reflectance", value: 85 }, + { label: "Luminance Over Alpha", value: 86 }, + { label: "Alpha", value: 87 }, + { label: "Albedo Alpha", value: 88 }, + { label: "Ambient occlusion color", value: 89 }, + ]; + + const realTimeFilteringQualityOptions = [ + { label: "Low", value: Constants.TEXTURE_FILTERING_QUALITY_LOW }, + { label: "Medium", value: Constants.TEXTURE_FILTERING_QUALITY_MEDIUM }, + { label: "High", value: Constants.TEXTURE_FILTERING_QUALITY_HIGH }, + ]; + + return ( + <> + + {this.renderTextures(this._onDebugSelectionChangeObservable)} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); + } +} diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/EXT_materials_clearcoat_darkening.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/EXT_materials_clearcoat_darkening.ts new file mode 100644 index 00000000000..4ae75fcb0c1 --- /dev/null +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/EXT_materials_clearcoat_darkening.ts @@ -0,0 +1,118 @@ +/* eslint-disable github/no-then */ +import type { Nullable } from "core/types"; +import type { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { OpenPBRMaterial } from "core/Materials/PBR/openPbrMaterial"; +import type { Material } from "core/Materials/material"; +import type { BaseTexture } from "core/Materials/Textures/baseTexture"; +import type { IMaterial, ITextureInfo } from "../glTFLoaderInterfaces"; +import type { IGLTFLoaderExtension } from "../glTFLoaderExtension"; +import { GLTFLoader } from "../glTFLoader"; +import type { IEXTMaterialsClearcoatDarkening } from "babylonjs-gltf2interface"; +import { registerGLTFExtension, unregisterGLTFExtension } from "../glTFLoaderExtensionRegistry"; + +const NAME = "EXT_materials_clearcoat_darkening"; + +declare module "../../glTFFileLoader" { + // eslint-disable-next-line jsdoc/require-jsdoc, @typescript-eslint/naming-convention + export interface GLTFLoaderExtensionOptions { + /** + * Defines options for the EXT_materials_clearcoat_darkening extension. + */ + // NOTE: Don't use NAME here as it will break the UMD type declarations. + ["EXT_materials_clearcoat_darkening"]: {}; + } +} + +let PBRMaterialClass: typeof PBRMaterial | typeof OpenPBRMaterial; + +/** + * [Proposed Specification](https://github.com/KhronosGroup/glTF/pull/2518) + * !!! Experimental Extension Subject to Changes !!! + */ +// eslint-disable-next-line @typescript-eslint/naming-convention +export class EXT_materials_clearcoat_darkening 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 = 191; + + 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 + */ + // eslint-disable-next-line no-restricted-syntax + public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material, useOpenPBR: boolean = false): Nullable> { + return GLTFLoader.LoadExtensionAsync(context, material, this.name, async (extensionContext, extension) => { + const promises = new Array>(); + promises.push(this._loader.loadMaterialPropertiesAsync(context, material, babylonMaterial)); + if (useOpenPBR) { + const mod = await import("core/Materials/PBR/openPbrMaterial"); + PBRMaterialClass = mod.OpenPBRMaterial; + } else { + // Logger.Warn(`${extensionContext}: The EXT_materials_clearcoat_darkening extension is only supported with OpenPBR materials. Falling back to PBRMaterial.`); + return await Promise.resolve(); + } + promises.push(this._loadDarkeningPropertiesAsync(extensionContext, material, babylonMaterial, extension)); + return await Promise.all(promises).then(() => {}); + }); + } + + // eslint-disable-next-line no-restricted-syntax, @typescript-eslint/promise-function-async + private _loadDarkeningPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material, extension: IEXTMaterialsClearcoatDarkening): Promise { + if (!(babylonMaterial instanceof PBRMaterialClass)) { + throw new Error(`${context}: Material type not supported`); + } + + let darkeningFactor = 1.0; + let darkeningFactorTexture: Nullable; + + if (extension.clearcoatDarkeningFactor !== undefined) { + darkeningFactor = extension.clearcoatDarkeningFactor; + } + + let texturePromise = Promise.resolve(); + + if (extension.clearcoatDarkeningTexture) { + (extension.clearcoatDarkeningTexture as ITextureInfo).nonColorData = true; + texturePromise = this._loader.loadTextureInfoAsync(`${context}/clearcoatDarkeningTexture`, extension.clearcoatDarkeningTexture).then((texture: BaseTexture) => { + texture.name = `${babylonMaterial.name} (Clearcoat Darkening)`; + darkeningFactorTexture = texture; + }); + } + + return texturePromise.then(() => { + const openpbrMaterial = babylonMaterial as OpenPBRMaterial; + openpbrMaterial.coatDarkening = darkeningFactor; + if (darkeningFactorTexture) { + openpbrMaterial.coatDarkeningTexture = darkeningFactorTexture; + } + }); + } +} + +unregisterGLTFExtension(NAME); +registerGLTFExtension(NAME, true, (loader) => new EXT_materials_clearcoat_darkening(loader)); 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 index 2ded7721f72..6fad1d1968f 100644 --- 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 @@ -1,5 +1,6 @@ import type { Nullable } from "core/types"; import { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import { OpenPBRMaterial } from "core/Materials/PBR/openPbrMaterial"; import type { Material } from "core/Materials/material"; import type { IMaterial } from "../glTFLoaderInterfaces"; @@ -74,13 +75,15 @@ export class EXT_materials_diffuse_roughness implements IGLTFLoaderExtension { // eslint-disable-next-line @typescript-eslint/promise-function-async, no-restricted-syntax private _loadDiffuseRoughnessPropertiesAsync(context: string, properties: IEXTMaterialsDiffuseRoughness, babylonMaterial: Material): Promise { - if (!(babylonMaterial instanceof PBRMaterial)) { + if (!(babylonMaterial instanceof PBRMaterial) && !(babylonMaterial instanceof OpenPBRMaterial)) { throw new Error(`${context}: Material type not supported`); } const promises = new Array>(); - babylonMaterial.brdf.baseDiffuseModel = Constants.MATERIAL_DIFFUSE_MODEL_E_OREN_NAYAR; + if (babylonMaterial instanceof PBRMaterial) { + babylonMaterial.brdf.baseDiffuseModel = Constants.MATERIAL_DIFFUSE_MODEL_E_OREN_NAYAR; + } if (properties.diffuseRoughnessFactor != undefined) { babylonMaterial.baseDiffuseRoughness = properties.diffuseRoughnessFactor; diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_anisotropy.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_anisotropy.ts index c425c295da5..9c3e82ddad3 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_anisotropy.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_anisotropy.ts @@ -1,5 +1,6 @@ import type { Nullable } from "core/types"; -import { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { OpenPBRMaterial } from "core/Materials/PBR/openPbrMaterial"; import type { Material } from "core/Materials/material"; import type { IMaterial, ITextureInfo } from "../glTFLoaderInterfaces"; @@ -21,6 +22,8 @@ declare module "../../glTFFileLoader" { } } +let PBRMaterialClass: typeof PBRMaterial | typeof OpenPBRMaterial; + /** * [Specification](https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_materials_anisotropy) */ @@ -60,33 +63,40 @@ export class KHR_materials_anisotropy implements IGLTFLoaderExtension { * @internal */ // eslint-disable-next-line no-restricted-syntax - public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material): Nullable> { + public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material, useOpenPBR: boolean = false): Nullable> { return GLTFLoader.LoadExtensionAsync(context, material, this.name, async (extensionContext, extension) => { const promises = new Array>(); promises.push(this._loader.loadMaterialPropertiesAsync(context, material, babylonMaterial)); + if (useOpenPBR) { + const mod = await import("core/Materials/PBR/openPbrMaterial"); + PBRMaterialClass = mod.OpenPBRMaterial; + } else { + const mod = await import("core/Materials/PBR/pbrMaterial"); + PBRMaterialClass = mod.PBRMaterial; + } promises.push(this._loadIridescencePropertiesAsync(extensionContext, extension, babylonMaterial)); await Promise.all(promises); }); } private async _loadIridescencePropertiesAsync(context: string, properties: IKHRMaterialsAnisotropy, babylonMaterial: Material): Promise { - if (!(babylonMaterial instanceof PBRMaterial)) { + if (!(babylonMaterial instanceof PBRMaterialClass)) { throw new Error(`${context}: Material type not supported`); } const promises = new Array>(); - babylonMaterial.anisotropy.isEnabled = true; + (babylonMaterial as PBRMaterial).anisotropy.isEnabled = true; - babylonMaterial.anisotropy.intensity = properties.anisotropyStrength ?? 0; - babylonMaterial.anisotropy.angle = properties.anisotropyRotation ?? 0; + (babylonMaterial as PBRMaterial).anisotropy.intensity = properties.anisotropyStrength ?? 0; + (babylonMaterial as PBRMaterial).anisotropy.angle = properties.anisotropyRotation ?? 0; if (properties.anisotropyTexture) { (properties.anisotropyTexture as ITextureInfo).nonColorData = true; promises.push( this._loader.loadTextureInfoAsync(`${context}/anisotropyTexture`, properties.anisotropyTexture, (texture) => { texture.name = `${babylonMaterial.name} (Anisotropy Intensity)`; - babylonMaterial.anisotropy.texture = texture; + (babylonMaterial as PBRMaterial).anisotropy.texture = texture; }) ); } diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_clearcoat.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_clearcoat.ts index bc394cd1040..293efce7bd4 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_clearcoat.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_clearcoat.ts @@ -1,12 +1,15 @@ import type { Nullable } from "core/types"; -import { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { OpenPBRMaterial } from "core/Materials/PBR/openPbrMaterial"; import type { Material } from "core/Materials/material"; +import type { BaseTexture } from "core/Materials/Textures/baseTexture"; import type { IMaterial, ITextureInfo } from "../glTFLoaderInterfaces"; import type { IGLTFLoaderExtension } from "../glTFLoaderExtension"; import { GLTFLoader } from "../glTFLoader"; import type { IKHRMaterialsClearcoat } from "babylonjs-gltf2interface"; -import { registerGLTFExtension, unregisterGLTFExtension } from "../glTFLoaderExtensionRegistry"; +import { registeredGLTFExtensions, registerGLTFExtension, unregisterGLTFExtension } from "../glTFLoaderExtensionRegistry"; +import type { EXT_materials_clearcoat_darkening } from "./EXT_materials_clearcoat_darkening"; const NAME = "KHR_materials_clearcoat"; @@ -21,6 +24,8 @@ declare module "../../glTFFileLoader" { } } +let PBRMaterialClass: typeof PBRMaterial | typeof OpenPBRMaterial; + /** * [Specification](https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_clearcoat/README.md) * [Playground Sample](https://www.babylonjs-playground.com/frame.html#7F7PN6#8) @@ -61,46 +66,68 @@ export class KHR_materials_clearcoat implements IGLTFLoaderExtension { * @internal */ // eslint-disable-next-line no-restricted-syntax - public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material): Nullable> { + public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material, useOpenPBR: boolean = false): Nullable> { return GLTFLoader.LoadExtensionAsync(context, material, this.name, async (extensionContext, extension) => { const promises = new Array>(); promises.push(this._loader.loadMaterialPropertiesAsync(context, material, babylonMaterial)); - promises.push(this._loadClearCoatPropertiesAsync(extensionContext, extension, babylonMaterial)); + if (useOpenPBR) { + const mod = await import("core/Materials/PBR/openPbrMaterial"); + PBRMaterialClass = mod.OpenPBRMaterial; + } else { + const mod = await import("core/Materials/PBR/pbrMaterial"); + PBRMaterialClass = mod.PBRMaterial; + } + if (!(babylonMaterial instanceof PBRMaterialClass)) { + throw new Error(`${context}: Material type not supported`); + } + promises.push(this._loadClearCoatPropertiesAsync(extensionContext, extension, babylonMaterial, useOpenPBR)); + if (useOpenPBR && extension.extensions && extension.extensions.EXT_materials_clearcoat_darkening) { + let darkeningExtension = await registeredGLTFExtensions.get("EXT_materials_clearcoat_darkening")?.factory(this._loader); + darkeningExtension = darkeningExtension as EXT_materials_clearcoat_darkening; + if (darkeningExtension && darkeningExtension.enabled && darkeningExtension.loadMaterialPropertiesAsync) { + const promise = darkeningExtension.loadMaterialPropertiesAsync(extensionContext, extension as any, babylonMaterial, useOpenPBR); + if (promise) { + promises.push(promise); + } + } + } await Promise.all(promises); }); } // eslint-disable-next-line @typescript-eslint/promise-function-async, no-restricted-syntax - private _loadClearCoatPropertiesAsync(context: string, properties: IKHRMaterialsClearcoat, babylonMaterial: Material): Promise { - if (!(babylonMaterial instanceof PBRMaterial)) { + private _loadClearCoatPropertiesAsync(context: string, properties: IKHRMaterialsClearcoat, babylonMaterial: Material, useOpenPBR: boolean = false): Promise { + if (!(babylonMaterial instanceof PBRMaterialClass)) { throw new Error(`${context}: Material type not supported`); } const promises = new Array>(); - - babylonMaterial.clearCoat.isEnabled = true; - babylonMaterial.clearCoat.useRoughnessFromMainTexture = false; - babylonMaterial.clearCoat.remapF0OnInterfaceChange = false; + let coatRoughness = 0; + let coatWeight = 0; + let coatWeightTexture: Nullable = null; + let coatRoughnessTexture: Nullable = null; + let coatNormalTexture: Nullable = null; + let coatNormalTextureScale = 1; if (properties.clearcoatFactor != undefined) { - babylonMaterial.clearCoat.intensity = properties.clearcoatFactor; + coatWeight = properties.clearcoatFactor; } else { - babylonMaterial.clearCoat.intensity = 0; + coatWeight = 0; } if (properties.clearcoatTexture) { promises.push( this._loader.loadTextureInfoAsync(`${context}/clearcoatTexture`, properties.clearcoatTexture, (texture) => { texture.name = `${babylonMaterial.name} (ClearCoat)`; - babylonMaterial.clearCoat.texture = texture; + coatWeightTexture = texture; }) ); } if (properties.clearcoatRoughnessFactor != undefined) { - babylonMaterial.clearCoat.roughness = properties.clearcoatRoughnessFactor; + coatRoughness = properties.clearcoatRoughnessFactor; } else { - babylonMaterial.clearCoat.roughness = 0; + coatRoughness = 0; } if (properties.clearcoatRoughnessTexture) { @@ -108,7 +135,7 @@ export class KHR_materials_clearcoat implements IGLTFLoaderExtension { promises.push( this._loader.loadTextureInfoAsync(`${context}/clearcoatRoughnessTexture`, properties.clearcoatRoughnessTexture, (texture) => { texture.name = `${babylonMaterial.name} (ClearCoat Roughness)`; - babylonMaterial.clearCoat.textureRoughness = texture; + coatRoughnessTexture = texture; }) ); } @@ -118,19 +145,43 @@ export class KHR_materials_clearcoat implements IGLTFLoaderExtension { promises.push( this._loader.loadTextureInfoAsync(`${context}/clearcoatNormalTexture`, properties.clearcoatNormalTexture, (texture) => { texture.name = `${babylonMaterial.name} (ClearCoat Normal)`; - babylonMaterial.clearCoat.bumpTexture = texture; + coatNormalTexture = texture; }) ); babylonMaterial.invertNormalMapX = !babylonMaterial.getScene().useRightHandedSystem; babylonMaterial.invertNormalMapY = babylonMaterial.getScene().useRightHandedSystem; if (properties.clearcoatNormalTexture.scale != undefined) { - babylonMaterial.clearCoat.bumpTexture!.level = properties.clearcoatNormalTexture.scale; + coatNormalTextureScale = properties.clearcoatNormalTexture.scale; } } // eslint-disable-next-line github/no-then - return Promise.all(promises).then(() => {}); + return Promise.all(promises).then(() => { + if (useOpenPBR) { + const material = babylonMaterial as OpenPBRMaterial; + material.coatWeight = coatWeight; + material.coatWeightTexture = coatWeightTexture; + material.coatRoughness = coatRoughness; + material.coatRoughnessTexture = coatRoughnessTexture; + // material.coatNormalTexture = coatNormalTexture; + // material.coatNormalTextureScale = coatNormalTextureScale; + return; + } + const material = babylonMaterial as PBRMaterial; + material.clearCoat.isEnabled = true; + material.clearCoat.useRoughnessFromMainTexture = false; + material.clearCoat.remapF0OnInterfaceChange = false; + material.clearCoat.intensity = coatWeight; + material.clearCoat.texture = coatWeightTexture; + material.clearCoat.roughness = coatRoughness; + material.clearCoat.textureRoughness = coatRoughnessTexture; + + material.clearCoat.bumpTexture = coatNormalTexture; + if (coatNormalTexture) { + material.clearCoat.bumpTexture!.level = coatNormalTextureScale; + } + }); } } diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_diffuse_transmission.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_diffuse_transmission.ts index 5507ebfb1ab..5b511ac53cf 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_diffuse_transmission.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_diffuse_transmission.ts @@ -1,6 +1,7 @@ /* eslint-disable github/no-then */ import type { Nullable } from "core/types"; -import { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { OpenPBRMaterial } from "core/Materials/PBR/openPbrMaterial"; import type { Material } from "core/Materials/material"; import type { BaseTexture } from "core/Materials/Textures/baseTexture"; import type { IMaterial, ITextureInfo } from "../glTFLoaderInterfaces"; @@ -23,6 +24,8 @@ declare module "../../glTFFileLoader" { } } +let PBRMaterialClass: typeof PBRMaterial | typeof OpenPBRMaterial; + /** * [Proposed Specification](https://github.com/KhronosGroup/glTF/pull/1825) * !!! Experimental Extension Subject to Changes !!! @@ -66,10 +69,17 @@ export class KHR_materials_diffuse_transmission implements IGLTFLoaderExtension * @internal */ // eslint-disable-next-line no-restricted-syntax - public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material): Nullable> { + public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material, useOpenPBR: boolean = false): Nullable> { return GLTFLoader.LoadExtensionAsync(context, material, this.name, async (extensionContext, extension) => { const promises = new Array>(); promises.push(this._loader.loadMaterialPropertiesAsync(context, material, babylonMaterial)); + if (useOpenPBR) { + const mod = await import("core/Materials/PBR/openPbrMaterial"); + PBRMaterialClass = mod.OpenPBRMaterial; + } else { + const mod = await import("core/Materials/PBR/pbrMaterial"); + PBRMaterialClass = mod.PBRMaterial; + } promises.push(this._loadTranslucentPropertiesAsync(extensionContext, material, babylonMaterial, extension)); return await Promise.all(promises).then(() => {}); }); @@ -77,62 +87,71 @@ export class KHR_materials_diffuse_transmission implements IGLTFLoaderExtension // eslint-disable-next-line no-restricted-syntax, @typescript-eslint/promise-function-async private _loadTranslucentPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material, extension: IKHRMaterialsDiffuseTransmission): Promise { - if (!(babylonMaterial instanceof PBRMaterial)) { + if (!(babylonMaterial instanceof PBRMaterialClass)) { throw new Error(`${context}: Material type not supported`); } - const pbrMaterial = babylonMaterial; - - // Enables "translucency" texture which represents diffusely-transmitted light. - pbrMaterial.subSurface.isTranslucencyEnabled = true; - - // Since this extension models thin-surface transmission only, we must make the - // internal IOR == 1.0 and set the thickness to 0. - pbrMaterial.subSurface.volumeIndexOfRefraction = 1.0; - pbrMaterial.subSurface.minimumThickness = 0.0; - pbrMaterial.subSurface.maximumThickness = 0.0; - - // Tint color will be used for transmission. - pbrMaterial.subSurface.useAlbedoToTintTranslucency = false; + let translucencyWeight = 0.0; + let translucencyWeightTexture: Nullable; + let translucencyColor = Color3.White(); + let translucencyColorTexture: Nullable; if (extension.diffuseTransmissionFactor !== undefined) { - pbrMaterial.subSurface.translucencyIntensity = extension.diffuseTransmissionFactor; - } else { - pbrMaterial.subSurface.translucencyIntensity = 0.0; - pbrMaterial.subSurface.isTranslucencyEnabled = false; - return Promise.resolve(); + translucencyWeight = extension.diffuseTransmissionFactor; } const promises = new Array>(); - pbrMaterial.subSurface.useGltfStyleTextures = true; - if (extension.diffuseTransmissionTexture) { (extension.diffuseTransmissionTexture as ITextureInfo).nonColorData = true; promises.push( this._loader.loadTextureInfoAsync(`${context}/diffuseTransmissionTexture`, extension.diffuseTransmissionTexture).then((texture: BaseTexture) => { texture.name = `${babylonMaterial.name} (Diffuse Transmission)`; - pbrMaterial.subSurface.translucencyIntensityTexture = texture; + translucencyWeightTexture = texture; }) ); } if (extension.diffuseTransmissionColorFactor !== undefined) { - pbrMaterial.subSurface.translucencyColor = Color3.FromArray(extension.diffuseTransmissionColorFactor); - } else { - pbrMaterial.subSurface.translucencyColor = Color3.White(); + translucencyColor = Color3.FromArray(extension.diffuseTransmissionColorFactor); } if (extension.diffuseTransmissionColorTexture) { promises.push( this._loader.loadTextureInfoAsync(`${context}/diffuseTransmissionColorTexture`, extension.diffuseTransmissionColorTexture).then((texture: BaseTexture) => { texture.name = `${babylonMaterial.name} (Diffuse Transmission Color)`; - pbrMaterial.subSurface.translucencyColorTexture = texture; + translucencyColorTexture = texture; }) ); } - return Promise.all(promises).then(() => {}); + return Promise.all(promises).then(() => { + const pbrMaterial = babylonMaterial as PBRMaterial; + + // Enables "translucency" texture which represents diffusely-transmitted light. + pbrMaterial.subSurface.isTranslucencyEnabled = true; + + // Since this extension models thin-surface transmission only, we must make the + // internal IOR == 1.0 and set the thickness to 0. + pbrMaterial.subSurface.volumeIndexOfRefraction = 1.0; + pbrMaterial.subSurface.minimumThickness = 0.0; + pbrMaterial.subSurface.maximumThickness = 0.0; + + // Tint color will be used for transmission. + pbrMaterial.subSurface.useAlbedoToTintTranslucency = false; + + pbrMaterial.subSurface.translucencyIntensity = translucencyWeight; + if (translucencyWeight === 0.0) { + pbrMaterial.subSurface.isTranslucencyEnabled = false; + return; + } + + pbrMaterial.subSurface.useGltfStyleTextures = true; + pbrMaterial.subSurface.translucencyIntensityTexture = translucencyWeightTexture; + + pbrMaterial.subSurface.translucencyColor = translucencyColor; + pbrMaterial.subSurface.translucencyColorTexture = translucencyColorTexture; + }); } } diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_dispersion.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_dispersion.ts index 1a99485b358..1783343dba8 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_dispersion.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_dispersion.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ import type { Nullable } from "core/types"; -import { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { OpenPBRMaterial } from "core/Materials/PBR/openPbrMaterial"; import type { Material } from "core/Materials/material"; import type { IMaterial } from "../glTFLoaderInterfaces"; import type { IGLTFLoaderExtension } from "../glTFLoaderExtension"; @@ -21,6 +22,8 @@ declare module "../../glTFFileLoader" { } } +let PBRMaterialClass: typeof PBRMaterial | typeof OpenPBRMaterial; + /** * [Specification](https://github.com/KhronosGroup/glTF/blob/87bd64a7f5e23c84b6aef2e6082069583ed0ddb4/extensions/2.0/Khronos/KHR_materials_dispersion/README.md) * @experimental @@ -61,29 +64,44 @@ export class KHR_materials_dispersion implements IGLTFLoaderExtension { * @internal */ // eslint-disable-next-line no-restricted-syntax - public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material): Nullable> { + public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material, useOpenPBR: false): Nullable> { return GLTFLoader.LoadExtensionAsync(context, material, this.name, async (extensionContext, extension) => { const promises = new Array>(); promises.push(this._loader.loadMaterialPropertiesAsync(context, material, babylonMaterial)); - promises.push(this._loadDispersionPropertiesAsync(extensionContext, material, babylonMaterial, extension)); + if (useOpenPBR) { + const mod = await import("core/Materials/PBR/openPbrMaterial"); + PBRMaterialClass = mod.OpenPBRMaterial; + } else { + const mod = await import("core/Materials/PBR/pbrMaterial"); + PBRMaterialClass = mod.PBRMaterial; + } + promises.push(this._loadDispersionPropertiesAsync(extensionContext, material, babylonMaterial, extension, useOpenPBR)); // eslint-disable-next-line github/no-then return await Promise.all(promises).then(() => {}); }); } // eslint-disable-next-line @typescript-eslint/promise-function-async, no-restricted-syntax - private _loadDispersionPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material, extension: IKHRMaterialsDispersion): Promise { - if (!(babylonMaterial instanceof PBRMaterial)) { + private _loadDispersionPropertiesAsync( + context: string, + material: IMaterial, + babylonMaterial: Material, + extension: IKHRMaterialsDispersion, + useOpenPBR: boolean = false + ): Promise { + if (!(babylonMaterial instanceof PBRMaterialClass)) { throw new Error(`${context}: Material type not supported`); } - // If transparency isn't enabled already, this extension shouldn't do anything. - // i.e. it requires either the KHR_materials_transmission or KHR_materials_diffuse_transmission extensions. - if (!babylonMaterial.subSurface.isRefractionEnabled || !extension.dispersion) { - return Promise.resolve(); + if (!useOpenPBR) { + // If transparency isn't enabled already, this extension shouldn't do anything. + // i.e. it requires either the KHR_materials_transmission or KHR_materials_diffuse_transmission extensions. + if (!(babylonMaterial as PBRMaterial).subSurface.isRefractionEnabled || !extension.dispersion) { + return Promise.resolve(); + } + (babylonMaterial as PBRMaterial).subSurface.isDispersionEnabled = true; + (babylonMaterial as PBRMaterial).subSurface.dispersion = extension.dispersion; } - babylonMaterial.subSurface.isDispersionEnabled = true; - babylonMaterial.subSurface.dispersion = extension.dispersion; return Promise.resolve(); } } diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_emissive_strength.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_emissive_strength.ts index b2c0e1a0e41..c95d370d2b2 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_emissive_strength.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_emissive_strength.ts @@ -1,5 +1,6 @@ import type { Nullable } from "core/types"; -import { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { OpenPBRMaterial } from "core/Materials/PBR/openPbrMaterial"; import type { Material } from "core/Materials/material"; import type { IMaterial } from "../glTFLoaderInterfaces"; @@ -21,6 +22,8 @@ declare module "../../glTFFileLoader" { } } +let PBRMaterialClass: typeof PBRMaterial | typeof OpenPBRMaterial; + /** * [Specification](https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_emissive_strength/README.md) */ @@ -60,22 +63,32 @@ export class KHR_materials_emissive_strength implements IGLTFLoaderExtension { * @internal */ // eslint-disable-next-line no-restricted-syntax - public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material): Nullable> { + public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material, useOpenPBR: boolean = false): Nullable> { return GLTFLoader.LoadExtensionAsync(context, material, this.name, async (extensionContext, extension) => { - // eslint-disable-next-line github/no-then - return await this._loader.loadMaterialPropertiesAsync(context, material, babylonMaterial).then(() => { - this._loadEmissiveProperties(extensionContext, extension, babylonMaterial); - }); + await this._loader.loadMaterialPropertiesAsync(context, material, babylonMaterial); + if (useOpenPBR) { + const mod = await import("core/Materials/PBR/openPbrMaterial"); + PBRMaterialClass = mod.OpenPBRMaterial; + } else { + const mod = await import("core/Materials/PBR/pbrMaterial"); + PBRMaterialClass = mod.PBRMaterial; + } + this._loadEmissiveProperties(extensionContext, extension, babylonMaterial, useOpenPBR); + return await Promise.resolve(); }); } - private _loadEmissiveProperties(context: string, properties: IKHRMaterialsEmissiveStrength, babylonMaterial: Material): void { - if (!(babylonMaterial instanceof PBRMaterial)) { + private _loadEmissiveProperties(context: string, properties: IKHRMaterialsEmissiveStrength, babylonMaterial: Material, useOpenPBR: boolean): void { + if (!(babylonMaterial instanceof PBRMaterialClass)) { throw new Error(`${context}: Material type not supported`); } if (properties.emissiveStrength !== undefined) { - babylonMaterial.emissiveIntensity = properties.emissiveStrength; + if (useOpenPBR) { + (babylonMaterial as OpenPBRMaterial).emissionLuminance = properties.emissiveStrength; + } else { + (babylonMaterial as PBRMaterial).emissiveIntensity = properties.emissiveStrength; + } } } } diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_ior.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_ior.ts index ad70f99ba58..08a06b1ab9d 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_ior.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_ior.ts @@ -1,5 +1,6 @@ import type { Nullable } from "core/types"; -import { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { OpenPBRMaterial } from "core/Materials/PBR/openPbrMaterial"; import type { Material } from "core/Materials/material"; import type { IMaterial } from "../glTFLoaderInterfaces"; @@ -21,6 +22,8 @@ declare module "../../glTFFileLoader" { } } +let PBRMaterialClass: typeof PBRMaterial | typeof OpenPBRMaterial; + /** * [Specification](https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_ior/README.md) */ @@ -65,26 +68,38 @@ export class KHR_materials_ior implements IGLTFLoaderExtension { * @internal */ // eslint-disable-next-line no-restricted-syntax - public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material): Nullable> { + public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material, useOpenPBR: boolean = false): Nullable> { return GLTFLoader.LoadExtensionAsync(context, material, this.name, async (extensionContext, extension) => { const promises = new Array>(); promises.push(this._loader.loadMaterialPropertiesAsync(context, material, babylonMaterial)); - promises.push(this._loadIorPropertiesAsync(extensionContext, extension, babylonMaterial)); + if (useOpenPBR) { + const mod = await import("core/Materials/PBR/openPbrMaterial"); + PBRMaterialClass = mod.OpenPBRMaterial; + } else { + const mod = await import("core/Materials/PBR/pbrMaterial"); + PBRMaterialClass = mod.PBRMaterial; + } + promises.push(this._loadIorPropertiesAsync(extensionContext, extension, babylonMaterial, useOpenPBR)); // eslint-disable-next-line github/no-then return await Promise.all(promises).then(() => {}); }); } // eslint-disable-next-line @typescript-eslint/promise-function-async, no-restricted-syntax - private _loadIorPropertiesAsync(context: string, properties: IKHRMaterialsIor, babylonMaterial: Material): Promise { - if (!(babylonMaterial instanceof PBRMaterial)) { + private _loadIorPropertiesAsync(context: string, properties: IKHRMaterialsIor, babylonMaterial: Material, useOpenPBR: boolean = false): Promise { + if (!(babylonMaterial instanceof PBRMaterialClass)) { throw new Error(`${context}: Material type not supported`); } + let indexOfRefraction = 1.5; if (properties.ior !== undefined) { - babylonMaterial.indexOfRefraction = properties.ior; + indexOfRefraction = properties.ior; } else { - babylonMaterial.indexOfRefraction = KHR_materials_ior._DEFAULT_IOR; + indexOfRefraction = KHR_materials_ior._DEFAULT_IOR; + } + + if (!useOpenPBR) { + (babylonMaterial as PBRMaterial).indexOfRefraction = indexOfRefraction; } return Promise.resolve(); diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_iridescence.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_iridescence.ts index 11168af02da..5bd277b1e6f 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_iridescence.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_iridescence.ts @@ -1,6 +1,8 @@ import type { Nullable } from "core/types"; -import { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { OpenPBRMaterial } from "core/Materials/PBR/openPbrMaterial"; import type { Material } from "core/Materials/material"; +import type { BaseTexture } from "core/Materials/Textures/baseTexture"; import type { IMaterial } from "../glTFLoaderInterfaces"; import type { IGLTFLoaderExtension } from "../glTFLoaderExtension"; @@ -21,6 +23,8 @@ declare module "../../glTFFileLoader" { } } +let PBRMaterialClass: typeof PBRMaterial | typeof OpenPBRMaterial; + /** * [Specification](https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_iridescence/README.md) */ @@ -60,36 +64,48 @@ export class KHR_materials_iridescence implements IGLTFLoaderExtension { * @internal */ // eslint-disable-next-line no-restricted-syntax - public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material): Nullable> { + public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material, useOpenPBR: boolean = false): Nullable> { return GLTFLoader.LoadExtensionAsync(context, material, this.name, async (extensionContext, extension) => { const promises = new Array>(); promises.push(this._loader.loadMaterialPropertiesAsync(context, material, babylonMaterial)); - promises.push(this._loadIridescencePropertiesAsync(extensionContext, extension, babylonMaterial)); + if (useOpenPBR) { + const mod = await import("core/Materials/PBR/openPbrMaterial"); + PBRMaterialClass = mod.OpenPBRMaterial; + } else { + const mod = await import("core/Materials/PBR/pbrMaterial"); + PBRMaterialClass = mod.PBRMaterial; + } + promises.push(this._loadIridescencePropertiesAsync(extensionContext, extension, babylonMaterial, useOpenPBR)); // eslint-disable-next-line github/no-then return await Promise.all(promises).then(() => {}); }); } // eslint-disable-next-line @typescript-eslint/promise-function-async, no-restricted-syntax - private _loadIridescencePropertiesAsync(context: string, properties: IKHRMaterialsIridescence, babylonMaterial: Material): Promise { - if (!(babylonMaterial instanceof PBRMaterial)) { + private _loadIridescencePropertiesAsync(context: string, properties: IKHRMaterialsIridescence, babylonMaterial: Material, useOpenPBR: boolean = false): Promise { + if (!(babylonMaterial instanceof PBRMaterialClass)) { throw new Error(`${context}: Material type not supported`); } - const promises = new Array>(); + let iridescenceWeight = 0.0; + let iridescenceWeightTexture: Nullable = null; + let iridescenceIor = 1.3; + let iridescenceThicknessMinimum = 100; + let iridescenceThicknessMaximum = 400; + let iridescenceThicknessTexture: Nullable = null; - babylonMaterial.iridescence.isEnabled = true; + const promises = new Array>(); - babylonMaterial.iridescence.intensity = properties.iridescenceFactor ?? 0; - babylonMaterial.iridescence.indexOfRefraction = properties.iridescenceIor ?? (properties as any).iridescenceIOR ?? 1.3; - babylonMaterial.iridescence.minimumThickness = properties.iridescenceThicknessMinimum ?? 100; - babylonMaterial.iridescence.maximumThickness = properties.iridescenceThicknessMaximum ?? 400; + iridescenceWeight = properties.iridescenceFactor ?? 0; + iridescenceIor = properties.iridescenceIor ?? (properties as any).iridescenceIOR ?? 1.3; + iridescenceThicknessMinimum = properties.iridescenceThicknessMinimum ?? 100; + iridescenceThicknessMaximum = properties.iridescenceThicknessMaximum ?? 400; if (properties.iridescenceTexture) { promises.push( this._loader.loadTextureInfoAsync(`${context}/iridescenceTexture`, properties.iridescenceTexture, (texture) => { texture.name = `${babylonMaterial.name} (Iridescence)`; - babylonMaterial.iridescence.texture = texture; + iridescenceWeightTexture = texture; }) ); } @@ -98,13 +114,23 @@ export class KHR_materials_iridescence implements IGLTFLoaderExtension { promises.push( this._loader.loadTextureInfoAsync(`${context}/iridescenceThicknessTexture`, properties.iridescenceThicknessTexture, (texture) => { texture.name = `${babylonMaterial.name} (Iridescence Thickness)`; - babylonMaterial.iridescence.thicknessTexture = texture; + iridescenceThicknessTexture = texture; }) ); } // eslint-disable-next-line github/no-then - return Promise.all(promises).then(() => {}); + return Promise.all(promises).then(() => { + const pbrMaterial = babylonMaterial as PBRMaterial; + pbrMaterial.iridescence.isEnabled = true; + + pbrMaterial.iridescence.intensity = iridescenceWeight; + pbrMaterial.iridescence.indexOfRefraction = iridescenceIor; + pbrMaterial.iridescence.minimumThickness = iridescenceThicknessMinimum; + pbrMaterial.iridescence.maximumThickness = iridescenceThicknessMaximum; + pbrMaterial.iridescence.texture = iridescenceWeightTexture; + pbrMaterial.iridescence.thicknessTexture = iridescenceThicknessTexture; + }); } } diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_sheen.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_sheen.ts index 4816792a5e3..cea6ceb4f3c 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_sheen.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_sheen.ts @@ -1,6 +1,8 @@ import type { Nullable } from "core/types"; -import { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { OpenPBRMaterial } from "core/Materials/PBR/openPbrMaterial"; import type { Material } from "core/Materials/material"; +import type { BaseTexture } from "core/Materials/Textures/baseTexture"; import type { IMaterial, ITextureInfo } from "../glTFLoaderInterfaces"; import type { IGLTFLoaderExtension } from "../glTFLoaderExtension"; @@ -22,6 +24,8 @@ declare module "../../glTFFileLoader" { } } +let PBRMaterialClass: typeof PBRMaterial | typeof OpenPBRMaterial; + /** * [Specification](https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_sheen/README.md) * [Playground Sample](https://www.babylonjs-playground.com/frame.html#BNIZX6#4) @@ -62,46 +66,51 @@ export class KHR_materials_sheen implements IGLTFLoaderExtension { * @internal */ // eslint-disable-next-line no-restricted-syntax - public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material): Nullable> { + public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material, useOpenPBR: boolean = false): Nullable> { return GLTFLoader.LoadExtensionAsync(context, material, this.name, async (extensionContext, extension) => { const promises = new Array>(); promises.push(this._loader.loadMaterialPropertiesAsync(context, material, babylonMaterial)); - promises.push(this._loadSheenPropertiesAsync(extensionContext, extension, babylonMaterial)); + if (useOpenPBR) { + const mod = await import("core/Materials/PBR/openPbrMaterial"); + PBRMaterialClass = mod.OpenPBRMaterial; + } else { + const mod = await import("core/Materials/PBR/pbrMaterial"); + PBRMaterialClass = mod.PBRMaterial; + } + promises.push(this._loadSheenPropertiesAsync(extensionContext, extension, babylonMaterial, useOpenPBR)); // eslint-disable-next-line github/no-then return await Promise.all(promises).then(() => {}); }); } // eslint-disable-next-line @typescript-eslint/promise-function-async, no-restricted-syntax - private _loadSheenPropertiesAsync(context: string, properties: IKHRMaterialsSheen, babylonMaterial: Material): Promise { - if (!(babylonMaterial instanceof PBRMaterial)) { + private _loadSheenPropertiesAsync(context: string, properties: IKHRMaterialsSheen, babylonMaterial: Material, useOpenPBR: boolean): Promise { + if (!(babylonMaterial instanceof PBRMaterialClass)) { throw new Error(`${context}: Material type not supported`); } - const promises = new Array>(); + let sheenColor = Color3.Black(); + let sheenColorTexture: Nullable = null; + let sheenRoughness = 0.0; + let sheenRoughnessTexture: Nullable = null; - babylonMaterial.sheen.isEnabled = true; - babylonMaterial.sheen.intensity = 1; + const promises = new Array>(); if (properties.sheenColorFactor != undefined) { - babylonMaterial.sheen.color = Color3.FromArray(properties.sheenColorFactor); - } else { - babylonMaterial.sheen.color = Color3.Black(); + sheenColor = Color3.FromArray(properties.sheenColorFactor); } if (properties.sheenColorTexture) { promises.push( this._loader.loadTextureInfoAsync(`${context}/sheenColorTexture`, properties.sheenColorTexture, (texture) => { texture.name = `${babylonMaterial.name} (Sheen Color)`; - babylonMaterial.sheen.texture = texture; + sheenColorTexture = texture; }) ); } if (properties.sheenRoughnessFactor !== undefined) { - babylonMaterial.sheen.roughness = properties.sheenRoughnessFactor; - } else { - babylonMaterial.sheen.roughness = 0; + sheenRoughness = properties.sheenRoughnessFactor; } if (properties.sheenRoughnessTexture) { @@ -109,16 +118,26 @@ export class KHR_materials_sheen implements IGLTFLoaderExtension { promises.push( this._loader.loadTextureInfoAsync(`${context}/sheenRoughnessTexture`, properties.sheenRoughnessTexture, (texture) => { texture.name = `${babylonMaterial.name} (Sheen Roughness)`; - babylonMaterial.sheen.textureRoughness = texture; + sheenRoughnessTexture = texture; }) ); } - babylonMaterial.sheen.albedoScaling = true; - babylonMaterial.sheen.useRoughnessFromMainTexture = false; - // eslint-disable-next-line github/no-then - return Promise.all(promises).then(() => {}); + return Promise.all(promises).then(() => { + if (useOpenPBR) { + return; + } + const pbrMaterial = babylonMaterial as PBRMaterial; + pbrMaterial.sheen.isEnabled = true; + pbrMaterial.sheen.intensity = 1; + pbrMaterial.sheen.color = sheenColor; + pbrMaterial.sheen.texture = sheenColorTexture; + pbrMaterial.sheen.roughness = sheenRoughness; + pbrMaterial.sheen.textureRoughness = sheenRoughnessTexture; + pbrMaterial.sheen.albedoScaling = true; + pbrMaterial.sheen.useRoughnessFromMainTexture = false; + }); } } diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_specular.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_specular.ts index 38cbf494cd9..9cc95e649e5 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_specular.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_specular.ts @@ -1,5 +1,6 @@ import type { Nullable } from "core/types"; -import { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { OpenPBRMaterial } from "core/Materials/PBR/openPbrMaterial"; import type { Material } from "core/Materials/material"; import type { IMaterial, ITextureInfo } from "../glTFLoaderInterfaces"; @@ -23,6 +24,8 @@ declare module "../../glTFFileLoader" { } } +let PBRMaterialClass: typeof PBRMaterial | typeof OpenPBRMaterial; + /** * [Specification](https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_specular/README.md) */ @@ -62,18 +65,25 @@ export class KHR_materials_specular implements IGLTFLoaderExtension { * @internal */ // eslint-disable-next-line no-restricted-syntax - public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material): Nullable> { + public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material, useOpenPBR: boolean = false): Nullable> { return GLTFLoader.LoadExtensionAsync(context, material, this.name, async (extensionContext, extension) => { const promises = new Array>(); promises.push(this._loader.loadMaterialPropertiesAsync(context, material, babylonMaterial)); - promises.push(this._loadSpecularPropertiesAsync(extensionContext, extension, babylonMaterial)); + if (useOpenPBR) { + const mod = await import("core/Materials/PBR/openPbrMaterial"); + PBRMaterialClass = mod.OpenPBRMaterial; + } else { + const mod = await import("core/Materials/PBR/pbrMaterial"); + PBRMaterialClass = mod.PBRMaterial; + } + promises.push(this._loadSpecularPropertiesAsync(extensionContext, extension, babylonMaterial, useOpenPBR)); // Handle the EXT_materials_specular_edge_color sub-extension // https://github.com/KhronosGroup/glTF/blob/2a1111b88f052cbd3e2d82abb9faee56e7494904/extensions/2.0/Vendor/EXT_materials_specular_edge_color/README.md - if (extension.extensions && extension.extensions.EXT_materials_specular_edge_color && babylonMaterial instanceof PBRMaterial) { + if (!useOpenPBR && extension.extensions && extension.extensions.EXT_materials_specular_edge_color) { const specularEdgeColorExtension = extension.extensions.EXT_materials_specular_edge_color as IEXTMaterialsSpecularEdgeColor; if (specularEdgeColorExtension.specularEdgeColorEnabled) { - babylonMaterial.brdf.dielectricSpecularModel = Constants.MATERIAL_DIELECTRIC_SPECULAR_MODEL_OPENPBR; - babylonMaterial.brdf.conductorSpecularModel = Constants.MATERIAL_CONDUCTOR_SPECULAR_MODEL_OPENPBR; + (babylonMaterial as PBRMaterial).brdf.dielectricSpecularModel = Constants.MATERIAL_DIELECTRIC_SPECULAR_MODEL_OPENPBR; + (babylonMaterial as PBRMaterial).brdf.conductorSpecularModel = Constants.MATERIAL_CONDUCTOR_SPECULAR_MODEL_OPENPBR; } } // eslint-disable-next-line github/no-then @@ -82,39 +92,70 @@ export class KHR_materials_specular implements IGLTFLoaderExtension { } // eslint-disable-next-line @typescript-eslint/promise-function-async, no-restricted-syntax - private _loadSpecularPropertiesAsync(context: string, properties: IKHRMaterialsSpecular, babylonMaterial: Material): Promise { - if (!(babylonMaterial instanceof PBRMaterial)) { + private _loadSpecularPropertiesAsync(context: string, properties: IKHRMaterialsSpecular, babylonMaterial: Material, useOpenPBR: boolean): Promise { + if (!(babylonMaterial instanceof PBRMaterialClass)) { throw new Error(`${context}: Material type not supported`); } const promises = new Array>(); + promises.push(Promise.resolve()); - if (properties.specularFactor !== undefined) { - babylonMaterial.metallicF0Factor = properties.specularFactor; - } + if (useOpenPBR) { + if (properties.specularFactor !== undefined) { + (babylonMaterial as OpenPBRMaterial).specularWeight = properties.specularFactor; + } - if (properties.specularColorFactor !== undefined) { - babylonMaterial.metallicReflectanceColor = Color3.FromArray(properties.specularColorFactor); - } + if (properties.specularColorFactor !== undefined) { + (babylonMaterial as OpenPBRMaterial).specularColor = Color3.FromArray(properties.specularColorFactor); + } - if (properties.specularTexture) { - (properties.specularTexture as ITextureInfo).nonColorData = true; - promises.push( - this._loader.loadTextureInfoAsync(`${context}/specularTexture`, properties.specularTexture, (texture) => { - texture.name = `${babylonMaterial.name} (Specular)`; - babylonMaterial.metallicReflectanceTexture = texture; - babylonMaterial.useOnlyMetallicFromMetallicReflectanceTexture = true; - }) - ); - } + if (properties.specularTexture) { + (properties.specularTexture as ITextureInfo).nonColorData = true; + promises.push( + this._loader.loadTextureInfoAsync(`${context}/specularTexture`, properties.specularTexture, (texture) => { + texture.name = `${babylonMaterial.name} (Specular)`; + (babylonMaterial as OpenPBRMaterial).specularWeightTexture = texture; + (babylonMaterial as OpenPBRMaterial).useSpecularWeightFromTextureAlpha = true; + }) + ); + } - if (properties.specularColorTexture) { - promises.push( - this._loader.loadTextureInfoAsync(`${context}/specularColorTexture`, properties.specularColorTexture, (texture) => { - texture.name = `${babylonMaterial.name} (Specular Color)`; - babylonMaterial.reflectanceTexture = texture; - }) - ); + if (properties.specularColorTexture) { + promises.push( + this._loader.loadTextureInfoAsync(`${context}/specularColorTexture`, properties.specularColorTexture, (texture) => { + texture.name = `${babylonMaterial.name} (Specular Color)`; + (babylonMaterial as OpenPBRMaterial).specularColorTexture = texture; + }) + ); + } + } else { + if (properties.specularFactor !== undefined) { + (babylonMaterial as PBRMaterial).metallicF0Factor = properties.specularFactor; + } + + if (properties.specularColorFactor !== undefined) { + (babylonMaterial as PBRMaterial).metallicReflectanceColor = Color3.FromArray(properties.specularColorFactor); + } + + if (properties.specularTexture) { + (properties.specularTexture as ITextureInfo).nonColorData = true; + promises.push( + this._loader.loadTextureInfoAsync(`${context}/specularTexture`, properties.specularTexture, (texture) => { + texture.name = `${babylonMaterial.name} (Specular)`; + (babylonMaterial as PBRMaterial).metallicReflectanceTexture = texture; + (babylonMaterial as PBRMaterial).useOnlyMetallicFromMetallicReflectanceTexture = true; + }) + ); + } + + if (properties.specularColorTexture) { + promises.push( + this._loader.loadTextureInfoAsync(`${context}/specularColorTexture`, properties.specularColorTexture, (texture) => { + texture.name = `${babylonMaterial.name} (Specular Color)`; + (babylonMaterial as PBRMaterial).reflectanceTexture = texture; + }) + ); + } } // eslint-disable-next-line github/no-then diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_transmission.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_transmission.ts index 5f91a41d555..e66b068e19d 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_transmission.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_transmission.ts @@ -1,5 +1,6 @@ import type { Nullable } from "core/types"; -import { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { OpenPBRMaterial } from "core/Materials/PBR/openPbrMaterial"; import type { Material } from "core/Materials/material"; import type { BaseTexture } from "core/Materials/Textures/baseTexture"; import type { IMaterial, ITextureInfo } from "../glTFLoaderInterfaces"; @@ -62,6 +63,8 @@ interface ITransmissionHelperOptions { clearColor?: Color4; } +let PBRMaterialClass: typeof PBRMaterial | typeof OpenPBRMaterial; + /** * A class to handle setting up the rendering of opaque objects to be shown through transmissive objects. */ @@ -166,7 +169,7 @@ class TransmissionHelper { if (!material) { return false; } - if (material instanceof PBRMaterial && material.subSurface.isRefractionEnabled) { + if (material instanceof PBRMaterialClass && (material as any).subSurface.isRefractionEnabled) { return true; } return false; @@ -220,8 +223,8 @@ class TransmissionHelper { // If the material is transparent, make sure that it's added to the transparent list and removed from the opaque list const useTransmission = this._shouldRenderAsTransmission(mesh.material); if (useTransmission) { - if (mesh.material instanceof PBRMaterial) { - mesh.material.subSurface.refractionTexture = this._opaqueRenderTarget; + if (mesh.material instanceof PBRMaterialClass) { + (mesh.material as any).subSurface.refractionTexture = this._opaqueRenderTarget; } if (opaqueIdx !== -1) { this._opaqueMeshesCache.splice(opaqueIdx, 1); @@ -366,25 +369,60 @@ export class KHR_materials_transmission implements IGLTFLoaderExtension { * @internal */ // eslint-disable-next-line no-restricted-syntax - public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material): Nullable> { + public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material, useOpenPBR: boolean = false): Nullable> { return GLTFLoader.LoadExtensionAsync(context, material, this.name, async (extensionContext, extension) => { const promises = new Array>(); promises.push(this._loader.loadMaterialPropertiesAsync(context, material, babylonMaterial)); - promises.push(this._loadTransparentPropertiesAsync(extensionContext, material, babylonMaterial, extension)); + if (useOpenPBR) { + const mod = await import("core/Materials/PBR/openPbrMaterial"); + PBRMaterialClass = mod.OpenPBRMaterial; + } else { + const mod = await import("core/Materials/PBR/pbrMaterial"); + PBRMaterialClass = mod.PBRMaterial; + } + promises.push(this._loadTransparentPropertiesAsync(extensionContext, material, babylonMaterial, extension, useOpenPBR)); // eslint-disable-next-line github/no-then return await Promise.all(promises).then(() => {}); }); } // eslint-disable-next-line no-restricted-syntax, @typescript-eslint/promise-function-async - private _loadTransparentPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material, extension: IKHRMaterialsTransmission): Promise { - if (!(babylonMaterial instanceof PBRMaterial)) { + private _loadTransparentPropertiesAsync( + context: string, + material: IMaterial, + babylonMaterial: Material, + extension: IKHRMaterialsTransmission, + useOpenPBR: boolean + ): Promise { + if (!(babylonMaterial instanceof PBRMaterialClass)) { throw new Error(`${context}: Material type not supported`); } - const pbrMaterial = babylonMaterial; + if (useOpenPBR) { + return Promise.resolve(); + } + + let transmissionWeight = 0.0; + let transmissionWeightTexture: Nullable = null; + + let texturePromise = new Promise>((resolve) => resolve(null)); + if (extension.transmissionFactor !== undefined) { + transmissionWeight = extension.transmissionFactor; + } else { + return Promise.resolve(); + } + if (extension.transmissionTexture) { + (extension.transmissionTexture as ITextureInfo).nonColorData = true; + // eslint-disable-next-line github/no-then + texturePromise = this._loader.loadTextureInfoAsync(`${context}/transmissionTexture`, extension.transmissionTexture, (texture: BaseTexture) => { + texture.name = `${babylonMaterial.name} (Transmission)`; + transmissionWeightTexture = texture; + }); + } + + const pbrMaterial = babylonMaterial as PBRMaterial; // Enables "refraction" texture which represents transmitted light. - pbrMaterial.subSurface.isRefractionEnabled = true; + pbrMaterial.subSurface.isRefractionEnabled = transmissionWeight !== 0; // Since this extension models thin-surface transmission only, we must make IOR = 1.0 pbrMaterial.subSurface.volumeIndexOfRefraction = 1.0; @@ -392,8 +430,9 @@ export class KHR_materials_transmission implements IGLTFLoaderExtension { // Albedo colour will tint transmission. pbrMaterial.subSurface.useAlbedoToTintRefraction = true; - if (extension.transmissionFactor !== undefined) { - pbrMaterial.subSurface.refractionIntensity = extension.transmissionFactor; + pbrMaterial.subSurface.refractionIntensity = transmissionWeight; + + if (transmissionWeight) { const scene = pbrMaterial.getScene() as unknown as ITransmissionHelperHolder; if (pbrMaterial.subSurface.refractionIntensity && !scene._transmissionHelper) { new TransmissionHelper({}, pbrMaterial.getScene()); @@ -401,25 +440,16 @@ export class KHR_materials_transmission implements IGLTFLoaderExtension { // If the render target is not valid, recreate it. scene._transmissionHelper?._setupRenderTargets(); } - } else { - pbrMaterial.subSurface.refractionIntensity = 0.0; - pbrMaterial.subSurface.isRefractionEnabled = false; - return Promise.resolve(); } pbrMaterial.subSurface.minimumThickness = 0.0; pbrMaterial.subSurface.maximumThickness = 0.0; - if (extension.transmissionTexture) { - (extension.transmissionTexture as ITextureInfo).nonColorData = true; - // eslint-disable-next-line github/no-then - return this._loader.loadTextureInfoAsync(`${context}/transmissionTexture`, extension.transmissionTexture, undefined).then((texture: BaseTexture) => { - texture.name = `${babylonMaterial.name} (Transmission)`; - pbrMaterial.subSurface.refractionIntensityTexture = texture; - pbrMaterial.subSurface.useGltfStyleTextures = true; - }); - } else { - return Promise.resolve(); - } + + // eslint-disable-next-line github/no-then + return texturePromise.then(() => { + pbrMaterial.subSurface.refractionIntensityTexture = transmissionWeightTexture; + pbrMaterial.subSurface.useGltfStyleTextures = true; + }); } } diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_unlit.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_unlit.ts index 1a9609ac33c..056511dbb80 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_unlit.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_unlit.ts @@ -1,6 +1,7 @@ import type { Nullable } from "core/types"; import { Color3 } from "core/Maths/math.color"; -import { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { OpenPBRMaterial } from "core/Materials/PBR/openPbrMaterial"; import type { Material } from "core/Materials/material"; import type { IMaterial } from "../glTFLoaderInterfaces"; @@ -21,6 +22,8 @@ declare module "../../glTFFileLoader" { } } +let PBRMaterialClass: typeof PBRMaterial | typeof OpenPBRMaterial; + /** * [Specification](https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_unlit/README.md) */ @@ -60,40 +63,58 @@ export class KHR_materials_unlit implements IGLTFLoaderExtension { * @internal */ // eslint-disable-next-line no-restricted-syntax - public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material): Nullable> { + public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material, useOpenPBR: boolean = false): Nullable> { return GLTFLoader.LoadExtensionAsync(context, material, this.name, async () => { - return await this._loadUnlitPropertiesAsync(context, material, babylonMaterial); + if (useOpenPBR) { + const mod = await import("core/Materials/PBR/openPbrMaterial"); + PBRMaterialClass = mod.OpenPBRMaterial; + } else { + const mod = await import("core/Materials/PBR/pbrMaterial"); + PBRMaterialClass = mod.PBRMaterial; + } + return await this._loadUnlitPropertiesAsync(context, material, babylonMaterial, useOpenPBR); }); } // eslint-disable-next-line @typescript-eslint/promise-function-async, no-restricted-syntax - private _loadUnlitPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material): Promise { - if (!(babylonMaterial instanceof PBRMaterial)) { + private _loadUnlitPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material, useOpenPBR: boolean = false): Promise { + if (!(babylonMaterial instanceof PBRMaterialClass)) { throw new Error(`${context}: Material type not supported`); } const promises = new Array>(); babylonMaterial.unlit = true; - + let baseColor: Color3 = Color3.White(); + let alpha: number = 1; const properties = material.pbrMetallicRoughness; if (properties) { if (properties.baseColorFactor) { - babylonMaterial.albedoColor = Color3.FromArray(properties.baseColorFactor); - babylonMaterial.alpha = properties.baseColorFactor[3]; - } else { - babylonMaterial.albedoColor = Color3.White(); + baseColor = Color3.FromArray(properties.baseColorFactor); + alpha = properties.baseColorFactor[3]; } if (properties.baseColorTexture) { promises.push( this._loader.loadTextureInfoAsync(`${context}/baseColorTexture`, properties.baseColorTexture, (texture) => { texture.name = `${babylonMaterial.name} (Base Color)`; - babylonMaterial.albedoTexture = texture; + if (useOpenPBR) { + (babylonMaterial as OpenPBRMaterial).baseColorTexture = texture; + } else { + (babylonMaterial as PBRMaterial).albedoTexture = texture; + } }) ); } } + if (useOpenPBR) { + (babylonMaterial as OpenPBRMaterial).baseColor = baseColor; + (babylonMaterial as OpenPBRMaterial).geometryOpacity = alpha; + } else { + (babylonMaterial as PBRMaterial).albedoColor = baseColor; + (babylonMaterial as PBRMaterial).alpha = alpha; + } + if (material.doubleSided) { babylonMaterial.backFaceCulling = false; babylonMaterial.twoSidedLighting = true; diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_volume.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_volume.ts index 349cbd67c10..f3e39c352d4 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_volume.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/KHR_materials_volume.ts @@ -1,7 +1,9 @@ /* eslint-disable @typescript-eslint/naming-convention */ import type { Nullable } from "core/types"; -import { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { OpenPBRMaterial } from "core/Materials/PBR/openPbrMaterial"; import type { Material } from "core/Materials/material"; +import { Color3 } from "core/Maths/math.color"; import type { BaseTexture } from "core/Materials/Textures/baseTexture"; import type { IMaterial, ITextureInfo } from "../glTFLoaderInterfaces"; import type { IGLTFLoaderExtension } from "../glTFLoaderExtension"; @@ -22,6 +24,8 @@ declare module "../../glTFFileLoader" { } } +let PBRMaterialClass: typeof PBRMaterial | typeof OpenPBRMaterial; + /** * [Specification](https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_volume/README.md) * @since 5.0.0 @@ -69,50 +73,85 @@ export class KHR_materials_volume implements IGLTFLoaderExtension { * @internal */ // eslint-disable-next-line no-restricted-syntax - public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material): Nullable> { + public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material, useOpenPBR: boolean = false): Nullable> { return GLTFLoader.LoadExtensionAsync(context, material, this.name, async (extensionContext, extension) => { const promises = new Array>(); promises.push(this._loader.loadMaterialPropertiesAsync(context, material, babylonMaterial)); - promises.push(this._loadVolumePropertiesAsync(extensionContext, material, babylonMaterial, extension)); + if (useOpenPBR) { + const mod = await import("core/Materials/PBR/openPbrMaterial"); + PBRMaterialClass = mod.OpenPBRMaterial; + } else { + const mod = await import("core/Materials/PBR/pbrMaterial"); + PBRMaterialClass = mod.PBRMaterial; + } + promises.push(this._loadVolumePropertiesAsync(extensionContext, material, babylonMaterial, extension, useOpenPBR)); // eslint-disable-next-line github/no-then return await Promise.all(promises).then(() => {}); }); } // eslint-disable-next-line @typescript-eslint/promise-function-async, no-restricted-syntax - private _loadVolumePropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material, extension: IKHRMaterialsVolume): Promise { - if (!(babylonMaterial instanceof PBRMaterial)) { + private _loadVolumePropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material, extension: IKHRMaterialsVolume, useOpenPBR: boolean): Promise { + if (!(babylonMaterial instanceof PBRMaterialClass)) { throw new Error(`${context}: Material type not supported`); } + if (useOpenPBR) { + return Promise.resolve(); + } // If transparency isn't enabled already, this extension shouldn't do anything. // i.e. it requires either the KHR_materials_transmission or KHR_materials_diffuse_transmission extensions. - if ((!babylonMaterial.subSurface.isRefractionEnabled && !babylonMaterial.subSurface.isTranslucencyEnabled) || !extension.thicknessFactor) { + if ( + (!(babylonMaterial as PBRMaterial).subSurface.isRefractionEnabled && !(babylonMaterial as PBRMaterial).subSurface.isTranslucencyEnabled) || + !extension.thicknessFactor + ) { return Promise.resolve(); } - // IOR in this extension only affects interior. - babylonMaterial.subSurface.volumeIndexOfRefraction = babylonMaterial.indexOfRefraction; - const attenuationDistance = extension.attenuationDistance !== undefined ? extension.attenuationDistance : Number.MAX_VALUE; - babylonMaterial.subSurface.tintColorAtDistance = attenuationDistance; + let attenuationDistance = Number.MAX_VALUE; + const attenuationColor: Color3 = Color3.White(); + let thicknessTexture: Nullable = null; + let thicknessFactor = 0.0; + + const promises = new Array>(); + + attenuationDistance = extension.attenuationDistance !== undefined ? extension.attenuationDistance : Number.MAX_VALUE; if (extension.attenuationColor !== undefined && extension.attenuationColor.length == 3) { - babylonMaterial.subSurface.tintColor.copyFromFloats(extension.attenuationColor[0], extension.attenuationColor[1], extension.attenuationColor[2]); + attenuationColor.copyFromFloats(extension.attenuationColor[0], extension.attenuationColor[1], extension.attenuationColor[2]); } - babylonMaterial.subSurface.minimumThickness = 0.0; - babylonMaterial.subSurface.maximumThickness = extension.thicknessFactor; - babylonMaterial.subSurface.useThicknessAsDepth = true; + thicknessFactor = extension.thicknessFactor; if (extension.thicknessTexture) { (extension.thicknessTexture as ITextureInfo).nonColorData = true; // eslint-disable-next-line github/no-then - return this._loader.loadTextureInfoAsync(`${context}/thicknessTexture`, extension.thicknessTexture).then((texture: BaseTexture) => { - texture.name = `${babylonMaterial.name} (Thickness)`; - babylonMaterial.subSurface.thicknessTexture = texture; - babylonMaterial.subSurface.useGltfStyleTextures = true; - }); - } else { - return Promise.resolve(); + promises.push( + this._loader.loadTextureInfoAsync(`${context}/thicknessTexture`, extension.thicknessTexture, (texture: BaseTexture) => { + texture.name = `${babylonMaterial.name} (Thickness)`; + thicknessTexture = texture; + }) + ); } + + // eslint-disable-next-line github/no-then + return Promise.all(promises).then(() => { + if (useOpenPBR) { + return; + } + + const pbrMaterial = babylonMaterial as PBRMaterial; + // IOR in this extension only affects interior. + pbrMaterial.subSurface.volumeIndexOfRefraction = pbrMaterial.indexOfRefraction; + pbrMaterial.subSurface.tintColorAtDistance = attenuationDistance; + pbrMaterial.subSurface.tintColor = attenuationColor; + + pbrMaterial.subSurface.minimumThickness = 0.0; + pbrMaterial.subSurface.maximumThickness = thicknessFactor; + pbrMaterial.subSurface.useThicknessAsDepth = true; + if (thicknessTexture) { + pbrMaterial.subSurface.thicknessTexture = thicknessTexture; + pbrMaterial.subSurface.useGltfStyleTextures = true; + } + }); } } diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/MSFT_minecraftMesh.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/MSFT_minecraftMesh.ts index 84055b9c49b..ecb866e40c5 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/MSFT_minecraftMesh.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/MSFT_minecraftMesh.ts @@ -1,6 +1,7 @@ import type { Nullable } from "core/types"; import type { Material } from "core/Materials/material"; -import { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { OpenPBRMaterial } from "core/Materials/PBR/openPbrMaterial"; import type { IMaterial } from "../glTFLoaderInterfaces"; import type { IGLTFLoaderExtension } from "../glTFLoaderExtension"; @@ -20,6 +21,8 @@ declare module "../../glTFFileLoader" { } } +let PBRMaterialClass: typeof PBRMaterial | typeof OpenPBRMaterial; + /** @internal */ // eslint-disable-next-line @typescript-eslint/naming-convention export class MSFT_minecraftMesh implements IGLTFLoaderExtension { @@ -44,10 +47,17 @@ export class MSFT_minecraftMesh implements IGLTFLoaderExtension { /** @internal */ // eslint-disable-next-line no-restricted-syntax - public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material): Nullable> { + public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material, useOpenPBR: boolean = false): Nullable> { return GLTFLoader.LoadExtraAsync(context, material, this.name, async (extraContext, extra) => { + if (useOpenPBR) { + const mod = await import("core/Materials/PBR/openPbrMaterial"); + PBRMaterialClass = mod.OpenPBRMaterial; + } else { + const mod = await import("core/Materials/PBR/pbrMaterial"); + PBRMaterialClass = mod.PBRMaterial; + } if (extra) { - if (!(babylonMaterial instanceof PBRMaterial)) { + if (!(babylonMaterial instanceof PBRMaterialClass)) { throw new Error(`${extraContext}: Material type not supported`); } diff --git a/packages/dev/loaders/src/glTF/2.0/Extensions/MSFT_sRGBFactors.ts b/packages/dev/loaders/src/glTF/2.0/Extensions/MSFT_sRGBFactors.ts index ab98d23ed09..815545e6e4b 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/MSFT_sRGBFactors.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/MSFT_sRGBFactors.ts @@ -1,6 +1,7 @@ import type { Nullable } from "core/types"; import type { Material } from "core/Materials/material"; -import { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { OpenPBRMaterial } from "core/Materials/PBR/openPbrMaterial"; import type { IMaterial } from "../glTFLoaderInterfaces"; import type { IGLTFLoaderExtension } from "../glTFLoaderExtension"; @@ -20,6 +21,8 @@ declare module "../../glTFFileLoader" { } } +let PBRMaterialClass: typeof PBRMaterial | typeof OpenPBRMaterial; + /** @internal */ // eslint-disable-next-line @typescript-eslint/naming-convention export class MSFT_sRGBFactors implements IGLTFLoaderExtension { @@ -44,22 +47,37 @@ export class MSFT_sRGBFactors implements IGLTFLoaderExtension { /** @internal*/ // eslint-disable-next-line no-restricted-syntax - public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material): Nullable> { + public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material, useOpenPBR: boolean = false): Nullable> { return GLTFLoader.LoadExtraAsync(context, material, this.name, async (extraContext, extra) => { + if (useOpenPBR) { + const mod = await import("core/Materials/PBR/openPbrMaterial"); + PBRMaterialClass = mod.OpenPBRMaterial; + } else { + const mod = await import("core/Materials/PBR/pbrMaterial"); + PBRMaterialClass = mod.PBRMaterial; + } if (extra) { - if (!(babylonMaterial instanceof PBRMaterial)) { + if (!(babylonMaterial instanceof PBRMaterialClass)) { throw new Error(`${extraContext}: Material type not supported`); } const promise = this._loader.loadMaterialPropertiesAsync(context, material, babylonMaterial); const useExactSrgbConversions = babylonMaterial.getScene().getEngine().useExactSrgbConversions; - if (!babylonMaterial.albedoTexture) { - babylonMaterial.albedoColor.toLinearSpaceToRef(babylonMaterial.albedoColor, useExactSrgbConversions); - } - - if (!babylonMaterial.reflectivityTexture) { - babylonMaterial.reflectivityColor.toLinearSpaceToRef(babylonMaterial.reflectivityColor, useExactSrgbConversions); + if (useOpenPBR) { + if (!(babylonMaterial as OpenPBRMaterial).baseColorTexture) { + (babylonMaterial as OpenPBRMaterial).baseColor.toLinearSpaceToRef((babylonMaterial as OpenPBRMaterial).baseColor, useExactSrgbConversions); + } + if (!(babylonMaterial as OpenPBRMaterial).specularColorTexture) { + (babylonMaterial as OpenPBRMaterial).specularColor.toLinearSpaceToRef((babylonMaterial as OpenPBRMaterial).specularColor, useExactSrgbConversions); + } + } else { + if (!(babylonMaterial as PBRMaterial).albedoTexture) { + (babylonMaterial as PBRMaterial).albedoColor.toLinearSpaceToRef((babylonMaterial as PBRMaterial).albedoColor, useExactSrgbConversions); + } + if (!(babylonMaterial as PBRMaterial).reflectivityTexture) { + (babylonMaterial as PBRMaterial).reflectivityColor.toLinearSpaceToRef((babylonMaterial as PBRMaterial).reflectivityColor, useExactSrgbConversions); + } } return await promise; 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 f6d80d8a0c4..b1f9bd36be1 100644 --- a/packages/dev/loaders/src/glTF/2.0/Extensions/index.ts +++ b/packages/dev/loaders/src/glTF/2.0/Extensions/index.ts @@ -11,6 +11,7 @@ export * from "./KHR_lights_punctual"; export * from "./KHR_materials_pbrSpecularGlossiness"; export * from "./KHR_materials_unlit"; export * from "./KHR_materials_clearcoat"; +export * from "./EXT_materials_clearcoat_darkening"; export * from "./KHR_materials_iridescence"; export * from "./KHR_materials_anisotropy"; export * from "./KHR_materials_emissive_strength"; diff --git a/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts b/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts index b199e919678..9d07cf7a397 100644 --- a/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts +++ b/packages/dev/loaders/src/glTF/2.0/glTFLoader.ts @@ -16,7 +16,8 @@ import type { AnimationGroup } from "core/Animations/animationGroup"; import { Bone } from "core/Bones/bone"; import { Skeleton } from "core/Bones/skeleton"; import { Material } from "core/Materials/material"; -import { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { PBRMaterial } from "core/Materials/PBR/pbrMaterial"; +import type { OpenPBRMaterial } from "core/Materials/PBR/openPbrMaterial"; import type { BaseTexture } from "core/Materials/Textures/baseTexture"; import type { ITextureCreationOptions } from "core/Materials/Textures/texture"; import { Texture } from "core/Materials/Textures/texture"; @@ -86,6 +87,8 @@ import { GetTypedArrayConstructor } from "core/Buffers/bufferUtils"; export { GLTFFileLoader }; +let PBRMaterialClass: typeof PBRMaterial | typeof OpenPBRMaterial; + interface ILoaderProperty extends IProperty { _activeLoaderExtensionFunctions: { [id: string]: boolean; @@ -399,6 +402,14 @@ export class GLTFLoader implements IGLTFLoader { await this._loadExtensionsAsync(); + if (this.parent.useOpenPBR) { + const mod = await import("core/Materials/PBR/openPbrMaterial"); + PBRMaterialClass = mod.OpenPBRMaterial; + } else { + const mod = await import("core/Materials/PBR/pbrMaterial"); + PBRMaterialClass = mod.PBRMaterial; + } + const loadingToReadyCounterName = `${GLTFLoaderState[GLTFLoaderState.LOADING]} => ${GLTFLoaderState[GLTFLoaderState.READY]}`; const loadingToCompleteCounterName = `${GLTFLoaderState[GLTFLoaderState.LOADING]} => ${GLTFLoaderState[GLTFLoaderState.COMPLETE]}`; @@ -2127,28 +2138,45 @@ export class GLTFLoader implements IGLTFLoader { } private _loadMaterialMetallicRoughnessPropertiesAsync(context: string, properties: IMaterialPbrMetallicRoughness, babylonMaterial: Material): Promise { - if (!(babylonMaterial instanceof PBRMaterial)) { + if (!(babylonMaterial instanceof PBRMaterialClass)) { throw new Error(`${context}: Material type not supported`); } const promises = new Array>(); if (properties) { - if (properties.baseColorFactor) { - babylonMaterial.albedoColor = Color3.FromArray(properties.baseColorFactor); - babylonMaterial.alpha = properties.baseColorFactor[3]; + if (this.parent.useOpenPBR) { + const mat = babylonMaterial as OpenPBRMaterial; + if (properties.baseColorFactor) { + mat.baseColor = Color3.FromArray(properties.baseColorFactor); + mat.geometryOpacity = properties.baseColorFactor[3]; + } else { + mat.baseColor = Color3.White(); + } + mat.baseMetalness = properties.metallicFactor == undefined ? 1 : properties.metallicFactor; + mat.specularRoughness = properties.roughnessFactor == undefined ? 1 : properties.roughnessFactor; } else { - babylonMaterial.albedoColor = Color3.White(); + const mat = babylonMaterial as PBRMaterial; + if (properties.baseColorFactor) { + mat.albedoColor = Color3.FromArray(properties.baseColorFactor); + mat.alpha = properties.baseColorFactor[3]; + } else { + mat.albedoColor = Color3.White(); + } + mat.metallic = properties.metallicFactor == undefined ? 1 : properties.metallicFactor; + mat.roughness = properties.roughnessFactor == undefined ? 1 : properties.roughnessFactor; + babylonMaterial = mat; } - babylonMaterial.metallic = properties.metallicFactor == undefined ? 1 : properties.metallicFactor; - babylonMaterial.roughness = properties.roughnessFactor == undefined ? 1 : properties.roughnessFactor; - if (properties.baseColorTexture) { promises.push( this.loadTextureInfoAsync(`${context}/baseColorTexture`, properties.baseColorTexture, (texture) => { texture.name = `${babylonMaterial.name} (Base Color)`; - babylonMaterial.albedoTexture = texture; + if (this.parent.useOpenPBR) { + (babylonMaterial as OpenPBRMaterial).baseColorTexture = texture; + } else { + (babylonMaterial as PBRMaterial).albedoTexture = texture; + } }) ); } @@ -2158,13 +2186,19 @@ export class GLTFLoader implements IGLTFLoader { promises.push( this.loadTextureInfoAsync(`${context}/metallicRoughnessTexture`, properties.metallicRoughnessTexture, (texture) => { texture.name = `${babylonMaterial.name} (Metallic Roughness)`; - babylonMaterial.metallicTexture = texture; + if (this.parent.useOpenPBR) { + (babylonMaterial as OpenPBRMaterial).baseMetalRoughTexture = texture; + } else { + (babylonMaterial as PBRMaterial).metallicTexture = texture; + } }) ); - babylonMaterial.useMetallnessFromMetallicTextureBlue = true; - babylonMaterial.useRoughnessFromMetallicTextureGreen = true; - babylonMaterial.useRoughnessFromMetallicTextureAlpha = false; + if (!this.parent.useOpenPBR) { + (babylonMaterial as PBRMaterial).useMetallnessFromMetallicTextureBlue = true; + (babylonMaterial as PBRMaterial).useRoughnessFromMetallicTextureGreen = true; + (babylonMaterial as PBRMaterial).useRoughnessFromMetallicTextureAlpha = false; + } } } @@ -2227,17 +2261,24 @@ export class GLTFLoader implements IGLTFLoader { private _createDefaultMaterial(name: string, babylonDrawMode: number): Material { this._babylonScene._blockEntityCollection = !!this._assetContainer; - const babylonMaterial = new PBRMaterial(name, this._babylonScene); + const babylonMaterial = new PBRMaterialClass(name, this._babylonScene); babylonMaterial._parentContainer = this._assetContainer; this._babylonScene._blockEntityCollection = false; // Moved to mesh so user can change materials on gltf meshes: babylonMaterial.sideOrientation = this._babylonScene.useRightHandedSystem ? Material.CounterClockWiseSideOrientation : Material.ClockWiseSideOrientation; babylonMaterial.fillMode = babylonDrawMode; babylonMaterial.enableSpecularAntiAliasing = true; - babylonMaterial.useRadianceOverAlpha = !this._parent.transparencyAsCoverage; - babylonMaterial.useSpecularOverAlpha = !this._parent.transparencyAsCoverage; - babylonMaterial.transparencyMode = PBRMaterial.PBRMATERIAL_OPAQUE; - babylonMaterial.metallic = 1; - babylonMaterial.roughness = 1; + if (!this.parent.useOpenPBR) { + (babylonMaterial as PBRMaterial).useRadianceOverAlpha = !this._parent.transparencyAsCoverage; + (babylonMaterial as PBRMaterial).useSpecularOverAlpha = !this._parent.transparencyAsCoverage; + } + babylonMaterial.transparencyMode = PBRMaterialClass.PBRMATERIAL_OPAQUE; + if (this.parent.useOpenPBR) { + (babylonMaterial as OpenPBRMaterial).baseMetalness = 1.0; + (babylonMaterial as OpenPBRMaterial).specularRoughness = 1.0; + } else { + (babylonMaterial as PBRMaterial).metallic = 1.0; + (babylonMaterial as PBRMaterial).roughness = 1.0; + } return babylonMaterial; } @@ -2295,13 +2336,17 @@ export class GLTFLoader implements IGLTFLoader { * @returns A promise that resolves when the load is complete */ public loadMaterialBasePropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material): Promise { - if (!(babylonMaterial instanceof PBRMaterial)) { + if (!(babylonMaterial instanceof PBRMaterialClass)) { throw new Error(`${context}: Material type not supported`); } const promises = new Array>(); - babylonMaterial.emissiveColor = material.emissiveFactor ? Color3.FromArray(material.emissiveFactor) : new Color3(0, 0, 0); + if (this.parent.useOpenPBR) { + (babylonMaterial as OpenPBRMaterial).emissionColor = material.emissiveFactor ? Color3.FromArray(material.emissiveFactor) : new Color3(0, 0, 0); + } else { + (babylonMaterial as PBRMaterial).emissiveColor = material.emissiveFactor ? Color3.FromArray(material.emissiveFactor) : new Color3(0, 0, 0); + } if (material.doubleSided) { babylonMaterial.backFaceCulling = false; babylonMaterial.twoSidedLighting = true; @@ -2312,31 +2357,42 @@ export class GLTFLoader implements IGLTFLoader { promises.push( this.loadTextureInfoAsync(`${context}/normalTexture`, material.normalTexture, (texture) => { texture.name = `${babylonMaterial.name} (Normal)`; - babylonMaterial.bumpTexture = texture; + if (this.parent.useOpenPBR) { + const mat = babylonMaterial as OpenPBRMaterial; + mat.geometryNormalTexture = texture; + if (material.normalTexture?.scale != undefined && mat.geometryNormalTexture) { + mat.geometryNormalTexture.level = material.normalTexture.scale; + } + } else { + const mat = babylonMaterial as PBRMaterial; + mat.bumpTexture = texture; + if (material.normalTexture?.scale != undefined && mat.bumpTexture) { + mat.bumpTexture.level = material.normalTexture.scale; + } + } }) ); babylonMaterial.invertNormalMapX = !this._babylonScene.useRightHandedSystem; babylonMaterial.invertNormalMapY = this._babylonScene.useRightHandedSystem; - if (material.normalTexture.scale != undefined && babylonMaterial.bumpTexture) { - babylonMaterial.bumpTexture.level = material.normalTexture.scale; - } - babylonMaterial.forceIrradianceInFragment = true; } + let aoTexture: BaseTexture; + let aoStrength: number = 1.0; + let emissionTexture: BaseTexture; + if (material.occlusionTexture) { material.occlusionTexture.nonColorData = true; promises.push( this.loadTextureInfoAsync(`${context}/occlusionTexture`, material.occlusionTexture, (texture) => { texture.name = `${babylonMaterial.name} (Occlusion)`; - babylonMaterial.ambientTexture = texture; + aoTexture = texture; }) ); - babylonMaterial.useAmbientInGrayScale = true; if (material.occlusionTexture.strength != undefined) { - babylonMaterial.ambientTextureStrength = material.occlusionTexture.strength; + aoStrength = material.occlusionTexture.strength; } } @@ -2344,12 +2400,25 @@ export class GLTFLoader implements IGLTFLoader { promises.push( this.loadTextureInfoAsync(`${context}/emissiveTexture`, material.emissiveTexture, (texture) => { texture.name = `${babylonMaterial.name} (Emissive)`; - babylonMaterial.emissiveTexture = texture; + emissionTexture = texture; }) ); } - return Promise.all(promises).then(() => {}); + return Promise.all(promises).then(() => { + if (this.parent.useOpenPBR) { + (babylonMaterial as OpenPBRMaterial).ambientOcclusionTexture = aoTexture; + (babylonMaterial as OpenPBRMaterial).emissionColorTexture = emissionTexture; + if (aoTexture) { + (babylonMaterial as OpenPBRMaterial).ambientOcclusionTexture.level = aoStrength; + } + } else { + (babylonMaterial as PBRMaterial).ambientTexture = aoTexture; + (babylonMaterial as PBRMaterial).emissiveTexture = emissionTexture; + (babylonMaterial as PBRMaterial).useAmbientInGrayScale = true; + (babylonMaterial as PBRMaterial).ambientTextureStrength = aoStrength; + } + }); } /** @@ -2360,29 +2429,31 @@ export class GLTFLoader implements IGLTFLoader { * @param babylonMaterial The Babylon material */ public loadMaterialAlphaProperties(context: string, material: IMaterial, babylonMaterial: Material): void { - if (!(babylonMaterial instanceof PBRMaterial)) { + if (!(babylonMaterial instanceof PBRMaterialClass)) { throw new Error(`${context}: Material type not supported`); } + const baseColorTexture = this.parent.useOpenPBR ? (babylonMaterial as OpenPBRMaterial).baseColorTexture : (babylonMaterial as PBRMaterial).albedoTexture; + const alphaMode = material.alphaMode || MaterialAlphaMode.OPAQUE; switch (alphaMode) { case MaterialAlphaMode.OPAQUE: { - babylonMaterial.transparencyMode = PBRMaterial.PBRMATERIAL_OPAQUE; + babylonMaterial.transparencyMode = PBRMaterialClass.PBRMATERIAL_OPAQUE; babylonMaterial.alpha = 1.0; // Force alpha to 1.0 for opaque mode. break; } case MaterialAlphaMode.MASK: { - babylonMaterial.transparencyMode = PBRMaterial.PBRMATERIAL_ALPHATEST; + babylonMaterial.transparencyMode = PBRMaterialClass.PBRMATERIAL_ALPHATEST; babylonMaterial.alphaCutOff = material.alphaCutoff == undefined ? 0.5 : material.alphaCutoff; - if (babylonMaterial.albedoTexture) { - babylonMaterial.albedoTexture.hasAlpha = true; + if (baseColorTexture) { + baseColorTexture.hasAlpha = true; } break; } case MaterialAlphaMode.BLEND: { - babylonMaterial.transparencyMode = PBRMaterial.PBRMATERIAL_ALPHABLEND; - if (babylonMaterial.albedoTexture) { - babylonMaterial.albedoTexture.hasAlpha = true; + babylonMaterial.transparencyMode = PBRMaterialClass.PBRMATERIAL_ALPHABLEND; + if (baseColorTexture) { + baseColorTexture.hasAlpha = true; babylonMaterial.useAlphaFromAlbedoTexture = true; } break; @@ -2901,7 +2972,7 @@ export class GLTFLoader implements IGLTFLoader { return this._applyExtensions( material, "loadMaterialProperties", - (extension) => extension.loadMaterialPropertiesAsync && extension.loadMaterialPropertiesAsync(context, material, babylonMaterial) + (extension) => extension.loadMaterialPropertiesAsync && extension.loadMaterialPropertiesAsync(context, material, babylonMaterial, this.parent.useOpenPBR) ); } diff --git a/packages/dev/loaders/src/glTF/2.0/glTFLoaderExtension.ts b/packages/dev/loaders/src/glTF/2.0/glTFLoaderExtension.ts index 72ae55dce23..15930ba12e6 100644 --- a/packages/dev/loaders/src/glTF/2.0/glTFLoaderExtension.ts +++ b/packages/dev/loaders/src/glTF/2.0/glTFLoaderExtension.ts @@ -127,9 +127,10 @@ export interface IGLTFLoaderExtension extends IGLTFBaseLoaderExtension, IDisposa * @param context The context when loading the asset * @param material The glTF material property * @param babylonMaterial The Babylon material + * @param useOpenPBR Load materials as OpenPBR materials instead of glTF PBR materials. * @returns A promise that resolves when the load is complete or null if not handled */ - loadMaterialPropertiesAsync?(context: string, material: IMaterial, babylonMaterial: Material): Nullable>; + loadMaterialPropertiesAsync?(context: string, material: IMaterial, babylonMaterial: Material, useOpenPBR: boolean): Nullable>; /** * Define this method to modify the default behavior when loading texture infos. diff --git a/packages/dev/loaders/src/glTF/glTFFileLoader.ts b/packages/dev/loaders/src/glTF/glTFFileLoader.ts index 4da0f4a44c3..2e0e712506b 100644 --- a/packages/dev/loaders/src/glTF/glTFFileLoader.ts +++ b/packages/dev/loaders/src/glTF/glTFFileLoader.ts @@ -206,6 +206,7 @@ abstract class GLTFLoaderOptions { this.compileShadowGenerators = options.compileShadowGenerators ?? this.compileShadowGenerators; this.transparencyAsCoverage = options.transparencyAsCoverage ?? this.transparencyAsCoverage; this.useRangeRequests = options.useRangeRequests ?? this.useRangeRequests; + this.useOpenPBR = options.useOpenPBR ?? this.useOpenPBR; this.createInstances = options.createInstances ?? this.createInstances; this.alwaysComputeBoundingBox = options.alwaysComputeBoundingBox ?? this.alwaysComputeBoundingBox; this.loadAllMaterials = options.loadAllMaterials ?? this.loadAllMaterials; @@ -294,6 +295,11 @@ abstract class GLTFLoaderOptions { */ public useRangeRequests = false; + /** + * Load the glTF files using the PBR2 material. + */ + public useOpenPBR = false; + /** * Defines if the loader should create instances when multiple glTF nodes point to the same glTF mesh. Defaults to true. */ diff --git a/packages/dev/serializers/src/glTF/2.0/Extensions/EXT_materials_clearcoat_darkening.ts b/packages/dev/serializers/src/glTF/2.0/Extensions/EXT_materials_clearcoat_darkening.ts new file mode 100644 index 00000000000..ecfee3c8bc3 --- /dev/null +++ b/packages/dev/serializers/src/glTF/2.0/Extensions/EXT_materials_clearcoat_darkening.ts @@ -0,0 +1,97 @@ +import type { IMaterial, IEXTMaterialsClearcoatDarkening } from "babylonjs-gltf2interface"; +import type { IGLTFExporterExtensionV2 } from "../glTFExporterExtension"; +import { GLTFExporter } from "../glTFExporter"; +import type { Material } from "core/Materials/material"; +import type { BaseTexture } from "core/Materials/Textures/baseTexture"; +import { OpenPBRMaterial } from "core/Materials/PBR/openPbrMaterial"; +import type { Nullable } from "core/types"; + +const NAME = "EXT_materials_clearcoat_darkening"; + +/** + * @internal + */ +// eslint-disable-next-line @typescript-eslint/naming-convention +export class EXT_materials_clearcoat_darkening 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 OpenPBRMaterial) { + if (babylonMaterial.coatDarkening) { + if (babylonMaterial.coatDarkeningTexture) { + additionalTextures.push(babylonMaterial.coatDarkeningTexture); + } + return additionalTextures; + } + } + + return []; + } + + // eslint-disable-next-line no-restricted-syntax + public postExportMaterialAsync?(context: string, node: IMaterial, babylonMaterial: Material): Promise { + return new Promise((resolve) => { + let coatDarkeningFactor: Nullable = null; + let coatDarkeningTexture: Nullable = null; + if (babylonMaterial instanceof OpenPBRMaterial) { + coatDarkeningFactor = babylonMaterial.coatDarkening; + coatDarkeningTexture = babylonMaterial.coatDarkeningTexture; + } + if (coatDarkeningFactor === null || (coatDarkeningFactor === 1.0 && coatDarkeningTexture === null)) { + resolve(node); + return; + } + + this._wasUsed = true; + + // This material must have the clearcoat extension already before + // we can add the clearcoat darkening sub-extension + const parentExt = node.extensions ? node.extensions["KHR_materials_clearcoat"] : null; + if (!parentExt) { + resolve(node); + return; + } + + const coatDarkeningTextureInfo = this._exporter._materialExporter.getTextureInfo(coatDarkeningTexture); + + const coatDarkeningInfo: IEXTMaterialsClearcoatDarkening = { + clearcoatDarkeningFactor: coatDarkeningFactor, + clearcoatDarkeningTexture: coatDarkeningTextureInfo ?? undefined, + }; + + if (coatDarkeningInfo.clearcoatDarkeningTexture !== null) { + this._exporter._materialNeedsUVsSet.add(babylonMaterial); + } + + parentExt.extensions = parentExt.extensions || {}; + parentExt.extensions[NAME] = coatDarkeningInfo; + + resolve(node); + }); + } +} + +GLTFExporter.RegisterExtension(NAME, (exporter) => new EXT_materials_clearcoat_darkening(exporter)); 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 index d86d3bd8f2a..e0476e6a274 100644 --- 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 @@ -4,6 +4,8 @@ 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"; +import { OpenPBRMaterial } from "core/Materials/PBR/openPbrMaterial"; +import type { Nullable } from "core/types"; const NAME = "EXT_materials_diffuse_roughness"; @@ -45,6 +47,13 @@ export class EXT_materials_diffuse_roughness implements IGLTFExporterExtensionV2 } return additionalTextures; } + } else if (babylonMaterial instanceof OpenPBRMaterial) { + if (babylonMaterial.baseDiffuseRoughness) { + if (babylonMaterial.baseDiffuseRoughnessTexture) { + additionalTextures.push(babylonMaterial.baseDiffuseRoughnessTexture); + } + return additionalTextures; + } } return []; @@ -53,29 +62,37 @@ export class EXT_materials_diffuse_roughness implements IGLTFExporterExtensionV2 // eslint-disable-next-line no-restricted-syntax public postExportMaterialAsync?(context: string, node: IMaterial, babylonMaterial: Material): Promise { return new Promise((resolve) => { + let diffuseRoughnessFactor: Nullable = null; + let diffuseRoughnessTexture: Nullable = null; if (babylonMaterial instanceof PBRBaseMaterial) { - if (!babylonMaterial._baseDiffuseRoughness) { - resolve(node); - return; - } - - this._wasUsed = true; + diffuseRoughnessFactor = babylonMaterial._baseDiffuseRoughness; + diffuseRoughnessTexture = babylonMaterial._baseDiffuseRoughnessTexture; + } else if (babylonMaterial instanceof OpenPBRMaterial) { + diffuseRoughnessFactor = babylonMaterial.baseDiffuseRoughness; + diffuseRoughnessTexture = babylonMaterial.baseDiffuseRoughnessTexture; + } + if (!diffuseRoughnessFactor) { + resolve(node); + return; + } - node.extensions = node.extensions || {}; + this._wasUsed = true; - const diffuseRoughnessTextureInfo = this._exporter._materialExporter.getTextureInfo(babylonMaterial._baseDiffuseRoughnessTexture); + node.extensions = node.extensions || {}; - const diffuseRoughnessInfo: IEXTMaterialsDiffuseRoughness = { - diffuseRoughnessFactor: babylonMaterial._baseDiffuseRoughness, - diffuseRoughnessTexture: diffuseRoughnessTextureInfo ?? undefined, - }; + const diffuseRoughnessTextureInfo = this._exporter._materialExporter.getTextureInfo(diffuseRoughnessTexture); - if (diffuseRoughnessInfo.diffuseRoughnessTexture !== null) { - this._exporter._materialNeedsUVsSet.add(babylonMaterial); - } + const diffuseRoughnessInfo: IEXTMaterialsDiffuseRoughness = { + diffuseRoughnessFactor: diffuseRoughnessFactor, + diffuseRoughnessTexture: diffuseRoughnessTextureInfo ?? undefined, + }; - node.extensions[NAME] = diffuseRoughnessInfo; + if (diffuseRoughnessInfo.diffuseRoughnessTexture !== null) { + this._exporter._materialNeedsUVsSet.add(babylonMaterial); } + + node.extensions[NAME] = diffuseRoughnessInfo; + resolve(node); }); } diff --git a/packages/dev/serializers/src/glTF/2.0/Extensions/KHR_materials_clearcoat.ts b/packages/dev/serializers/src/glTF/2.0/Extensions/KHR_materials_clearcoat.ts index 4aad4002efb..f81e9f6cb1d 100644 --- a/packages/dev/serializers/src/glTF/2.0/Extensions/KHR_materials_clearcoat.ts +++ b/packages/dev/serializers/src/glTF/2.0/Extensions/KHR_materials_clearcoat.ts @@ -6,6 +6,7 @@ import { PBRBaseMaterial } from "core/Materials/PBR/pbrBaseMaterial"; import type { BaseTexture } from "core/Materials/Textures/baseTexture"; import { Tools } from "core/Misc/tools"; +import { OpenPBRMaterial } from "core/Materials/PBR/openPbrMaterial"; const NAME = "KHR_materials_clearcoat"; @@ -53,6 +54,19 @@ export class KHR_materials_clearcoat implements IGLTFExporterExtensionV2 { } return additionalTextures; } + } else if (babylonMaterial instanceof OpenPBRMaterial) { + if (babylonMaterial.coatWeight > 0) { + if (babylonMaterial.coatWeightTexture) { + additionalTextures.push(babylonMaterial.coatWeightTexture); + } + if (babylonMaterial.geometryCoatNormalTexture) { + additionalTextures.push(babylonMaterial.geometryCoatNormalTexture); + } + if (babylonMaterial.coatRoughnessTexture) { + additionalTextures.push(babylonMaterial.coatRoughnessTexture); + } + return additionalTextures; + } } return []; @@ -101,6 +115,43 @@ export class KHR_materials_clearcoat implements IGLTFExporterExtensionV2 { this._exporter._materialNeedsUVsSet.add(babylonMaterial); } + node.extensions[NAME] = clearCoatInfo; + } else if (babylonMaterial instanceof OpenPBRMaterial) { + if (babylonMaterial.coatWeight == 0.0) { + resolve(node); + return; + } + + this._wasUsed = true; + + node.extensions = node.extensions || {}; + + const clearCoatTextureInfo = this._exporter._materialExporter.getTextureInfo(babylonMaterial.coatWeightTexture); + let clearCoatTextureRoughnessInfo; + if (babylonMaterial.useCoatRoughnessFromWeightTexture) { + clearCoatTextureRoughnessInfo = this._exporter._materialExporter.getTextureInfo(babylonMaterial.coatWeightTexture); + } else { + clearCoatTextureRoughnessInfo = this._exporter._materialExporter.getTextureInfo(babylonMaterial.coatRoughnessTexture); + } + + if (babylonMaterial.coatColorTexture) { + Tools.Warn(`Clear Color tint is not supported for glTF export. Ignoring for: ${babylonMaterial.name}`); + } + + const clearCoatNormalTextureInfo = this._exporter._materialExporter.getTextureInfo(babylonMaterial.geometryCoatNormalTexture); + + const clearCoatInfo: IKHRMaterialsClearcoat = { + clearcoatFactor: babylonMaterial.coatWeight, + clearcoatTexture: clearCoatTextureInfo ?? undefined, + clearcoatRoughnessFactor: babylonMaterial.coatRoughness, + clearcoatRoughnessTexture: clearCoatTextureRoughnessInfo ?? undefined, + clearcoatNormalTexture: clearCoatNormalTextureInfo ?? undefined, + }; + + if (clearCoatInfo.clearcoatTexture !== null || clearCoatInfo.clearcoatRoughnessTexture !== null || clearCoatInfo.clearcoatRoughnessTexture !== null) { + this._exporter._materialNeedsUVsSet.add(babylonMaterial); + } + node.extensions[NAME] = clearCoatInfo; } resolve(node); 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 d2b5adfee31..78b8d361916 100644 --- a/packages/dev/serializers/src/glTF/2.0/Extensions/index.ts +++ b/packages/dev/serializers/src/glTF/2.0/Extensions/index.ts @@ -3,6 +3,7 @@ export * from "./KHR_draco_mesh_compression"; export * from "./KHR_lights_punctual"; export * from "./KHR_materials_anisotropy"; export * from "./KHR_materials_clearcoat"; +export * from "./EXT_materials_clearcoat_darkening"; export * from "./KHR_materials_diffuse_transmission"; export * from "./KHR_materials_dispersion"; export * from "./KHR_materials_emissive_strength"; diff --git a/packages/dev/serializers/src/glTF/2.0/glTFExporter.ts b/packages/dev/serializers/src/glTF/2.0/glTFExporter.ts index f35f7202b7d..38a9a70b754 100644 --- a/packages/dev/serializers/src/glTF/2.0/glTFExporter.ts +++ b/packages/dev/serializers/src/glTF/2.0/glTFExporter.ts @@ -80,6 +80,7 @@ import { Color3, Color4 } from "core/Maths/math.color"; import { TargetCamera } from "core/Cameras/targetCamera"; import { Epsilon } from "core/Maths/math.constants"; import { DataWriter } from "./dataWriter"; +import { OpenPBRMaterial } from "core/Materials/PBR/openPbrMaterial"; class ExporterState { // Babylon indices array, start, count, offset, flip -> glTF accessor index @@ -1411,6 +1412,8 @@ export class GLTFExporter { materialIndex = await this._materialExporter.exportPBRMaterialAsync(babylonMaterial, hasUVs); } else if (babylonMaterial instanceof StandardMaterial) { materialIndex = await this._materialExporter.exportStandardMaterialAsync(babylonMaterial, hasUVs); + } else if (babylonMaterial instanceof OpenPBRMaterial) { + materialIndex = await this._materialExporter.exportOpenPBRMaterialAsync(babylonMaterial, hasUVs); } else { Logger.Warn(`Unsupported material '${babylonMaterial.name}' with type ${babylonMaterial.getClassName()}`); return; diff --git a/packages/dev/serializers/src/glTF/2.0/glTFMaterialExporter.ts b/packages/dev/serializers/src/glTF/2.0/glTFMaterialExporter.ts index 00788b831d4..88d433846ac 100644 --- a/packages/dev/serializers/src/glTF/2.0/glTFMaterialExporter.ts +++ b/packages/dev/serializers/src/glTF/2.0/glTFMaterialExporter.ts @@ -22,8 +22,9 @@ import { DumpTools } from "core/Misc/dumpTools"; import type { Material } from "core/Materials/material"; import type { StandardMaterial } from "core/Materials/standardMaterial"; -import type { PBRBaseMaterial } from "core/Materials/PBR/pbrBaseMaterial"; +import { PBRBaseMaterial } from "core/Materials/PBR/pbrBaseMaterial"; import { SpecularPowerToRoughness } from "core/Helpers/materialConversionHelper"; +import type { OpenPBRMaterial } from "core/Materials/PBR/openPbrMaterial"; const Epsilon = 1e-6; const DielectricSpecular = new Color3(0.04, 0.04, 0.04); @@ -524,26 +525,35 @@ export class GLTFMaterialExporter { /** * Convert a PBRMaterial (Metallic/Roughness) to Metallic Roughness factors + * @param baseColor Base color of the material + * @param metallic Metallic factor of the material + * @param roughness Roughness factor of the material + * @param albedoTexture Albedo texture of the material + * @param metallicRoughnessTexture Metallic roughness texture of the material * @param babylonPBRMaterial BJS PBR Metallic Roughness Material * @param glTFPbrMetallicRoughness glTF PBR Metallic Roughness interface * @param hasUVs specifies if texture coordinates are present on the submesh to determine if textures should be applied * @returns glTF PBR Metallic Roughness factors */ private async _convertMetalRoughFactorsToMetallicRoughnessAsync( - babylonPBRMaterial: PBRBaseMaterial, + baseColor: Color3, + metallic: Nullable, + roughness: Nullable, + albedoTexture: Nullable, + metallicRoughnessTexture: Nullable, + babylonPBRMaterial: PBRBaseMaterial | OpenPBRMaterial, glTFPbrMetallicRoughness: IMaterialPbrMetallicRoughness, hasUVs: boolean ): Promise { const promises: Promise[] = []; const metallicRoughness: IPBRMetallicRoughness = { - baseColor: babylonPBRMaterial._albedoColor, - metallic: babylonPBRMaterial._metallic, - roughness: babylonPBRMaterial._roughness, + baseColor: baseColor, + metallic: metallic, + roughness: roughness, }; if (hasUVs) { - const albedoTexture = babylonPBRMaterial._albedoTexture; if (albedoTexture) { promises.push( this.exportTextureAsync(albedoTexture).then((glTFTexture) => { @@ -553,10 +563,9 @@ export class GLTFMaterialExporter { }) ); } - const metallicTexture = babylonPBRMaterial._metallicTexture; - if (metallicTexture) { + if (metallicRoughnessTexture) { promises.push( - this.exportTextureAsync(metallicTexture).then((glTFTexture) => { + this.exportTextureAsync(metallicRoughnessTexture).then((glTFTexture) => { if (glTFTexture) { glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFTexture; } @@ -741,7 +750,16 @@ export class GLTFMaterialExporter { } const metallicRoughness = useMetallicRoughness - ? await this._convertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, glTFPbrMetallicRoughness, hasUVs) + ? await this._convertMetalRoughFactorsToMetallicRoughnessAsync( + babylonPBRMaterial._albedoColor, + babylonPBRMaterial._metallic, + babylonPBRMaterial._roughness, + babylonPBRMaterial._albedoTexture, + babylonPBRMaterial._metallicTexture, + babylonPBRMaterial, + glTFPbrMetallicRoughness, + hasUVs + ) : await this._convertSpecGlossFactorsToMetallicRoughnessAsync(babylonPBRMaterial, glTFPbrMetallicRoughness, hasUVs); await this._setMetallicRoughnessPbrMaterialAsync(metallicRoughness, babylonPBRMaterial, glTFMaterial, glTFPbrMetallicRoughness, hasUVs); @@ -754,7 +772,7 @@ export class GLTFMaterialExporter { private async _setMetallicRoughnessPbrMaterialAsync( metallicRoughness: IPBRMetallicRoughness, - babylonPBRMaterial: PBRBaseMaterial, + babylonPBRMaterial: PBRBaseMaterial | OpenPBRMaterial, glTFMaterial: IMaterial, glTFPbrMetallicRoughness: IMaterialPbrMetallicRoughness, hasUVs: boolean @@ -782,7 +800,7 @@ export class GLTFMaterialExporter { if (hasUVs) { const promises: Promise[] = []; - const bumpTexture = babylonPBRMaterial._bumpTexture; + const bumpTexture = babylonPBRMaterial instanceof PBRBaseMaterial ? babylonPBRMaterial._bumpTexture : babylonPBRMaterial.geometryNormalTexture; if (bumpTexture) { promises.push( this.exportTextureAsync(bumpTexture).then((glTFTexture) => { @@ -796,7 +814,7 @@ export class GLTFMaterialExporter { ); } - const ambientTexture = babylonPBRMaterial._ambientTexture; + const ambientTexture = babylonPBRMaterial instanceof PBRBaseMaterial ? babylonPBRMaterial._ambientTexture : babylonPBRMaterial.ambientOcclusionTexture; if (ambientTexture) { promises.push( this.exportTextureAsync(ambientTexture).then((glTFTexture) => { @@ -808,7 +826,8 @@ export class GLTFMaterialExporter { }; glTFMaterial.occlusionTexture = occlusionTexture; - const ambientTextureStrength = babylonPBRMaterial._ambientTextureStrength; + const ambientTextureStrength = + babylonPBRMaterial instanceof PBRBaseMaterial ? babylonPBRMaterial._ambientTextureStrength : babylonPBRMaterial.ambientOcclusionTexture.level; if (ambientTextureStrength) { occlusionTexture.strength = ambientTextureStrength; } @@ -817,7 +836,7 @@ export class GLTFMaterialExporter { ); } - const emissiveTexture = babylonPBRMaterial._emissiveTexture; + const emissiveTexture = babylonPBRMaterial instanceof PBRBaseMaterial ? babylonPBRMaterial._emissiveTexture : babylonPBRMaterial.emissionColorTexture; if (emissiveTexture) { promises.push( this.exportTextureAsync(emissiveTexture).then((glTFTexture) => { @@ -834,7 +853,7 @@ export class GLTFMaterialExporter { } } - const emissiveColor = babylonPBRMaterial._emissiveColor; + const emissiveColor = babylonPBRMaterial instanceof PBRBaseMaterial ? babylonPBRMaterial._emissiveColor : babylonPBRMaterial.emissionColor; if (!emissiveColor.equalsWithEpsilon(Black, Epsilon)) { glTFMaterial.emissiveFactor = emissiveColor.asArray(); } @@ -842,6 +861,38 @@ export class GLTFMaterialExporter { glTFMaterial.pbrMetallicRoughness = glTFPbrMetallicRoughness; } + public async exportOpenPBRMaterialAsync(babylonOpenPBRMaterial: OpenPBRMaterial, hasUVs: boolean): Promise { + const glTFPbrMetallicRoughness: IMaterialPbrMetallicRoughness = {}; + + const glTFMaterial: IMaterial = { + name: babylonOpenPBRMaterial.name, + }; + + const albedoColor = babylonOpenPBRMaterial.baseColor; + const alpha = babylonOpenPBRMaterial.geometryOpacity; + if (albedoColor) { + glTFPbrMetallicRoughness.baseColorFactor = [albedoColor.r, albedoColor.g, albedoColor.b, alpha]; + } + + const metallicRoughness = await this._convertMetalRoughFactorsToMetallicRoughnessAsync( + babylonOpenPBRMaterial.baseColor, + babylonOpenPBRMaterial.baseMetalness, + babylonOpenPBRMaterial.specularRoughness, + babylonOpenPBRMaterial.baseColorTexture, + babylonOpenPBRMaterial.baseMetalRoughTexture, + babylonOpenPBRMaterial, + glTFPbrMetallicRoughness, + hasUVs + ); + + await this._setMetallicRoughnessPbrMaterialAsync(metallicRoughness, babylonOpenPBRMaterial, glTFMaterial, glTFPbrMetallicRoughness, hasUVs); + await this._finishMaterialAsync(glTFMaterial, babylonOpenPBRMaterial); + + const materials = this._exporter._materials; + materials.push(glTFMaterial); + return materials.length - 1; + } + public async exportTextureAsync(babylonTexture: BaseTexture): Promise> { let textureInfo = this._textureMap.get(babylonTexture); if (textureInfo) { diff --git a/packages/public/glTF2Interface/babylon.glTF2Interface.d.ts b/packages/public/glTF2Interface/babylon.glTF2Interface.d.ts index 6578b6e423b..f54807b2e8a 100644 --- a/packages/public/glTF2Interface/babylon.glTF2Interface.d.ts +++ b/packages/public/glTF2Interface/babylon.glTF2Interface.d.ts @@ -1068,6 +1068,18 @@ declare module BABYLON.GLTF2 { clearcoatRoughnessFactor?: number; clearcoatRoughnessTexture?: ITextureInfo; clearcoatNormalTexture?: IMaterialNormalTextureInfo; + /** + * Dictionary object with extension-specific objects + */ + extensions?: { + [key: string]: any; + }; + } + + /** @internal */ + interface IEXTMaterialsClearcoatDarkening { + clearcoatDarkeningFactor?: number; + clearcoatDarkeningTexture?: ITextureInfo; } /** @internal */ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Analytic-Lights-Coat-Darkening-vs-Coat-IOR.png b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Analytic-Lights-Coat-Darkening-vs-Coat-IOR.png new file mode 100644 index 00000000000..d8dda54f5ac Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Analytic-Lights-Coat-Darkening-vs-Coat-IOR.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Analytic-Lights-Coat-Darkening.png b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Analytic-Lights-Coat-Darkening.png new file mode 100644 index 00000000000..44a4e20f895 Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Analytic-Lights-Coat-Darkening.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Analytic-Lights-Specular-IOR-vs-Coat-IOR.png b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Analytic-Lights-Specular-IOR-vs-Coat-IOR.png new file mode 100644 index 00000000000..d0721191699 Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Analytic-Lights-Specular-IOR-vs-Coat-IOR.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Analytic-Lights-Specular-IOR-vs-Coat-Weight.png b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Analytic-Lights-Specular-IOR-vs-Coat-Weight.png new file mode 100644 index 00000000000..baf96643e5b Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Analytic-Lights-Specular-IOR-vs-Coat-Weight.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Analytic-Lights-Specular-Roughness-vs-Coat-Roughness.png b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Analytic-Lights-Specular-Roughness-vs-Coat-Roughness.png new file mode 100644 index 00000000000..db31db0945d Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Analytic-Lights-Specular-Roughness-vs-Coat-Roughness.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Analytic-Lights-Specular-Weight-vs-Metalness.png b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Analytic-Lights-Specular-Weight-vs-Metalness.png new file mode 100644 index 00000000000..baaab50b422 Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Analytic-Lights-Specular-Weight-vs-Metalness.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-Coat-Darkening-vs-Coat-IOR.png b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-Coat-Darkening-vs-Coat-IOR.png new file mode 100644 index 00000000000..543fc3f30e1 Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-Coat-Darkening-vs-Coat-IOR.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-Coat-Darkening.png b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-Coat-Darkening.png new file mode 100644 index 00000000000..ffa0f048fd2 Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-Coat-Darkening.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-F82-Specular-Metal-vs-Weight.png b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-F82-Specular-Metal-vs-Weight.png new file mode 100644 index 00000000000..f69a2107bd7 Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-F82-Specular-Metal-vs-Weight.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-F82-Specular-Roughness-vs-Weight.png b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-F82-Specular-Roughness-vs-Weight.png new file mode 100644 index 00000000000..8b122826c50 Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-F82-Specular-Roughness-vs-Weight.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-Furnace-Test-for-Metal-Rough.png b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-Furnace-Test-for-Metal-Rough.png new file mode 100644 index 00000000000..f5a88884c55 Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-Furnace-Test-for-Metal-Rough.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-Metal-vs-Coat-IOR.png b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-Metal-vs-Coat-IOR.png new file mode 100644 index 00000000000..5f98fb9bf31 Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-Metal-vs-Coat-IOR.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-Metalness-vs-Coat-IOR.png b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-Metalness-vs-Coat-IOR.png new file mode 100644 index 00000000000..384af598c0d Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-Metalness-vs-Coat-IOR.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-Normal-and-Coat-Normal-Mapping.png b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-Normal-and-Coat-Normal-Mapping.png new file mode 100644 index 00000000000..bc6909c5460 Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-Normal-and-Coat-Normal-Mapping.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-Reflectance-with-IOR.png b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-Reflectance-with-IOR.png new file mode 100644 index 00000000000..fb5755a7ef3 Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-Reflectance-with-IOR.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-Specular-IOR-vs-Coat-IOR.png b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-Specular-IOR-vs-Coat-IOR.png new file mode 100644 index 00000000000..ee61b4deb3d Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-Specular-IOR-vs-Coat-IOR.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-Specular-IOR-vs-Coat-Weight.png b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-Specular-IOR-vs-Coat-Weight.png new file mode 100644 index 00000000000..a58485a2cf9 Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-Specular-IOR-vs-Coat-Weight.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-Specular-Roughness-vs-Coat-Roughness.png b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-Specular-Roughness-vs-Coat-Roughness.png new file mode 100644 index 00000000000..f7ea12554a3 Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-IBL-Specular-Roughness-vs-Coat-Roughness.png differ diff --git a/packages/tools/tests/test/visualization/config.json b/packages/tools/tests/test/visualization/config.json index ce0e002de93..c0ce6be670a 100644 --- a/packages/tools/tests/test/visualization/config.json +++ b/packages/tools/tests/test/visualization/config.json @@ -2756,6 +2756,91 @@ { "title": "Test code inlining", "playgroundId": "#YG3BBF#51" + }, + { + "title": "OpenPBR IBL Reflectance with IOR", + "playgroundId": "#KQYNYS#46", + "excludedEngines": ["webgl1"] + }, + { + "title": "OpenPBR IBL F82 Specular Metal vs Weight", + "playgroundId": "#GRQHVV#25", + "excludedEngines": ["webgl1"] + }, + { + "title": "OpenPBR IBL F82 Specular Roughness vs Weight", + "playgroundId": "#GRQHVV#26", + "excludedEngines": ["webgl1"] + }, + { + "title": "OpenPBR IBL Furnace Test for Metal-Rough", + "playgroundId": "#KQYNYS#47", + "excludedEngines": ["webgl1"] + }, + { + "title": "OpenPBR IBL Specular IOR vs Coat IOR", + "playgroundId": "#GRQHVV#39", + "excludedEngines": ["webgl1"] + }, + { + "title": "OpenPBR IBL Metalness vs Coat IOR", + "playgroundId": "#GRQHVV#38", + "excludedEngines": ["webgl1"] + }, + { + "title": "OpenPBR IBL Specular IOR vs Coat Weight", + "playgroundId": "#GRQHVV#40", + "excludedEngines": ["webgl1"] + }, + { + "title": "OpenPBR IBL Specular Roughness vs Coat Roughness", + "playgroundId": "#GRQHVV#41", + "excludedEngines": ["webgl1"] + }, + { + "title": "OpenPBR Analytic Lights Specular Roughness vs Coat Roughness", + "playgroundId": "#GRQHVV#42", + "excludedEngines": ["webgl1"] + }, + { + "title": "OpenPBR Analytic Lights Specular Weight vs Metalness", + "playgroundId": "#GRQHVV#34", + "excludedEngines": ["webgl1"] + }, + { + "title": "OpenPBR Analytic Lights Specular IOR vs Coat Weight", + "playgroundId": "#GRQHVV#35", + "excludedEngines": ["webgl1"] + }, + { + "title": "OpenPBR Analytic Lights Specular IOR vs Coat IOR", + "playgroundId": "#GRQHVV#36", + "excludedEngines": ["webgl1"] + }, + { + "title": "OpenPBR IBL Normal and Coat Normal Mapping", + "playgroundId": "#GRQHVV#44", + "excludedEngines": ["webgl1"] + }, + { + "title": "OpenPBR IBL Coat Darkening", + "playgroundId": "#GRQHVV#46", + "excludedEngines": ["webgl1"] + }, + { + "title": "OpenPBR IBL Coat Darkening vs Coat IOR", + "playgroundId": "#GRQHVV#48", + "excludedEngines": ["webgl1"] + }, + { + "title": "OpenPBR Analytic Lights Coat Darkening", + "playgroundId": "#GRQHVV#47", + "excludedEngines": ["webgl1"] + }, + { + "title": "OpenPBR Analytic Lights Coat Darkening vs Coat IOR", + "playgroundId": "#GRQHVV#49", + "excludedEngines": ["webgl1"] } ] }