@babylonjs/core
Version:
Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.
872 lines (868 loc) • 101 kB
JavaScript
/** This file must only contain pure code and pure imports */
import { __esDecorate, __runInitializers } from "../../../../tslib.es6.js";
import { NodeMaterialBlock } from "../../nodeMaterialBlock.js";
import { NodeMaterialBlockConnectionPointTypes } from "../../Enums/nodeMaterialBlockConnectionPointTypes.js";
import { NodeMaterialBlockTargets } from "../../Enums/nodeMaterialBlockTargets.js";
import { NodeMaterialSystemValues } from "../../Enums/nodeMaterialSystemValues.js";
import { InputBlock } from "../Input/inputBlock.pure.js";
import { PBRBaseMaterial } from "../../../PBR/pbrBaseMaterial.pure.js";
import { editableInPropertyPage } from "../../../../Decorators/nodeDecorator.js";
import { NodeMaterialConnectionPointCustomObject } from "../../nodeMaterialConnectionPointCustomObject.js";
import { SheenBlock } from "./sheenBlock.pure.js";
import { GetEnvironmentBRDFTexture } from "../../../../Misc/brdfTextureTools.js";
import { MaterialFlags } from "../../../materialFlags.js";
import { AnisotropyBlock } from "./anisotropyBlock.pure.js";
import { ReflectionBlock } from "./reflectionBlock.pure.js";
import { ClearCoatBlock } from "./clearCoatBlock.pure.js";
import { IridescenceBlock } from "./iridescenceBlock.pure.js";
import { SubSurfaceBlock } from "./subSurfaceBlock.pure.js";
import { Color3 } from "../../../../Maths/math.color.pure.js";
import { Logger } from "../../../../Misc/logger.js";
import { BindLight, BindLights, PrepareDefinesForLight, PrepareDefinesForLights, PrepareDefinesForMultiview, PrepareUniformsAndSamplersForLight, } from "../../../materialHelper.functions.js";
import { RegisterClass } from "../../../../Misc/typeStore.js";
const MapOutputToVariable = {
ambientClr: ["finalAmbient", ""],
diffuseDir: ["finalDiffuse", ""],
specularDir: ["finalSpecularScaled", "!defined(UNLIT) && defined(SPECULARTERM)"],
clearcoatDir: ["finalClearCoatScaled", "!defined(UNLIT) && defined(CLEARCOAT)"],
sheenDir: ["finalSheenScaled", "!defined(UNLIT) && defined(SHEEN)"],
diffuseInd: ["finalIrradiance", "!defined(UNLIT) && defined(REFLECTION)"],
specularInd: ["finalRadianceScaled", "!defined(UNLIT) && defined(REFLECTION)"],
clearcoatInd: ["clearcoatOut.finalClearCoatRadianceScaled", "!defined(UNLIT) && defined(REFLECTION) && defined(CLEARCOAT)"],
sheenInd: ["sheenOut.finalSheenRadianceScaled", "!defined(UNLIT) && defined(REFLECTION) && defined(SHEEN) && defined(ENVIRONMENTBRDF)"],
refraction: ["subSurfaceOut.finalRefraction", "!defined(UNLIT) && defined(SS_REFRACTION)"],
lighting: ["finalColor.rgb", ""],
shadow: ["aggShadow", ""],
alpha: ["alpha", ""],
};
/**
* Block used to implement the PBR metallic/roughness model
* @see https://playground.babylonjs.com/#D8AK3Z#80
*/
let PBRMetallicRoughnessBlock = (() => {
var _a;
let _classSuper = NodeMaterialBlock;
let _directIntensity_decorators;
let _directIntensity_initializers = [];
let _directIntensity_extraInitializers = [];
let _environmentIntensity_decorators;
let _environmentIntensity_initializers = [];
let _environmentIntensity_extraInitializers = [];
let _specularIntensity_decorators;
let _specularIntensity_initializers = [];
let _specularIntensity_extraInitializers = [];
let _lightFalloff_decorators;
let _lightFalloff_initializers = [];
let _lightFalloff_extraInitializers = [];
let _useAlphaTest_decorators;
let _useAlphaTest_initializers = [];
let _useAlphaTest_extraInitializers = [];
let _alphaTestCutoff_decorators;
let _alphaTestCutoff_initializers = [];
let _alphaTestCutoff_extraInitializers = [];
let _useAlphaBlending_decorators;
let _useAlphaBlending_initializers = [];
let _useAlphaBlending_extraInitializers = [];
let _useRadianceOverAlpha_decorators;
let _useRadianceOverAlpha_initializers = [];
let _useRadianceOverAlpha_extraInitializers = [];
let _useSpecularOverAlpha_decorators;
let _useSpecularOverAlpha_initializers = [];
let _useSpecularOverAlpha_extraInitializers = [];
let _enableSpecularAntiAliasing_decorators;
let _enableSpecularAntiAliasing_initializers = [];
let _enableSpecularAntiAliasing_extraInitializers = [];
let _realTimeFiltering_decorators;
let _realTimeFiltering_initializers = [];
let _realTimeFiltering_extraInitializers = [];
let _realTimeFilteringQuality_decorators;
let _realTimeFilteringQuality_initializers = [];
let _realTimeFilteringQuality_extraInitializers = [];
let _baseDiffuseModel_decorators;
let _baseDiffuseModel_initializers = [];
let _baseDiffuseModel_extraInitializers = [];
let _useEnergyConservation_decorators;
let _useEnergyConservation_initializers = [];
let _useEnergyConservation_extraInitializers = [];
let _useRadianceOcclusion_decorators;
let _useRadianceOcclusion_initializers = [];
let _useRadianceOcclusion_extraInitializers = [];
let _useHorizonOcclusion_decorators;
let _useHorizonOcclusion_initializers = [];
let _useHorizonOcclusion_extraInitializers = [];
let _unlit_decorators;
let _unlit_initializers = [];
let _unlit_extraInitializers = [];
let _forceNormalForward_decorators;
let _forceNormalForward_initializers = [];
let _forceNormalForward_extraInitializers = [];
let _generateOnlyFragmentCode_decorators;
let _generateOnlyFragmentCode_initializers = [];
let _generateOnlyFragmentCode_extraInitializers = [];
let _debugMode_decorators;
let _debugMode_initializers = [];
let _debugMode_extraInitializers = [];
let _debugLimit_decorators;
let _debugLimit_initializers = [];
let _debugLimit_extraInitializers = [];
let _debugFactor_decorators;
let _debugFactor_initializers = [];
let _debugFactor_extraInitializers = [];
return _a = class PBRMetallicRoughnessBlock extends _classSuper {
static _OnGenerateOnlyFragmentCodeChanged(block, _propertyName) {
const that = block;
if (that.worldPosition.isConnected || that.worldNormal.isConnected) {
that.generateOnlyFragmentCode = !that.generateOnlyFragmentCode;
Logger.Error("The worldPosition and worldNormal inputs must not be connected to be able to switch!");
return false;
}
that._setTarget();
return true;
}
_setTarget() {
this._setInitialTarget(this.generateOnlyFragmentCode ? NodeMaterialBlockTargets.Fragment : NodeMaterialBlockTargets.VertexAndFragment);
this.getInputByName("worldPosition").target = this.generateOnlyFragmentCode ? NodeMaterialBlockTargets.Fragment : NodeMaterialBlockTargets.Vertex;
this.getInputByName("worldNormal").target = this.generateOnlyFragmentCode ? NodeMaterialBlockTargets.Fragment : NodeMaterialBlockTargets.Vertex;
}
/**
* Create a new ReflectionBlock
* @param name defines the block name
*/
constructor(name) {
super(name, NodeMaterialBlockTargets.VertexAndFragment);
this._environmentBRDFTexture = null;
this._metallicReflectanceColor = Color3.White();
this._metallicF0Factor = 1;
/**
* Intensity of the direct lights e.g. the four lights available in your scene.
* This impacts both the direct diffuse and specular highlights.
*/
this.directIntensity = __runInitializers(this, _directIntensity_initializers, 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.
*/
this.environmentIntensity = (__runInitializers(this, _directIntensity_extraInitializers), __runInitializers(this, _environmentIntensity_initializers, 1.0));
/**
* This is a special control allowing the reduction of the specular highlights coming from the
* four lights of the scene. Those highlights may not be needed in full environment lighting.
*/
this.specularIntensity = (__runInitializers(this, _environmentIntensity_extraInitializers), __runInitializers(this, _specularIntensity_initializers, 1.0));
/**
* Defines the falloff type used in this material.
* It by default is Physical.
*/
this.lightFalloff = (__runInitializers(this, _specularIntensity_extraInitializers), __runInitializers(this, _lightFalloff_initializers, 0));
/**
* Specifies that alpha test should be used
*/
this.useAlphaTest = (__runInitializers(this, _lightFalloff_extraInitializers), __runInitializers(this, _useAlphaTest_initializers, false));
/**
* Defines the alpha limits in alpha test mode.
*/
this.alphaTestCutoff = (__runInitializers(this, _useAlphaTest_extraInitializers), __runInitializers(this, _alphaTestCutoff_initializers, 0.5));
/**
* Specifies that alpha blending should be used
*/
this.useAlphaBlending = (__runInitializers(this, _alphaTestCutoff_extraInitializers), __runInitializers(this, _useAlphaBlending_initializers, false));
/**
* Specifies that the material will keeps the reflection highlights over a transparent surface (only the most luminous ones).
* A car glass is a good example of that. When the street lights reflects on it you can not see what is behind.
*/
this.useRadianceOverAlpha = (__runInitializers(this, _useAlphaBlending_extraInitializers), __runInitializers(this, _useRadianceOverAlpha_initializers, true));
/**
* Specifies that the material will keeps the specular highlights over a transparent surface (only the most luminous ones).
* A car glass is a good example of that. When sun reflects on it you can not see what is behind.
*/
this.useSpecularOverAlpha = (__runInitializers(this, _useRadianceOverAlpha_extraInitializers), __runInitializers(this, _useSpecularOverAlpha_initializers, true));
/**
* 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 bump values.
*/
this.enableSpecularAntiAliasing = (__runInitializers(this, _useSpecularOverAlpha_extraInitializers), __runInitializers(this, _enableSpecularAntiAliasing_initializers, false));
/**
* Enables realtime filtering on the texture.
*/
this.realTimeFiltering = (__runInitializers(this, _enableSpecularAntiAliasing_extraInitializers), __runInitializers(this, _realTimeFiltering_initializers, false));
/**
* Quality switch for realtime filtering
*/
this.realTimeFilteringQuality = (__runInitializers(this, _realTimeFiltering_extraInitializers), __runInitializers(this, _realTimeFilteringQuality_initializers, 8));
/**
* Base Diffuse Model
*/
this.baseDiffuseModel = (__runInitializers(this, _realTimeFilteringQuality_extraInitializers), __runInitializers(this, _baseDiffuseModel_initializers, 0));
/**
* Defines if the material uses energy conservation.
*/
this.useEnergyConservation = (__runInitializers(this, _baseDiffuseModel_extraInitializers), __runInitializers(this, _useEnergyConservation_initializers, 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.
*/
this.useRadianceOcclusion = (__runInitializers(this, _useEnergyConservation_extraInitializers), __runInitializers(this, _useRadianceOcclusion_initializers, true));
/**
* 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).
*/
this.useHorizonOcclusion = (__runInitializers(this, _useRadianceOcclusion_extraInitializers), __runInitializers(this, _useHorizonOcclusion_initializers, true));
/**
* If set to true, no lighting calculations will be applied.
*/
this.unlit = (__runInitializers(this, _useHorizonOcclusion_extraInitializers), __runInitializers(this, _unlit_initializers, false));
/**
* Force normal to face away from face.
*/
this.forceNormalForward = (__runInitializers(this, _unlit_extraInitializers), __runInitializers(this, _forceNormalForward_initializers, false));
/** Indicates that no code should be generated in the vertex shader. Can be useful in some specific circumstances (like when doing ray marching for eg) */
this.generateOnlyFragmentCode = (__runInitializers(this, _forceNormalForward_extraInitializers), __runInitializers(this, _generateOnlyFragmentCode_initializers, false));
/**
* Defines the material debug mode.
* It helps seeing only some components of the material while troubleshooting.
*/
this.debugMode = (__runInitializers(this, _generateOnlyFragmentCode_extraInitializers), __runInitializers(this, _debugMode_initializers, 0));
/**
* 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 0
*/
this.debugLimit = (__runInitializers(this, _debugMode_extraInitializers), __runInitializers(this, _debugLimit_initializers, 0));
/**
* 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.
*/
this.debugFactor = (__runInitializers(this, _debugLimit_extraInitializers), __runInitializers(this, _debugFactor_initializers, 1));
__runInitializers(this, _debugFactor_extraInitializers);
this._isUnique = true;
this.registerInput("worldPosition", NodeMaterialBlockConnectionPointTypes.Vector4, false, NodeMaterialBlockTargets.Vertex);
this.registerInput("worldNormal", NodeMaterialBlockConnectionPointTypes.Vector4, false, NodeMaterialBlockTargets.Vertex);
this.registerInput("view", NodeMaterialBlockConnectionPointTypes.Matrix, false);
this.registerInput("cameraPosition", NodeMaterialBlockConnectionPointTypes.Vector3, false, NodeMaterialBlockTargets.Fragment);
this.registerInput("perturbedNormal", NodeMaterialBlockConnectionPointTypes.Vector4, true, NodeMaterialBlockTargets.Fragment);
this.registerInput("baseColor", NodeMaterialBlockConnectionPointTypes.Color3, true, NodeMaterialBlockTargets.Fragment);
this.registerInput("metallic", NodeMaterialBlockConnectionPointTypes.Float, false, NodeMaterialBlockTargets.Fragment);
this.registerInput("roughness", NodeMaterialBlockConnectionPointTypes.Float, false, NodeMaterialBlockTargets.Fragment);
this.registerInput("ambientOcc", NodeMaterialBlockConnectionPointTypes.Float, true, NodeMaterialBlockTargets.Fragment);
this.registerInput("opacity", NodeMaterialBlockConnectionPointTypes.Float, true, NodeMaterialBlockTargets.Fragment);
this.registerInput("indexOfRefraction", NodeMaterialBlockConnectionPointTypes.Float, true, NodeMaterialBlockTargets.Fragment);
this.registerInput("ambientColor", NodeMaterialBlockConnectionPointTypes.Color3, true, NodeMaterialBlockTargets.Fragment);
this.registerInput("reflection", NodeMaterialBlockConnectionPointTypes.Object, true, NodeMaterialBlockTargets.Fragment, new NodeMaterialConnectionPointCustomObject("reflection", this, 0 /* NodeMaterialConnectionPointDirection.Input */, ReflectionBlock, "ReflectionBlock"));
this.registerInput("clearcoat", NodeMaterialBlockConnectionPointTypes.Object, true, NodeMaterialBlockTargets.Fragment, new NodeMaterialConnectionPointCustomObject("clearcoat", this, 0 /* NodeMaterialConnectionPointDirection.Input */, ClearCoatBlock, "ClearCoatBlock"));
this.registerInput("sheen", NodeMaterialBlockConnectionPointTypes.Object, true, NodeMaterialBlockTargets.Fragment, new NodeMaterialConnectionPointCustomObject("sheen", this, 0 /* NodeMaterialConnectionPointDirection.Input */, SheenBlock, "SheenBlock"));
this.registerInput("subsurface", NodeMaterialBlockConnectionPointTypes.Object, true, NodeMaterialBlockTargets.Fragment, new NodeMaterialConnectionPointCustomObject("subsurface", this, 0 /* NodeMaterialConnectionPointDirection.Input */, SubSurfaceBlock, "SubSurfaceBlock"));
this.registerInput("anisotropy", NodeMaterialBlockConnectionPointTypes.Object, true, NodeMaterialBlockTargets.Fragment, new NodeMaterialConnectionPointCustomObject("anisotropy", this, 0 /* NodeMaterialConnectionPointDirection.Input */, AnisotropyBlock, "AnisotropyBlock"));
this.registerInput("iridescence", NodeMaterialBlockConnectionPointTypes.Object, true, NodeMaterialBlockTargets.Fragment, new NodeMaterialConnectionPointCustomObject("iridescence", this, 0 /* NodeMaterialConnectionPointDirection.Input */, IridescenceBlock, "IridescenceBlock"));
this.registerOutput("ambientClr", NodeMaterialBlockConnectionPointTypes.Color3, NodeMaterialBlockTargets.Fragment);
this.registerOutput("diffuseDir", NodeMaterialBlockConnectionPointTypes.Color3, NodeMaterialBlockTargets.Fragment);
this.registerOutput("specularDir", NodeMaterialBlockConnectionPointTypes.Color3, NodeMaterialBlockTargets.Fragment);
this.registerOutput("clearcoatDir", NodeMaterialBlockConnectionPointTypes.Color3, NodeMaterialBlockTargets.Fragment);
this.registerOutput("sheenDir", NodeMaterialBlockConnectionPointTypes.Color3, NodeMaterialBlockTargets.Fragment);
this.registerOutput("diffuseInd", NodeMaterialBlockConnectionPointTypes.Color3, NodeMaterialBlockTargets.Fragment);
this.registerOutput("specularInd", NodeMaterialBlockConnectionPointTypes.Color3, NodeMaterialBlockTargets.Fragment);
this.registerOutput("clearcoatInd", NodeMaterialBlockConnectionPointTypes.Color3, NodeMaterialBlockTargets.Fragment);
this.registerOutput("sheenInd", NodeMaterialBlockConnectionPointTypes.Color3, NodeMaterialBlockTargets.Fragment);
this.registerOutput("refraction", NodeMaterialBlockConnectionPointTypes.Color3, NodeMaterialBlockTargets.Fragment);
this.registerOutput("lighting", NodeMaterialBlockConnectionPointTypes.Color3, NodeMaterialBlockTargets.Fragment);
this.registerOutput("shadow", NodeMaterialBlockConnectionPointTypes.Float, NodeMaterialBlockTargets.Fragment);
this.registerOutput("alpha", NodeMaterialBlockConnectionPointTypes.Float, NodeMaterialBlockTargets.Fragment);
}
/**
* Initialize the block and prepare the context for build
* @param state defines the state that will be used for the build
*/
initialize(state) {
state._excludeVariableName("vLightingIntensity");
state._excludeVariableName("geometricNormalW");
state._excludeVariableName("normalW");
state._excludeVariableName("faceNormal");
state._excludeVariableName("albedoOpacityOut");
state._excludeVariableName("surfaceAlbedo");
state._excludeVariableName("alpha");
state._excludeVariableName("aoOut");
state._excludeVariableName("baseColor");
state._excludeVariableName("reflectivityOut");
state._excludeVariableName("microSurface");
state._excludeVariableName("roughness");
state._excludeVariableName("vReflectivityColor");
state._excludeVariableName("NdotVUnclamped");
state._excludeVariableName("NdotV");
state._excludeVariableName("alphaG");
state._excludeVariableName("AARoughnessFactors");
state._excludeVariableName("environmentBrdf");
state._excludeVariableName("ambientMonochrome");
state._excludeVariableName("seo");
state._excludeVariableName("eho");
state._excludeVariableName("environmentRadiance");
state._excludeVariableName("irradianceVector");
state._excludeVariableName("environmentIrradiance");
state._excludeVariableName("diffuseBase");
state._excludeVariableName("specularBase");
state._excludeVariableName("preInfo");
state._excludeVariableName("info");
state._excludeVariableName("shadow");
state._excludeVariableName("finalDiffuse");
state._excludeVariableName("finalAmbient");
state._excludeVariableName("ambientOcclusionForDirectDiffuse");
state._excludeVariableName("finalColor");
state._excludeVariableName("vClipSpacePosition");
state._excludeVariableName("vDebugMode");
state._excludeVariableName("vViewDepth");
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this._initShaderSourceAsync(state.shaderLanguage);
}
async _initShaderSourceAsync(shaderLanguage) {
this._codeIsReady = false;
if (shaderLanguage === 1 /* ShaderLanguage.WGSL */) {
await Promise.all([import("../../../../ShadersWGSL/pbr.vertex.js"), import("../../../../ShadersWGSL/pbr.fragment.js")]);
}
else {
await Promise.all([import("../../../../Shaders/pbr.vertex.js"), import("../../../../Shaders/pbr.fragment.js")]);
}
this._codeIsReady = true;
this.onCodeIsReadyObservable.notifyObservers(this);
}
/**
* Gets the current class name
* @returns the class name
*/
getClassName() {
return "PBRMetallicRoughnessBlock";
}
/**
* Gets the world position input component
*/
get worldPosition() {
return this._inputs[0];
}
/**
* Gets the world normal input component
*/
get worldNormal() {
return this._inputs[1];
}
/**
* Gets the view matrix parameter
*/
get view() {
return this._inputs[2];
}
/**
* Gets the camera position input component
*/
get cameraPosition() {
return this._inputs[3];
}
/**
* Gets the perturbed normal input component
*/
get perturbedNormal() {
return this._inputs[4];
}
/**
* Gets the base color input component
*/
get baseColor() {
return this._inputs[5];
}
/**
* Gets the metallic input component
*/
get metallic() {
return this._inputs[6];
}
/**
* Gets the roughness input component
*/
get roughness() {
return this._inputs[7];
}
/**
* Gets the ambient occlusion input component
*/
get ambientOcc() {
return this._inputs[8];
}
/**
* Gets the opacity input component
*/
get opacity() {
return this._inputs[9];
}
/**
* Gets the index of refraction input component
*/
get indexOfRefraction() {
return this._inputs[10];
}
/**
* Gets the ambient color input component
*/
get ambientColor() {
return this._inputs[11];
}
/**
* Gets the reflection object parameters
*/
get reflection() {
return this._inputs[12];
}
/**
* Gets the clear coat object parameters
*/
get clearcoat() {
return this._inputs[13];
}
/**
* Gets the sheen object parameters
*/
get sheen() {
return this._inputs[14];
}
/**
* Gets the sub surface object parameters
*/
get subsurface() {
return this._inputs[15];
}
/**
* Gets the anisotropy object parameters
*/
get anisotropy() {
return this._inputs[16];
}
/**
* Gets the iridescence object parameters
*/
get iridescence() {
return this._inputs[17];
}
/**
* Gets the ambient output component
*/
get ambientClr() {
return this._outputs[0];
}
/**
* Gets the diffuse output component
*/
get diffuseDir() {
return this._outputs[1];
}
/**
* Gets the specular output component
*/
get specularDir() {
return this._outputs[2];
}
/**
* Gets the clear coat output component
*/
get clearcoatDir() {
return this._outputs[3];
}
/**
* Gets the sheen output component
*/
get sheenDir() {
return this._outputs[4];
}
/**
* Gets the indirect diffuse output component
*/
get diffuseInd() {
return this._outputs[5];
}
/**
* Gets the indirect specular output component
*/
get specularInd() {
return this._outputs[6];
}
/**
* Gets the indirect clear coat output component
*/
get clearcoatInd() {
return this._outputs[7];
}
/**
* Gets the indirect sheen output component
*/
get sheenInd() {
return this._outputs[8];
}
/**
* Gets the refraction output component
*/
get refraction() {
return this._outputs[9];
}
/**
* Gets the global lighting output component
*/
get lighting() {
return this._outputs[10];
}
/**
* Gets the shadow output component
*/
get shadow() {
return this._outputs[11];
}
/**
* Gets the alpha output component
*/
get alpha() {
return this._outputs[12];
}
/**
* Auto configure the block based on the material
* @param material - the node material
* @param additionalFilteringInfo - additional filtering info
*/
autoConfigure(material, additionalFilteringInfo = () => true) {
if (!this.cameraPosition.isConnected) {
let cameraPositionInput = material.getInputBlockByPredicate((b) => b.systemValue === NodeMaterialSystemValues.CameraPosition && additionalFilteringInfo(b));
if (!cameraPositionInput) {
cameraPositionInput = new InputBlock("cameraPosition");
cameraPositionInput.setAsSystemValue(NodeMaterialSystemValues.CameraPosition);
}
cameraPositionInput.output.connectTo(this.cameraPosition);
}
if (!this.view.isConnected) {
let viewInput = material.getInputBlockByPredicate((b) => b.systemValue === NodeMaterialSystemValues.View && additionalFilteringInfo(b));
if (!viewInput) {
viewInput = new InputBlock("view");
viewInput.setAsSystemValue(NodeMaterialSystemValues.View);
}
viewInput.output.connectTo(this.view);
}
}
/**
* Prepare the list of defines
* @param defines - the list of defines to update
* @param nodeMaterial - the node material
* @param mesh - the mesh to prepare defines for
*/
prepareDefines(defines, nodeMaterial, mesh) {
if (!mesh) {
return;
}
// General
defines.setValue("PBR", true);
defines.setValue("METALLICWORKFLOW", true);
defines.setValue("DEBUGMODE", this.debugMode, true);
defines.setValue("DEBUGMODE_FORCERETURN", true);
defines.setValue("NORMALXYSCALE", true);
defines.setValue("BUMP", this.perturbedNormal.isConnected, true);
defines.setValue("LODBASEDMICROSFURACE", this._scene.getEngine().getCaps().textureLOD);
// Albedo & Opacity
defines.setValue("ALBEDO", false, true);
defines.setValue("OPACITY", this.opacity.isConnected, true);
// Ambient occlusion
defines.setValue("AMBIENT", true, true);
defines.setValue("AMBIENTINGRAYSCALE", false, true);
// Reflectivity
defines.setValue("REFLECTIVITY", false, true);
defines.setValue("AOSTOREINMETALMAPRED", false, true);
defines.setValue("METALLNESSSTOREINMETALMAPBLUE", false, true);
defines.setValue("ROUGHNESSSTOREINMETALMAPALPHA", false, true);
defines.setValue("ROUGHNESSSTOREINMETALMAPGREEN", false, true);
// Lighting & colors
if (this.lightFalloff === PBRBaseMaterial.LIGHTFALLOFF_STANDARD) {
defines.setValue("USEPHYSICALLIGHTFALLOFF", false);
defines.setValue("USEGLTFLIGHTFALLOFF", false);
}
else if (this.lightFalloff === PBRBaseMaterial.LIGHTFALLOFF_GLTF) {
defines.setValue("USEPHYSICALLIGHTFALLOFF", false);
defines.setValue("USEGLTFLIGHTFALLOFF", true);
}
else {
defines.setValue("USEPHYSICALLIGHTFALLOFF", true);
defines.setValue("USEGLTFLIGHTFALLOFF", false);
}
// Transparency
const alphaTestCutOffString = this.alphaTestCutoff.toString();
defines.setValue("ALPHABLEND", this.useAlphaBlending, true);
defines.setValue("ALPHAFROMALBEDO", false, true);
defines.setValue("ALPHATEST", this.useAlphaTest, true);
defines.setValue("ALPHATESTVALUE", alphaTestCutOffString.indexOf(".") < 0 ? alphaTestCutOffString + "." : alphaTestCutOffString, true);
defines.setValue("OPACITYRGB", false, true);
// Rendering
defines.setValue("RADIANCEOVERALPHA", this.useRadianceOverAlpha, true);
defines.setValue("SPECULAROVERALPHA", this.useSpecularOverAlpha, true);
defines.setValue("SPECULARAA", this._scene.getEngine().getCaps().standardDerivatives && this.enableSpecularAntiAliasing, true);
defines.setValue("REALTIME_FILTERING", this.realTimeFiltering, true);
const scene = mesh.getScene();
const engine = scene.getEngine();
if (engine._features.needTypeSuffixInShaderConstants) {
defines.setValue("NUM_SAMPLES", this.realTimeFilteringQuality + "u", true);
}
else {
defines.setValue("NUM_SAMPLES", "" + this.realTimeFilteringQuality, true);
}
defines.setValue("BASE_DIFFUSE_MODEL", this.baseDiffuseModel, true);
// Advanced
defines.setValue("BRDF_V_HEIGHT_CORRELATED", true);
defines.setValue("LEGACY_SPECULAR_ENERGY_CONSERVATION", true);
defines.setValue("MS_BRDF_ENERGY_CONSERVATION", this.useEnergyConservation, true);
defines.setValue("RADIANCEOCCLUSION", this.useRadianceOcclusion, true);
defines.setValue("HORIZONOCCLUSION", this.useHorizonOcclusion, true);
defines.setValue("UNLIT", this.unlit, true);
defines.setValue("FORCENORMALFORWARD", this.forceNormalForward, true);
if (this._environmentBRDFTexture && MaterialFlags.ReflectionTextureEnabled) {
defines.setValue("ENVIRONMENTBRDF", true);
defines.setValue("ENVIRONMENTBRDF_RGBD", this._environmentBRDFTexture.isRGBD, true);
}
else {
defines.setValue("ENVIRONMENTBRDF", false);
defines.setValue("ENVIRONMENTBRDF_RGBD", false);
}
if (defines._areImageProcessingDirty && nodeMaterial.imageProcessingConfiguration) {
nodeMaterial.imageProcessingConfiguration.prepareDefines(defines);
}
if (!defines._areLightsDirty) {
return;
}
if (!this.light) {
// Lights
PrepareDefinesForLights(scene, mesh, defines, true, nodeMaterial.maxSimultaneousLights);
defines._needNormals = true;
// Multiview
PrepareDefinesForMultiview(scene, defines);
}
else {
const state = {
needNormals: false,
needRebuild: false,
lightmapMode: false,
shadowEnabled: false,
specularEnabled: false,
};
PrepareDefinesForLight(scene, mesh, this.light, this._lightId, defines, true, state);
if (state.needRebuild) {
defines.rebuild();
}
}
}
/**
* Update the uniforms and samples
* @param state - the build state
* @param nodeMaterial - the node material
* @param defines - the list of defines
* @param uniformBuffers - the uniform buffers
*/
updateUniformsAndSamples(state, nodeMaterial, defines, uniformBuffers) {
for (let lightIndex = 0; lightIndex < nodeMaterial.maxSimultaneousLights; lightIndex++) {
if (!defines["LIGHT" + lightIndex]) {
break;
}
const onlyUpdateBuffersList = state.uniforms.indexOf("vLightData" + lightIndex) >= 0;
PrepareUniformsAndSamplersForLight(lightIndex, state.uniforms, state.samplers, defines["PROJECTEDLIGHTTEXTURE" + lightIndex], uniformBuffers, onlyUpdateBuffersList, defines["IESLIGHTTEXTURE" + lightIndex], defines["CLUSTLIGHT" + lightIndex], defines["RECTAREALIGHTEMISSIONTEXTURE" + lightIndex], state.shaderLanguage === 1 /* ShaderLanguage.WGSL */);
}
}
/**
* Checks if the block is ready
* @param mesh - the mesh to check
* @param nodeMaterial - the node material
* @param defines - the list of defines
* @returns true if ready
*/
isReady(mesh, nodeMaterial, defines) {
if (this._environmentBRDFTexture && !this._environmentBRDFTexture.isReady()) {
return false;
}
if (defines._areImageProcessingDirty && nodeMaterial.imageProcessingConfiguration) {
if (!nodeMaterial.imageProcessingConfiguration.isReady()) {
return false;
}
}
if (this.light && !this.light.areLightTexturesReady()) {
return false;
}
return true;
}
/**
* Bind data to effect
* @param effect - the effect to bind data to
* @param nodeMaterial - the node material
* @param mesh - the mesh to bind data for
*/
bind(effect, nodeMaterial, mesh) {
if (!mesh) {
return;
}
const scene = mesh.getScene();
if (!this.light) {
BindLights(scene, mesh, effect, true, nodeMaterial.maxSimultaneousLights);
}
else {
BindLight(this.light, this._lightId, scene, effect, true);
}
effect.setTexture(this._environmentBrdfSamplerName, this._environmentBRDFTexture);
effect.setFloat2("vDebugMode", this.debugLimit, this.debugFactor);
const ambientScene = this._scene.ambientColor;
if (ambientScene) {
effect.setColor3("ambientFromScene", ambientScene);
}
const invertNormal = scene.useRightHandedSystem === (scene._mirroredCameraPosition != null);
effect.setFloat(this._invertNormalName, invertNormal ? -1 : 1);
effect.setFloat4("vLightingIntensity", this.directIntensity, 1, this.environmentIntensity * this._scene.environmentIntensity, this.specularIntensity);
// reflectivity bindings
const metallicF90 = this._metallicF0Factor;
effect.setColor4(this._vMetallicReflectanceFactorsName, this._metallicReflectanceColor, metallicF90);
if (nodeMaterial.imageProcessingConfiguration) {
nodeMaterial.imageProcessingConfiguration.bind(effect);
}
}
_injectVertexCode(state) {
const worldPos = this.worldPosition;
const worldNormal = this.worldNormal;
const comments = `//${this.name}`;
const isWebGPU = state.shaderLanguage === 1 /* ShaderLanguage.WGSL */;
const scene = state.sharedData.nodeMaterial.getScene();
// Declaration
if (!this.light) {
// Emit for all lights
state._emitFunctionFromInclude(state.supportUniformBuffers ? "lightVxUboDeclaration" : "lightVxFragmentDeclaration", comments, {
repeatKey: "maxSimultaneousLights",
});
this._lightId = 0;
state.sharedData.dynamicUniformBlocks.push(this);
}
else {
this._lightId = (state.counters["lightCounter"] !== undefined ? state.counters["lightCounter"] : -1) + 1;
state.counters["lightCounter"] = this._lightId;
state._emitFunctionFromInclude(state.supportUniformBuffers ? "lightVxUboDeclaration" : "lightVxFragmentDeclaration", comments, {
replaceStrings: [{ search: /{X}/g, replace: this._lightId.toString() }],
}, this._lightId.toString());
}
// Inject code in vertex
const worldPosVaryingName = "v_" + worldPos.associatedVariableName;
if (state._emitVaryingFromString(worldPosVaryingName, NodeMaterialBlockConnectionPointTypes.Vector4)) {
state.compilationString += (isWebGPU ? "vertexOutputs." : "") + `${worldPosVaryingName} = ${worldPos.associatedVariableName};\n`;
}
const worldNormalVaryingName = "v_" + worldNormal.associatedVariableName;
if (state._emitVaryingFromString(worldNormalVaryingName, NodeMaterialBlockConnectionPointTypes.Vector4)) {
state.compilationString += (isWebGPU ? "vertexOutputs." : "") + `${worldNormalVaryingName} = ${worldNormal.associatedVariableName};\n`;
}
const reflectionBlock = this.reflection.isConnected ? this.reflection.connectedPoint?.ownerBlock : null;
if (reflectionBlock) {
reflectionBlock.viewConnectionPoint = this.view;
}
state.compilationString += reflectionBlock?.handleVertexSide(state) ?? "";
if (state._emitVaryingFromString("vClipSpacePosition", NodeMaterialBlockConnectionPointTypes.Vector4, "defined(IGNORE) || DEBUGMODE > 0")) {
state._injectAtEnd += `#if DEBUGMODE > 0\n`;
state._injectAtEnd += (isWebGPU ? "vertexOutputs." : "") + `vClipSpacePosition = ${isWebGPU ? "vertexOutputs.position" : "gl_Position"};\n`;
state._injectAtEnd += `#endif\n`;
}
if (this.light) {
state.compilationString += state._emitCodeFromInclude("shadowsVertex", comments, {
replaceStrings: [
{ search: /{X}/g, replace: this._lightId.toString() },
{ search: /worldPos/g, replace: worldPos.associatedVariableName },
],
});
}
else {
state.compilationString += `${state._declareLocalVar("worldPos", NodeMaterialBlockConnectionPointTypes.Vector4)} = ${worldPos.associatedVariableName};\n`;
if (this.view.isConnected) {
state.compilationString += `${state._declareLocalVar("view", NodeMaterialBlockConnectionPointTypes.Matrix)} = ${this.view.associatedVariableName};\n`;
state._emitVaryingFromString("vViewDepth", NodeMaterialBlockConnectionPointTypes.Float);
state.compilationString +=
(state.shaderLanguage === 1 /* ShaderLanguage.WGSL */ ? "vertexOutputs." : "") +
`vViewDepth = ${scene.useRightHandedSystem ? "-" : ""}(${this.view.associatedVariableName} * ${worldPos.associatedVariableName}).z;\n`;
}
state.compilationString += state._emitCodeFromInclude("shadowsVertex", comments, {
repeatKey: "maxSimultaneousLights",
});
}
}
_getAlbedoOpacityCode(state) {
const isWebGPU = state.shaderLanguage === 1 /* ShaderLanguage.WGSL */;
let code = isWebGPU ? "var albedoOpacityOut: albedoOpacityOutParams;\n" : `albedoOpacityOutParams albedoOpacityOut;\n`;
const albedoColor = this.baseColor.isConnected ? this.baseColor.associatedVariableName : "vec3(1.)";
const opacity = this.opacity.isConnected ? this.opacity.associatedVariableName : "1.";
code += `albedoOpacityOut = albedoOpacityBlock(
vec4${state.fSuffix}(${albedoColor}, 1.)
#ifdef ALBEDO
,vec4${state.fSuffix}(1.)
,vec2${state.fSuffix}(1., 1.)
#endif
,1. /* Base Weight */
#ifdef OPACITY
,vec4${state.fSuffix}(${opacity})
,vec2${state.fSuffix}(1., 1.)
#endif
);
${state._declareLocalVar("surfaceAlbedo", NodeMaterialBlockConnectionPointTypes.Vector3)} = albedoOpacityOut.surfaceAlbedo;
${state._declareLocalVar("alpha", NodeMaterialBlockConnectionPointTypes.Float)} = albedoOpacityOut.alpha;\n`;
return code;
}
_getAmbientOcclusionCode(state) {
const isWebGPU = state.shaderLanguage === 1 /* ShaderLanguage.WGSL */;
let code = isWebGPU ? "var aoOut: ambientOcclusionOutParams;\n" : `ambientOcclusionOutParams aoOut;\n`;
const ao = this.ambientOcc.isConnected ? this.ambientOcc.associatedVariableName : "1.";
code += `aoOut = ambientOcclusionBlock(
#ifdef AMBIENT
vec3${state.fSuffix}(${ao}),
vec4${state.fSuffix}(0., 1.0, 1.0, 0.)
#endif
);\n`;
return code;
}
_getReflectivityCode(state) {
const isWebGPU = state.shaderLanguage === 1 /* ShaderLanguage.WGSL */;
let code = isWebGPU ? "var reflectivityOut: reflectivityOutParams;\n" : `reflectivityOutParams reflectivityOut;\n`;
const aoIntensity = "1.";
this._vMetallicReflectanceFactorsName = state._getFreeVariableName("vMetallicReflectanceFactors");
state._emitUniformFromString(this._vMetallicReflectanceFactorsName, NodeMaterialBlockConnectionPointTypes.Vector4);
this._baseDiffuseRoughnessName = state._getFreeVariableName("baseDiffuseRoughness");
state._emitUniformFromString(this._baseDiffuseRoughnessName, NodeMaterialBlockConnectionPointTypes.Float);
const outsideIOR = 1; // consider air as clear coat and other layers would remap in the shader.
const ior = this.indexOfRefraction.connectInputBlock?.value ?? 1.5;
// Based of the schlick fresnel approximation model
// for dielectrics.
const f0 = Math.pow((ior - outsideIOR) / (ior + outsideIOR), 2);
code += `${state._declareLocalVar("baseColor", NodeMaterialBlockConnectionPointTypes.Vector3)} = surfaceAlbedo;
${isWebGPU ? "let" : `vec4${state.fSuffix}`} vReflectivityColor = vec4${state.fSuffix}(${this.metallic.associatedVariableName}, ${this.roughness.associatedVariableName}, ${this.indexOfRefraction.associatedVariableName || "1.5"}, ${f0});
reflectivityOut = reflectivityBlock(
vReflectivityColor
#ifdef METALLICWORKFLOW
, surfaceAlbedo
, ${(isWebGPU ? "uniforms." : "") + this._vMetallicReflectanceFactorsName}
#endif
, ${(isWebGPU ? "uniforms." : "") + this._baseDiffuseRoughnessName}
#ifdef BASE_DIFFUSE_ROUGHNESS
, 0.
, vec2${state.fSuffix}(0., 0.)
#endif
#ifdef REFLECTIVITY
, vec3${state.fSuffix}(0., 0., ${aoIntensity})
, vec4${state.fSuffix}(1.)
#endif
#if defined(METALLICWORKFLOW) && defined(REFLECTIVITY) && defined(AOSTOREINMETALMAPRED)
, aoOut.ambientOcclusionColor
#endif
#ifdef MICROSURFACEMAP
, microSurfaceTexel <== not handled!
#endif
);
${state._declareLocalVar("microSurface", NodeMaterialBlockConnectionPointTypes.Float)} = reflectivityOut.microSurface;
${state._declareLocalVar("roughness", NodeMaterialBlockConnectionPointTypes.Float)} = reflectivityOut.roughness;
${state._declareLocalVar("diffuseRoughness", NodeMaterialBlockConnectionPointTypes.Float)} = reflectivityOut.diffuseRoughness;
#ifdef METALLICWORKFLOW
surfaceAlbedo = reflectivityOut.surfaceAlbedo;
#endif
#if defined(METALLICWORKFLOW) && defined(REFLECTIVITY) && defined(AOSTOREINMETALMAPRED)
aoOut.ambientOcclusionColor = reflectivityOut.ambientOcclusionColor;
#endif\n`;
return code;
}
_buildBlock(state) {
super._buildBlock(state);
this._scene = state.sharedData.scene;
const isWebGPU = state.shaderLanguage === 1 /* ShaderLanguage.WGSL */;
if (!this._environmentBRDFTexture) {
th