@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.
344 lines (343 loc) • 18 kB
JavaScript
import { __decorate } from "../../../../tslib.es6.js";
import { NodeMaterialBlock } from "../../nodeMaterialBlock.js";
import { NodeMaterialBlockConnectionPointTypes } from "../../Enums/nodeMaterialBlockConnectionPointTypes.js";
import { NodeMaterialBlockTargets } from "../../Enums/nodeMaterialBlockTargets.js";
import { RegisterClass } from "../../../../Misc/typeStore.js";
import { InputBlock } from "../Input/inputBlock.js";
import { NodeMaterialConnectionPointCustomObject } from "../../nodeMaterialConnectionPointCustomObject.js";
import { PBRClearCoatConfiguration } from "../../../PBR/pbrClearCoatConfiguration.js";
import { editableInPropertyPage } from "../../../../Decorators/nodeDecorator.js";
import { TBNBlock } from "../Fragment/TBNBlock.js";
/**
* Block used to implement the clear coat module of the PBR material
*/
export class ClearCoatBlock extends NodeMaterialBlock {
/**
* Create a new ClearCoatBlock
* @param name defines the block name
*/
constructor(name) {
super(name, NodeMaterialBlockTargets.Fragment);
this._tangentCorrectionFactorName = "";
/**
* Defines if the F0 value should be remapped to account for the interface change in the material.
*/
this.remapF0OnInterfaceChange = true;
this._isUnique = true;
this.registerInput("intensity", NodeMaterialBlockConnectionPointTypes.Float, false, NodeMaterialBlockTargets.Fragment);
this.registerInput("roughness", NodeMaterialBlockConnectionPointTypes.Float, true, NodeMaterialBlockTargets.Fragment);
this.registerInput("indexOfRefraction", NodeMaterialBlockConnectionPointTypes.Float, true, NodeMaterialBlockTargets.Fragment);
this.registerInput("normalMapColor", NodeMaterialBlockConnectionPointTypes.Color3, true, NodeMaterialBlockTargets.Fragment);
this.registerInput("uv", NodeMaterialBlockConnectionPointTypes.Vector2, true, NodeMaterialBlockTargets.Fragment);
this.registerInput("tintColor", NodeMaterialBlockConnectionPointTypes.Color3, true, NodeMaterialBlockTargets.Fragment);
this.registerInput("tintAtDistance", NodeMaterialBlockConnectionPointTypes.Float, true, NodeMaterialBlockTargets.Fragment);
this.registerInput("tintThickness", NodeMaterialBlockConnectionPointTypes.Float, true, NodeMaterialBlockTargets.Fragment);
this.registerInput("worldTangent", NodeMaterialBlockConnectionPointTypes.Vector4, true);
this.registerInput("worldNormal", NodeMaterialBlockConnectionPointTypes.AutoDetect, true);
this.worldNormal.addExcludedConnectionPointFromAllowedTypes(NodeMaterialBlockConnectionPointTypes.Color4 | NodeMaterialBlockConnectionPointTypes.Vector4 | NodeMaterialBlockConnectionPointTypes.Vector3);
this.registerInput("TBN", NodeMaterialBlockConnectionPointTypes.Object, true, NodeMaterialBlockTargets.VertexAndFragment, new NodeMaterialConnectionPointCustomObject("TBN", this, 0 /* NodeMaterialConnectionPointDirection.Input */, TBNBlock, "TBNBlock"));
this.registerOutput("clearcoat", NodeMaterialBlockConnectionPointTypes.Object, NodeMaterialBlockTargets.Fragment, new NodeMaterialConnectionPointCustomObject("clearcoat", this, 1 /* NodeMaterialConnectionPointDirection.Output */, ClearCoatBlock, "ClearCoatBlock"));
}
/**
* 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("clearcoatOut");
state._excludeVariableName("vClearCoatParams");
state._excludeVariableName("vClearCoatTintParams");
state._excludeVariableName("vClearCoatRefractionParams");
state._excludeVariableName("vClearCoatTangentSpaceParams");
state._excludeVariableName("vGeometricNormaClearCoatW");
}
/**
* Gets the current class name
* @returns the class name
*/
getClassName() {
return "ClearCoatBlock";
}
/**
* Gets the intensity input component
*/
get intensity() {
return this._inputs[0];
}
/**
* Gets the roughness input component
*/
get roughness() {
return this._inputs[1];
}
/**
* Gets the ior input component
*/
get indexOfRefraction() {
return this._inputs[2];
}
/**
* Gets the bump texture input component
*/
get normalMapColor() {
return this._inputs[3];
}
/**
* Gets the uv input component
*/
get uv() {
return this._inputs[4];
}
/**
* Gets the tint color input component
*/
get tintColor() {
return this._inputs[5];
}
/**
* Gets the tint "at distance" input component
*/
get tintAtDistance() {
return this._inputs[6];
}
/**
* Gets the tint thickness input component
*/
get tintThickness() {
return this._inputs[7];
}
/**
* Gets the world tangent input component
*/
get worldTangent() {
return this._inputs[8];
}
/**
* Gets the world normal input component
*/
get worldNormal() {
return this._inputs[9];
}
/**
* Gets the TBN input component
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
get TBN() {
return this._inputs[10];
}
/**
* Gets the clear coat object output component
*/
get clearcoat() {
return this._outputs[0];
}
autoConfigure() {
if (!this.intensity.isConnected) {
const intensityInput = new InputBlock("ClearCoat intensity", NodeMaterialBlockTargets.Fragment, NodeMaterialBlockConnectionPointTypes.Float);
intensityInput.value = 1;
intensityInput.output.connectTo(this.intensity);
}
}
prepareDefines(defines) {
defines.setValue("CLEARCOAT", true);
defines.setValue("CLEARCOAT_TEXTURE", false, true);
defines.setValue("CLEARCOAT_USE_ROUGHNESS_FROM_MAINTEXTURE", true, true);
defines.setValue("CLEARCOAT_TINT", this.tintColor.isConnected || this.tintThickness.isConnected || this.tintAtDistance.isConnected, true);
defines.setValue("CLEARCOAT_BUMP", this.normalMapColor.isConnected, true);
defines.setValue("CLEARCOAT_DEFAULTIOR", this.indexOfRefraction.isConnected ? this.indexOfRefraction.connectInputBlock.value === PBRClearCoatConfiguration._DefaultIndexOfRefraction : true, true);
defines.setValue("CLEARCOAT_REMAP_F0", this.remapF0OnInterfaceChange, true);
}
bind(effect, nodeMaterial, mesh) {
super.bind(effect, nodeMaterial, mesh);
// Clear Coat Refraction params
const indexOfRefraction = this.indexOfRefraction.connectInputBlock?.value ?? PBRClearCoatConfiguration._DefaultIndexOfRefraction;
const a = 1 - indexOfRefraction;
const b = 1 + indexOfRefraction;
const f0 = Math.pow(-a / b, 2); // Schlicks approx: (ior1 - ior2) / (ior1 + ior2) where ior2 for air is close to vacuum = 1.
const eta = 1 / indexOfRefraction;
effect.setFloat4("vClearCoatRefractionParams", f0, eta, a, b);
// Clear Coat tangent space params
const mainPBRBlock = this.clearcoat.hasEndpoints ? this.clearcoat.endpoints[0].ownerBlock : null;
const perturbedNormalBlock = mainPBRBlock?.perturbedNormal.isConnected ? mainPBRBlock.perturbedNormal.connectedPoint.ownerBlock : null;
if (this._scene._mirroredCameraPosition) {
effect.setFloat2("vClearCoatTangentSpaceParams", perturbedNormalBlock?.invertX ? 1.0 : -1.0, perturbedNormalBlock?.invertY ? 1.0 : -1.0);
}
else {
effect.setFloat2("vClearCoatTangentSpaceParams", perturbedNormalBlock?.invertX ? -1.0 : 1.0, perturbedNormalBlock?.invertY ? -1.0 : 1.0);
}
if (mesh) {
effect.setFloat(this._tangentCorrectionFactorName, mesh.getWorldMatrix().determinant() < 0 ? -1 : 1);
}
}
_generateTBNSpace(state, worldPositionVarName, worldNormalVarName) {
let code = "";
const comments = `//${this.name}`;
const worldTangent = this.worldTangent;
const isWebGPU = state.shaderLanguage === 1 /* ShaderLanguage.WGSL */;
if (!isWebGPU) {
state._emitExtension("derivatives", "#extension GL_OES_standard_derivatives : enable");
}
const tangentReplaceString = { search: /defined\(TANGENT\)/g, replace: worldTangent.isConnected ? "defined(TANGENT)" : "defined(IGNORE)" };
const tbn = this.TBN;
if (tbn.isConnected) {
state.compilationString += `
#ifdef TBNBLOCK
${isWebGPU ? "var TBN" : "mat3 TBN"} = ${tbn.associatedVariableName};
#endif
`;
}
else if (worldTangent.isConnected) {
code += `${state._declareLocalVar("tbnNormal", NodeMaterialBlockConnectionPointTypes.Vector3)} = normalize(${worldNormalVarName}.xyz);\n`;
code += `${state._declareLocalVar("tbnTangent", NodeMaterialBlockConnectionPointTypes.Vector3)} = normalize(${worldTangent.associatedVariableName}.xyz);\n`;
code += `${state._declareLocalVar("tbnBitangent", NodeMaterialBlockConnectionPointTypes.Vector3)} = cross(tbnNormal, tbnTangent) * ${this._tangentCorrectionFactorName};\n`;
code += `${isWebGPU ? "var vTBN" : "mat3 vTBN"} = ${isWebGPU ? "mat3x3f" : "mat3"}(tbnTangent, tbnBitangent, tbnNormal);\n`;
}
state._emitFunctionFromInclude("bumpFragmentMainFunctions", comments, {
replaceStrings: [tangentReplaceString],
});
return code;
}
/** @internal */
static _GetInitializationCode(state, ccBlock) {
let code = "";
const intensity = ccBlock?.intensity.isConnected ? ccBlock.intensity.associatedVariableName : "1.";
const roughness = ccBlock?.roughness.isConnected ? ccBlock.roughness.associatedVariableName : "0.";
const tintColor = ccBlock?.tintColor.isConnected ? ccBlock.tintColor.associatedVariableName : `vec3${state.fSuffix}(1.)`;
const tintThickness = ccBlock?.tintThickness.isConnected ? ccBlock.tintThickness.associatedVariableName : "1.";
code += `
#ifdef CLEARCOAT
${state._declareLocalVar("vClearCoatParams", NodeMaterialBlockConnectionPointTypes.Vector2)} = vec2${state.fSuffix}(${intensity}, ${roughness});
${state._declareLocalVar("vClearCoatTintParams", NodeMaterialBlockConnectionPointTypes.Vector4)} = vec4${state.fSuffix}(${tintColor}, ${tintThickness});
#endif\n`;
return code;
}
/**
* Gets the main code of the block (fragment side)
* @param state current state of the node material building
* @param ccBlock instance of a ClearCoatBlock or null if the code must be generated without an active clear coat module
* @param reflectionBlock instance of a ReflectionBlock null if the code must be generated without an active reflection module
* @param worldPosVarName name of the variable holding the world position
* @param generateTBNSpace if true, the code needed to create the TBN coordinate space is generated
* @param vTBNAvailable indicate that the vTBN variable is already existing because it has already been generated by another block (PerturbNormal or Anisotropy)
* @param worldNormalVarName name of the variable holding the world normal
* @returns the shader code
*/
static GetCode(state, ccBlock, reflectionBlock, worldPosVarName, generateTBNSpace, vTBNAvailable, worldNormalVarName) {
let code = "";
const normalMapColor = ccBlock?.normalMapColor.isConnected ? ccBlock.normalMapColor.associatedVariableName : `vec3${state.fSuffix}(0.)`;
const uv = ccBlock?.uv.isConnected ? ccBlock.uv.associatedVariableName : `vec2${state.fSuffix}(0.)`;
const tintAtDistance = ccBlock?.tintAtDistance.isConnected ? ccBlock.tintAtDistance.associatedVariableName : "1.";
const tintTexture = `vec4${state.fSuffix}(0.)`;
if (ccBlock) {
state._emitUniformFromString("vClearCoatRefractionParams", NodeMaterialBlockConnectionPointTypes.Vector4);
state._emitUniformFromString("vClearCoatTangentSpaceParams", NodeMaterialBlockConnectionPointTypes.Vector2);
const normalShading = ccBlock.worldNormal;
code += `${state._declareLocalVar("vGeometricNormaClearCoatW", NodeMaterialBlockConnectionPointTypes.Vector3)} = ${normalShading.isConnected ? "normalize(" + normalShading.associatedVariableName + ".xyz)" : "geometricNormalW"};\n`;
}
else {
code += `${state._declareLocalVar("vGeometricNormaClearCoatW", NodeMaterialBlockConnectionPointTypes.Vector3)} = geometricNormalW;\n`;
}
if (generateTBNSpace && ccBlock) {
code += ccBlock._generateTBNSpace(state, worldPosVarName, worldNormalVarName);
vTBNAvailable = ccBlock.worldTangent.isConnected;
}
const isWebGPU = state.shaderLanguage === 1 /* ShaderLanguage.WGSL */;
code += `${isWebGPU ? "var clearcoatOut: clearcoatOutParams" : "clearcoatOutParams clearcoatOut"};
#ifdef CLEARCOAT
clearcoatOut = clearcoatBlock(
${worldPosVarName}.xyz
, vGeometricNormaClearCoatW
, viewDirectionW
, vClearCoatParams
, specularEnvironmentR0
#ifdef CLEARCOAT_TEXTURE
, vec2${state.fSuffix}(0.)
#endif
#ifdef CLEARCOAT_TINT
, vClearCoatTintParams
, ${tintAtDistance}
, ${isWebGPU ? "uniforms." : ""}vClearCoatRefractionParams
#ifdef CLEARCOAT_TINT_TEXTURE
, ${tintTexture}
#endif
#endif
#ifdef CLEARCOAT_BUMP
, vec2${state.fSuffix}(0., 1.)
, vec4${state.fSuffix}(${normalMapColor}, 0.)
, ${uv}
#if defined(${vTBNAvailable ? "TANGENT" : "IGNORE"}) && defined(NORMAL)
, vTBN
#else
, ${isWebGPU ? "uniforms." : ""}vClearCoatTangentSpaceParams
#endif
#ifdef OBJECTSPACE_NORMALMAP
, normalMatrix
#endif
#endif
#if defined(FORCENORMALFORWARD) && defined(NORMAL)
, faceNormal
#endif
#ifdef REFLECTION
, ${isWebGPU ? "uniforms." : ""}${reflectionBlock?._vReflectionMicrosurfaceInfosName}
, ${reflectionBlock?._vReflectionInfosName}
, ${reflectionBlock?.reflectionColor}
, ${isWebGPU ? "uniforms." : ""}vLightingIntensity
#ifdef ${reflectionBlock?._define3DName}
, ${reflectionBlock?._cubeSamplerName}
${isWebGPU ? `, ${reflectionBlock?._cubeSamplerName}Sampler` : ""}
#else
, ${reflectionBlock?._2DSamplerName}
${isWebGPU ? `, ${reflectionBlock?._2DSamplerName}Sampler` : ""}
#endif
#ifndef LODBASEDMICROSFURACE
#ifdef ${reflectionBlock?._define3DName}
, ${reflectionBlock?._cubeSamplerName}
${isWebGPU ? `, ${reflectionBlock?._cubeSamplerName}Sampler` : ""}
, ${reflectionBlock?._cubeSamplerName}
${isWebGPU ? `, ${reflectionBlock?._cubeSamplerName}Sampler` : ""}
#else
, ${reflectionBlock?._2DSamplerName}
${isWebGPU ? `, ${reflectionBlock?._2DSamplerName}Sampler` : ""}
, ${reflectionBlock?._2DSamplerName}
${isWebGPU ? `, ${reflectionBlock?._2DSamplerName}Sampler` : ""}
#endif
#endif
#endif
#if defined(CLEARCOAT_BUMP) || defined(TWOSIDEDLIGHTING)
, (${state._generateTernary("1.", "-1.", isWebGPU ? "fragmentInputs.frontFacing" : "gl_FrontFacing")})
#endif
);
#else
clearcoatOut.specularEnvironmentR0 = specularEnvironmentR0;
#endif\n`;
return code;
}
_buildBlock(state) {
this._scene = state.sharedData.scene;
if (state.target === NodeMaterialBlockTargets.Fragment) {
state.sharedData.bindableBlocks.push(this);
state.sharedData.blocksWithDefines.push(this);
this._tangentCorrectionFactorName = state._getFreeDefineName("tangentCorrectionFactor");
state._emitUniformFromString(this._tangentCorrectionFactorName, NodeMaterialBlockConnectionPointTypes.Float);
}
return this;
}
_dumpPropertiesCode() {
let codeString = super._dumpPropertiesCode();
codeString += `${this._codeVariableName}.remapF0OnInterfaceChange = ${this.remapF0OnInterfaceChange};\n`;
return codeString;
}
serialize() {
const serializationObject = super.serialize();
serializationObject.remapF0OnInterfaceChange = this.remapF0OnInterfaceChange;
return serializationObject;
}
_deserialize(serializationObject, scene, rootUrl) {
super._deserialize(serializationObject, scene, rootUrl);
this.remapF0OnInterfaceChange = serializationObject.remapF0OnInterfaceChange ?? true;
}
}
__decorate([
editableInPropertyPage("Remap F0 on interface change", 0 /* PropertyTypeForEdition.Boolean */, "ADVANCED", { embedded: true })
], ClearCoatBlock.prototype, "remapF0OnInterfaceChange", void 0);
RegisterClass("BABYLON.ClearCoatBlock", ClearCoatBlock);
//# sourceMappingURL=clearCoatBlock.js.map