@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.
1,089 lines (1,088 loc) • 68.9 kB
JavaScript
import { Matrix } from "../Maths/math.vector.js";
import { VertexBuffer } from "../Buffers/buffer.js";
import { Texture } from "../Materials/Textures/texture.js";
import { MultiRenderTarget } from "../Materials/Textures/multiRenderTarget.js";
import { Color4 } from "../Maths/math.color.js";
import { _WarnImport } from "../Misc/devTools.js";
import { Material } from "../Materials/material.js";
import "../Shaders/geometry.fragment.js";
import "../Shaders/geometry.vertex.js";
import { MaterialFlags } from "../Materials/materialFlags.js";
import { AddClipPlaneUniforms, BindClipPlane, PrepareStringDefinesForClipPlanes } from "../Materials/clipPlaneMaterialHelper.js";
import { BindMorphTargetParameters, BindSceneUniformBuffer, PrepareDefinesAndAttributesForMorphTargets, PushAttributesForInstances, PrepareUniformsAndSamplersForIBL, PrepareDefinesForIBL, } from "../Materials/materialHelper.functions.js";
import "../Engines/Extensions/engine.multiRender.js";
const Samplers = [
"diffuseSampler",
"bumpSampler",
"reflectivitySampler",
"albedoSampler",
"morphTargets",
"boneSampler",
"transmissionWeightSampler",
"subsurfaceWeightSampler",
"iblShadowSampler",
];
/** list the uniforms used by the geometry renderer */
const Uniforms = [
"world",
"mBones",
"viewProjection",
"diffuseMatrix",
"view",
"previousWorld",
"previousViewProjection",
"mPreviousBones",
"bumpMatrix",
"reflectivityMatrix",
"albedoMatrix",
"reflectivityColor",
"albedoColor",
"reflectionMatrix",
"vTransmissionWeight",
"vSubsurfaceWeight",
"vEyePosition",
"vTransmissionScatterAnisotropy",
"vSubsurfaceScatterAnisotropy",
"shadowTextureSize",
"metallic",
"glossiness",
"vTangentSpaceParams",
"vBumpInfos",
"morphTargetInfluences",
"morphTargetCount",
"morphTargetTextureInfo",
"morphTargetTextureIndices",
"boneTextureInfo",
];
PrepareUniformsAndSamplersForIBL(Uniforms, Samplers, true);
AddClipPlaneUniforms(Uniforms);
/**
* This renderer is helpful to fill one of the render target with a geometry buffer.
*/
export class GeometryBufferRenderer {
/**
* Gets a boolean indicating if normals are encoded in the [0,1] range in the render target. If true, you should do `normal = normal_rt * 2.0 - 1.0` to get the right normal
*/
get normalsAreUnsigned() {
return this._normalsAreUnsigned;
}
/**
* @internal
* Sets up internal structures to share outputs with PrePassRenderer
* This method should only be called by the PrePassRenderer itself
*/
_linkPrePassRenderer(prePassRenderer) {
this._linkedWithPrePass = true;
this._prePassRenderer = prePassRenderer;
if (this._multiRenderTarget) {
// prevents clearing of the RT since it's done by prepass
this._multiRenderTarget.onClearObservable.clear();
this._multiRenderTarget.onClearObservable.add(() => {
// pass
});
}
}
/**
* @internal
* Separates internal structures from PrePassRenderer so the geometry buffer can now operate by itself.
* This method should only be called by the PrePassRenderer itself
*/
_unlinkPrePassRenderer() {
this._linkedWithPrePass = false;
this._createRenderTargets();
}
/**
* @internal
* Resets the geometry buffer layout
*/
_resetLayout() {
this._enableDepth = true;
this._enableNormal = true;
this._enablePosition = false;
this._enableReflectivity = false;
this._enableVelocity = false;
this._enableVelocityLinear = false;
this._enableScreenspaceDepth = false;
this._enableIrradiance = false;
this._attachmentsFromPrePass = [];
}
/**
* @internal
* Replaces a texture in the geometry buffer renderer
* Useful when linking textures of the prepass renderer
*/
_forceTextureType(geometryBufferType, index) {
if (geometryBufferType === GeometryBufferRenderer.POSITION_TEXTURE_TYPE) {
this._positionIndex = index;
this._enablePosition = true;
}
else if (geometryBufferType === GeometryBufferRenderer.VELOCITY_TEXTURE_TYPE) {
this._velocityIndex = index;
this._enableVelocity = true;
}
else if (geometryBufferType === GeometryBufferRenderer.VELOCITY_LINEAR_TEXTURE_TYPE) {
this._velocityLinearIndex = index;
this._enableVelocityLinear = true;
}
else if (geometryBufferType === GeometryBufferRenderer.REFLECTIVITY_TEXTURE_TYPE) {
this._reflectivityIndex = index;
this._enableReflectivity = true;
}
else if (geometryBufferType === GeometryBufferRenderer.DEPTH_TEXTURE_TYPE) {
this._depthIndex = index;
this._enableDepth = true;
}
else if (geometryBufferType === GeometryBufferRenderer.NORMAL_TEXTURE_TYPE) {
this._normalIndex = index;
this._enableNormal = true;
}
else if (geometryBufferType === GeometryBufferRenderer.SCREENSPACE_DEPTH_TEXTURE_TYPE) {
this._screenspaceDepthIndex = index;
this._enableScreenspaceDepth = true;
}
else if (geometryBufferType === GeometryBufferRenderer.IRRADIANCE_TEXTURE_TYPE) {
this._irradianceIndex = index;
this._enableIrradiance = true;
}
}
/**
* @internal
* Sets texture attachments
* Useful when linking textures of the prepass renderer
*/
_setAttachments(attachments) {
this._attachmentsFromPrePass = attachments;
}
/**
* @internal
* Replaces the first texture which is hard coded as a depth texture in the geometry buffer
* Useful when linking textures of the prepass renderer
*/
_linkInternalTexture(internalTexture) {
this._multiRenderTarget.setInternalTexture(internalTexture, 0, false);
}
/**
* Gets the render list (meshes to be rendered) used in the G buffer.
*/
get renderList() {
return this._multiRenderTarget.renderList;
}
/**
* Set the render list (meshes to be rendered) used in the G buffer.
*/
set renderList(meshes) {
this._multiRenderTarget.renderList = meshes;
}
/**
* Gets whether or not G buffer are supported by the running hardware.
* This requires draw buffer supports
*/
get isSupported() {
return this._multiRenderTarget.isSupported;
}
/**
* Returns the index of the given texture type in the G-Buffer textures array
* @param textureType The texture type constant. For example GeometryBufferRenderer.POSITION_TEXTURE_INDEX
* @returns the index of the given texture type in the G-Buffer textures array
*/
getTextureIndex(textureType) {
switch (textureType) {
case GeometryBufferRenderer.POSITION_TEXTURE_TYPE:
return this._positionIndex;
case GeometryBufferRenderer.VELOCITY_TEXTURE_TYPE:
return this._velocityIndex;
case GeometryBufferRenderer.VELOCITY_LINEAR_TEXTURE_TYPE:
return this._velocityLinearIndex;
case GeometryBufferRenderer.REFLECTIVITY_TEXTURE_TYPE:
return this._reflectivityIndex;
case GeometryBufferRenderer.DEPTH_TEXTURE_TYPE:
return this._depthIndex;
case GeometryBufferRenderer.NORMAL_TEXTURE_TYPE:
return this._normalIndex;
case GeometryBufferRenderer.SCREENSPACE_DEPTH_TEXTURE_TYPE:
return this._screenspaceDepthIndex;
case GeometryBufferRenderer.IRRADIANCE_TEXTURE_TYPE:
return this._irradianceIndex;
default:
return -1;
}
}
/**
* @returns a boolean indicating if object's depths are enabled for the G buffer.
*/
get enableDepth() {
return this._enableDepth;
}
/**
* Sets whether or not object's depths are enabled for the G buffer.
*/
set enableDepth(enable) {
this._enableDepth = enable;
if (!this._linkedWithPrePass) {
this.dispose();
this._createRenderTargets();
}
}
/**
* @returns a boolean indicating if object's normals are enabled for the G buffer.
*/
get enableNormal() {
return this._enableNormal;
}
/**
* Sets whether or not object's normals are enabled for the G buffer.
*/
set enableNormal(enable) {
this._enableNormal = enable;
if (!this._linkedWithPrePass) {
this.dispose();
this._createRenderTargets();
}
}
/**
* @returns a boolean indicating if objects positions are enabled for the G buffer.
*/
get enablePosition() {
return this._enablePosition;
}
/**
* Sets whether or not objects positions are enabled for the G buffer.
*/
set enablePosition(enable) {
this._enablePosition = enable;
// PrePass handles index and texture links
if (!this._linkedWithPrePass) {
this.dispose();
this._createRenderTargets();
}
}
/**
* @returns a boolean indicating if objects velocities are enabled for the G buffer.
*/
get enableVelocity() {
return this._enableVelocity;
}
/**
* Sets whether or not objects velocities are enabled for the G buffer.
*/
set enableVelocity(enable) {
this._enableVelocity = enable;
if (!enable) {
this._previousTransformationMatrices = {};
}
if (!this._linkedWithPrePass) {
this.dispose();
this._createRenderTargets();
}
this._scene.needsPreviousWorldMatrices = enable;
}
/**
* @returns a boolean indicating if object's linear velocities are enabled for the G buffer.
*/
get enableVelocityLinear() {
return this._enableVelocityLinear;
}
/**
* Sets whether or not object's linear velocities are enabled for the G buffer.
*/
set enableVelocityLinear(enable) {
this._enableVelocityLinear = enable;
if (!this._linkedWithPrePass) {
this.dispose();
this._createRenderTargets();
}
}
/**
* Gets a boolean indicating if objects reflectivity are enabled in the G buffer.
*/
get enableReflectivity() {
return this._enableReflectivity;
}
/**
* Sets whether or not objects reflectivity are enabled for the G buffer.
* For Metallic-Roughness workflow with ORM texture, we assume that ORM texture is defined according to the default layout:
* pbr.useRoughnessFromMetallicTextureAlpha = false;
* pbr.useRoughnessFromMetallicTextureGreen = true;
* pbr.useMetallnessFromMetallicTextureBlue = true;
*/
set enableReflectivity(enable) {
this._enableReflectivity = enable;
if (!this._linkedWithPrePass) {
this.dispose();
this._createRenderTargets();
}
}
/**
* Sets whether or not objects screenspace depth are enabled for the G buffer.
*/
get enableScreenspaceDepth() {
return this._enableScreenspaceDepth;
}
set enableScreenspaceDepth(enable) {
this._enableScreenspaceDepth = enable;
if (!this._linkedWithPrePass) {
this.dispose();
this._createRenderTargets();
}
}
/**
* Gets a boolean indicating if objects irradiance are enabled in the G buffer.
*/
get enableIrradiance() {
return this._enableIrradiance;
}
/**
* Sets whether or not objects irradiance are enabled for the G buffer.
*/
set enableIrradiance(enable) {
this._enableIrradiance = enable;
if (!this._linkedWithPrePass) {
this.dispose();
this._createRenderTargets();
}
}
/**
* Gets the scene associated with the buffer.
*/
get scene() {
return this._scene;
}
/**
* Gets the ratio used by the buffer during its creation.
* How big is the buffer related to the main canvas.
*/
get ratio() {
return typeof this._ratioOrDimensions === "object" ? 1 : this._ratioOrDimensions;
}
/**
* Gets the shader language used in this material.
*/
get shaderLanguage() {
return this._shaderLanguage;
}
/**
* Creates a new G Buffer for the scene
* @param scene The scene the buffer belongs to
* @param ratioOrDimensions How big is the buffer related to the main canvas (default: 1). You can also directly pass a width and height for the generated textures
* @param depthFormat Format of the depth texture (default: 15)
* @param textureTypesAndFormats The types, formats and optional sampling modes of textures to create as render targets.
* If not provided, all textures will be RGBA and float or half float, depending on the engine capabilities.
*/
constructor(scene, ratioOrDimensions = 1, depthFormat = 15, textureTypesAndFormats) {
/**
* Dictionary used to store the previous transformation matrices of each rendered mesh
* in order to compute objects velocities when enableVelocity is set to "true"
* @internal
*/
this._previousTransformationMatrices = {};
/**
* Dictionary used to store the previous bones transformation matrices of each rendered mesh
* in order to compute objects velocities when enableVelocity is set to "true"
* @internal
*/
this._previousBonesTransformationMatrices = {};
/**
* Array used to store the ignored skinned meshes while computing velocity map (typically used by the motion blur post-process).
* Avoids computing bones velocities and computes only mesh's velocity itself (position, rotation, scaling).
*/
this.excludedSkinnedMeshesFromVelocity = [];
/** Gets or sets a boolean indicating if transparent meshes should be rendered */
this.renderTransparentMeshes = true;
/**
* Gets or sets a boolean indicating if normals should be generated in world space (default: false, meaning normals are generated in view space)
*/
this.generateNormalsInWorldSpace = false;
this._normalsAreUnsigned = false;
this._resizeObserver = null;
this._enableDepth = true;
this._enableNormal = true;
this._enablePosition = false;
this._enableVelocity = false;
this._enableVelocityLinear = false;
this._enableReflectivity = false;
this._enableScreenspaceDepth = false;
this._enableIrradiance = false;
this._clearColor = new Color4(0, 0, 0, 0);
this._clearDepthColor = new Color4(0, 0, 0, 1); // sets an invalid value by default - depth in the depth texture is view.z, so 0 is not possible because view.z can't be less than camera.minZ
this._positionIndex = -1;
this._velocityIndex = -1;
this._velocityLinearIndex = -1;
this._reflectivityIndex = -1;
this._depthIndex = -1;
this._normalIndex = -1;
this._screenspaceDepthIndex = -1;
this._irradianceIndex = -1;
this._linkedWithPrePass = false;
/**
* This will store a mask in the alpha channel of the irradiance texture to indicate which pixels have
* scattering and should be taken into account when applying image-based lighting.
*/
this.generateIrradianceWithScatterMask = false;
/**
* If set to true (default: false), the depth texture will be cleared with the depth value corresponding to the far plane (1 in normal mode, 0 in reverse depth buffer mode)
* If set to false, the depth texture is always cleared with 0.
*/
this.useSpecificClearForDepthTexture = false;
/** Shader language used by the material */
this._shaderLanguage = 0 /* ShaderLanguage.GLSL */;
this._shadersLoaded = false;
this._scene = scene;
this._ratioOrDimensions = ratioOrDimensions;
this._useUbo = scene.getEngine().supportsUniformBuffers;
this._depthFormat = depthFormat;
this._textureTypesAndFormats = textureTypesAndFormats || {};
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this._initShaderSourceAsync();
GeometryBufferRenderer._SceneComponentInitialization(this._scene);
// Render target
this._createRenderTargets();
}
async _initShaderSourceAsync() {
const engine = this._scene.getEngine();
if (engine.isWebGPU && !GeometryBufferRenderer.ForceGLSL) {
this._shaderLanguage = 1 /* ShaderLanguage.WGSL */;
await Promise.all([import("../ShadersWGSL/geometry.vertex.js"), import("../ShadersWGSL/geometry.fragment.js")]);
}
else {
await Promise.all([import("../Shaders/geometry.vertex.js"), import("../Shaders/geometry.fragment.js")]);
}
this._shadersLoaded = true;
}
/**
* Checks whether everything is ready to render a submesh to the G buffer.
* @param subMesh the submesh to check readiness for
* @param useInstances is the mesh drawn using instance or not
* @returns true if ready otherwise false
*/
isReady(subMesh, useInstances) {
if (!this._shadersLoaded) {
return false;
}
const material = subMesh.getMaterial();
if (material && material.disableDepthWrite) {
return false;
}
const defines = [];
const attribs = [VertexBuffer.PositionKind];
const mesh = subMesh.getMesh();
const hasNormals = mesh.isVerticesDataPresent(VertexBuffer.NormalKind);
if (hasNormals) {
defines.push("#define HAS_NORMAL_ATTRIBUTE");
attribs.push(VertexBuffer.NormalKind);
}
let uv1 = false;
let uv2 = false;
const color = false;
if (material) {
let needUv = false;
// Alpha test
if (material.needAlphaTestingForMesh(mesh) && material.getAlphaTestTexture()) {
defines.push("#define ALPHATEST");
defines.push(`#define ALPHATEST_UV${material.getAlphaTestTexture().coordinatesIndex + 1}`);
needUv = true;
}
// Normal map texture
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;
}
if (this._enableReflectivity) {
let metallicWorkflow = false;
// for PBR materials: cf. https://doc.babylonjs.com/features/featuresDeepDive/materials/using/masterPBR
if (material.getClassName() === "PBRMetallicRoughnessMaterial") {
// if it is a PBR material in MetallicRoughness Mode:
if (material.metallicRoughnessTexture) {
defines.push("#define ORMTEXTURE");
defines.push(`#define REFLECTIVITY_UV${material.metallicRoughnessTexture.coordinatesIndex + 1}`);
defines.push("#define METALLICWORKFLOW");
needUv = true;
metallicWorkflow = true;
}
// null or undefined
if (material.metallic != null) {
defines.push("#define METALLIC");
defines.push("#define METALLICWORKFLOW");
metallicWorkflow = true;
}
// null or undefined
if (material.roughness != null) {
defines.push("#define ROUGHNESS");
defines.push("#define METALLICWORKFLOW");
metallicWorkflow = true;
}
if (metallicWorkflow) {
if (material.baseTexture) {
defines.push("#define ALBEDOTEXTURE");
defines.push(`#define ALBEDO_UV${material.baseTexture.coordinatesIndex + 1}`);
if (material.baseTexture.gammaSpace) {
defines.push("#define GAMMAALBEDO");
}
needUv = true;
}
if (material.baseColor) {
defines.push("#define ALBEDOCOLOR");
}
}
}
else if (material.getClassName() === "PBRSpecularGlossinessMaterial") {
// if it is a PBR material in Specular/Glossiness Mode:
if (material.specularGlossinessTexture) {
defines.push("#define SPECULARGLOSSINESSTEXTURE");
defines.push(`#define REFLECTIVITY_UV${material.specularGlossinessTexture.coordinatesIndex + 1}`);
needUv = true;
if (material.specularGlossinessTexture.gammaSpace) {
defines.push("#define GAMMAREFLECTIVITYTEXTURE");
}
}
else {
if (material.specularColor) {
defines.push("#define REFLECTIVITYCOLOR");
}
}
// null or undefined
if (material.glossiness != null) {
defines.push("#define GLOSSINESS");
}
}
else if (material.getClassName() === "PBRMaterial") {
// if it is the bigger PBRMaterial
if (material.metallicTexture) {
defines.push("#define ORMTEXTURE");
defines.push(`#define REFLECTIVITY_UV${material.metallicTexture.coordinatesIndex + 1}`);
defines.push("#define METALLICWORKFLOW");
needUv = true;
metallicWorkflow = true;
}
// null or undefined
if (material.metallic != null) {
defines.push("#define METALLIC");
defines.push("#define METALLICWORKFLOW");
metallicWorkflow = true;
}
// null or undefined
if (material.roughness != null) {
defines.push("#define ROUGHNESS");
defines.push("#define METALLICWORKFLOW");
metallicWorkflow = true;
}
if (metallicWorkflow) {
if (material.albedoTexture) {
defines.push("#define ALBEDOTEXTURE");
defines.push(`#define ALBEDO_UV${material.albedoTexture.coordinatesIndex + 1}`);
if (material.albedoTexture.gammaSpace) {
defines.push("#define GAMMAALBEDO");
}
needUv = true;
}
if (material.albedoColor) {
defines.push("#define ALBEDOCOLOR");
}
}
else {
// SpecularGlossiness Model
if (material.reflectivityTexture) {
defines.push("#define SPECULARGLOSSINESSTEXTURE");
defines.push(`#define REFLECTIVITY_UV${material.reflectivityTexture.coordinatesIndex + 1}`);
if (material.reflectivityTexture.gammaSpace) {
defines.push("#define GAMMAREFLECTIVITYTEXTURE");
}
needUv = true;
}
else if (material.reflectivityColor) {
defines.push("#define REFLECTIVITYCOLOR");
}
// null or undefined
if (material.microSurface != null) {
defines.push("#define GLOSSINESS");
}
}
}
else if (material.getClassName() === "StandardMaterial") {
// if StandardMaterial:
if (material.specularTexture) {
defines.push("#define REFLECTIVITYTEXTURE");
defines.push(`#define REFLECTIVITY_UV${material.specularTexture.coordinatesIndex + 1}`);
if (material.specularTexture.gammaSpace) {
defines.push("#define GAMMAREFLECTIVITYTEXTURE");
}
needUv = true;
}
if (material.specularColor) {
defines.push("#define REFLECTIVITYCOLOR");
}
}
else if (material.getClassName() === "OpenPBRMaterial") {
const openpbrMaterial = material;
defines.push("#define METALLIC");
defines.push("#define ROUGHNESS");
if (openpbrMaterial._useRoughnessFromMetallicTextureGreen && openpbrMaterial.baseMetalnessTexture) {
defines.push("#define ORMTEXTURE");
defines.push(`#define REFLECTIVITY_UV${openpbrMaterial.baseMetalnessTexture.coordinatesIndex + 1}`);
needUv = true;
}
else if (openpbrMaterial.baseMetalnessTexture) {
defines.push("#define METALLIC_TEXTURE");
defines.push(`#define METALLIC_UV${openpbrMaterial.baseMetalnessTexture.coordinatesIndex + 1}`);
needUv = true;
}
else if (openpbrMaterial.specularRoughnessTexture) {
defines.push("#define ROUGHNESS_TEXTURE");
defines.push(`#define ROUGHNESS_UV${openpbrMaterial.specularRoughnessTexture.coordinatesIndex + 1}`);
needUv = true;
}
if (openpbrMaterial.baseColorTexture) {
defines.push("#define ALBEDOTEXTURE");
defines.push(`#define ALBEDO_UV${openpbrMaterial.baseColorTexture.coordinatesIndex + 1}`);
if (openpbrMaterial.baseColorTexture.gammaSpace) {
defines.push("#define GAMMAALBEDO");
}
needUv = true;
}
if (openpbrMaterial.baseColor) {
defines.push("#define ALBEDOCOLOR");
}
}
}
if (this._enableIrradiance && this.generateIrradianceWithScatterMask) {
defines.push("#define IRRADIANCE_SCATTER_MASK");
if (material.getClassName() === "OpenPBRMaterial") {
const openpbrMaterial = material;
if (openpbrMaterial.subsurfaceWeight > 0) {
if (openpbrMaterial.subsurfaceWeightTexture) {
defines.push("#define SUBSURFACE_WEIGHT");
defines.push(`#define SUBSURFACEWEIGHT_UV${openpbrMaterial.subsurfaceWeightTexture.coordinatesIndex + 1}`);
needUv = true;
}
}
if (openpbrMaterial.transmissionWeight > 0) {
if (openpbrMaterial.transmissionWeightTexture) {
defines.push("#define TRANSMISSION_WEIGHT");
defines.push(`#define TRANSMISSIONWEIGHT_UV${openpbrMaterial.transmissionWeightTexture.coordinatesIndex + 1}`);
needUv = true;
}
}
}
}
if (needUv) {
defines.push("#define NEED_UV");
if (mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
attribs.push(VertexBuffer.UVKind);
defines.push("#define UV1");
uv1 = true;
}
if (mesh.isVerticesDataPresent(VertexBuffer.UV2Kind)) {
attribs.push(VertexBuffer.UV2Kind);
defines.push("#define UV2");
uv2 = true;
}
}
}
// Buffers
if (this._enableDepth) {
defines.push("#define DEPTH");
defines.push("#define DEPTH_INDEX " + this._depthIndex);
}
if (this._enableNormal) {
defines.push("#define NORMAL");
defines.push("#define NORMAL_INDEX " + this._normalIndex);
}
if (this._enablePosition) {
defines.push("#define POSITION");
defines.push("#define POSITION_INDEX " + this._positionIndex);
}
if (this._enableVelocity) {
defines.push("#define VELOCITY");
defines.push("#define VELOCITY_INDEX " + this._velocityIndex);
if (this.excludedSkinnedMeshesFromVelocity.indexOf(mesh) === -1) {
defines.push("#define BONES_VELOCITY_ENABLED");
}
}
if (this._enableVelocityLinear) {
defines.push("#define VELOCITY_LINEAR");
defines.push("#define VELOCITY_LINEAR_INDEX " + this._velocityLinearIndex);
if (this.excludedSkinnedMeshesFromVelocity.indexOf(mesh) === -1) {
defines.push("#define BONES_VELOCITY_ENABLED");
}
}
if (this._enableReflectivity) {
defines.push("#define REFLECTIVITY");
defines.push("#define REFLECTIVITY_INDEX " + this._reflectivityIndex);
}
if (this._enableScreenspaceDepth) {
if (this._screenspaceDepthIndex !== -1) {
defines.push("#define SCREENSPACE_DEPTH_INDEX " + this._screenspaceDepthIndex);
defines.push("#define SCREENSPACE_DEPTH");
}
}
if (this._enableIrradiance) {
if (this._irradianceIndex !== -1) {
defines.push("#define IRRADIANCE_INDEX " + this._irradianceIndex);
defines.push("#define IRRADIANCE");
// Check if scene has IBL setup
const scene = this._scene;
if (scene.environmentTexture) {
const iblDefines = {};
let realtime = false;
let realtimeQuality = 0;
if (material.getClassName() === "OpenPBRMaterial" ||
material.getClassName() === "StandardMaterial" ||
material.getClassName() === "PBRMetallicRoughnessMaterial" ||
material.getClassName() === "PBRSpecularGlossinessMaterial" ||
material.getClassName() === "PBRMaterial") {
realtime = material.realtimeFiltering ? true : false;
realtimeQuality = material.realtimeFilteringQuality || 0;
}
PrepareDefinesForIBL(scene, scene.environmentTexture, iblDefines, realtime, realtimeQuality, true);
for (const define in iblDefines) {
if (iblDefines[define]) {
defines.push("#define " + define);
}
}
if (!iblDefines.USEIRRADIANCEMAP) {
defines.push("#define SPHERICAL_HARMONICS");
}
const iblShadowsPipeline = scene.postProcessRenderPipelineManager.supportedPipelines.find((p) => p.getClassName() === "IBLShadowsRenderPipeline");
if (iblShadowsPipeline) {
const pipeline = iblShadowsPipeline;
const shadowTexture = pipeline._getAccumulatedTexture();
if (shadowTexture) {
defines.push("#define IBL_SHADOW_TEXTURE");
if (pipeline.coloredShadows) {
defines.push("#define COLORED_IBL_SHADOWS");
}
}
}
}
}
}
if (this.generateNormalsInWorldSpace) {
defines.push("#define NORMAL_WORLDSPACE");
}
if (this._normalsAreUnsigned) {
defines.push("#define ENCODE_NORMAL");
}
// Bones
if (mesh.useBones && mesh.computeBonesUsingShaders && mesh.skeleton) {
attribs.push(VertexBuffer.MatricesIndicesKind);
attribs.push(VertexBuffer.MatricesWeightsKind);
if (mesh.numBoneInfluencers > 4) {
attribs.push(VertexBuffer.MatricesIndicesExtraKind);
attribs.push(VertexBuffer.MatricesWeightsExtraKind);
}
defines.push("#define NUM_BONE_INFLUENCERS " + mesh.numBoneInfluencers);
defines.push("#define BONETEXTURE " + mesh.skeleton.isUsingTextureForMatrices);
defines.push("#define BonesPerMesh " + (mesh.skeleton.bones.length + 1));
}
else {
defines.push("#define NUM_BONE_INFLUENCERS 0");
defines.push("#define BONETEXTURE false");
defines.push("#define BonesPerMesh 0");
}
// Morph targets
const numMorphInfluencers = mesh.morphTargetManager
? PrepareDefinesAndAttributesForMorphTargets(mesh.morphTargetManager, defines, attribs, mesh, true, // usePositionMorph
true, // useNormalMorph
false, // useTangentMorph
uv1, // useUVMorph
uv2, // useUV2Morph
color // useColorMorph
)
: 0;
// Instances
if (useInstances) {
defines.push("#define INSTANCES");
PushAttributesForInstances(attribs, this._enableVelocity || this._enableVelocityLinear);
if (subMesh.getRenderingMesh().hasThinInstances) {
defines.push("#define THIN_INSTANCES");
}
}
// Setup textures count
if (this._linkedWithPrePass) {
defines.push("#define SCENE_MRT_COUNT " + this._attachmentsFromPrePass.length);
}
else {
defines.push("#define SCENE_MRT_COUNT " + this._multiRenderTarget.textures.length);
}
PrepareStringDefinesForClipPlanes(material, this._scene, defines);
// Get correct effect
const engine = this._scene.getEngine();
const drawWrapper = subMesh._getDrawWrapper(undefined, true);
const cachedDefines = drawWrapper.defines;
const join = defines.join("\n");
if (cachedDefines !== join) {
drawWrapper.setEffect(engine.createEffect("geometry", {
attributes: attribs,
uniformsNames: Uniforms,
samplers: Samplers,
defines: join,
onCompiled: null,
fallbacks: null,
onError: null,
uniformBuffersNames: ["Scene"],
indexParameters: { buffersCount: this._multiRenderTarget.textures.length - 1, maxSimultaneousMorphTargets: numMorphInfluencers },
shaderLanguage: this.shaderLanguage,
}, engine), join);
}
return drawWrapper.effect.isReady();
}
/**
* Gets the current underlying G Buffer.
* @returns the buffer
*/
getGBuffer() {
return this._multiRenderTarget;
}
/**
* Gets the number of samples used to render the buffer (anti aliasing).
*/
get samples() {
return this._multiRenderTarget.samples;
}
/**
* Sets the number of samples used to render the buffer (anti aliasing).
*/
set samples(value) {
this._multiRenderTarget.samples = value;
}
/**
* Disposes the renderer and frees up associated resources.
*/
dispose() {
if (this._resizeObserver) {
const engine = this._scene.getEngine();
engine.onResizeObservable.remove(this._resizeObserver);
this._resizeObserver = null;
}
if (this._multiRenderTarget?.renderTarget && this.scene.getEngine()._currentRenderTarget === this._multiRenderTarget.renderTarget) {
this.scene.getEngine().unBindFramebuffer(this._multiRenderTarget?.renderTarget);
}
this.getGBuffer().dispose();
}
_assignRenderTargetIndices() {
const textureNames = [];
const textureTypesAndFormats = [];
let count = 0;
if (this._enableDepth) {
this._depthIndex = count;
count++;
textureNames.push("gBuffer_Depth");
textureTypesAndFormats.push(this._textureTypesAndFormats[GeometryBufferRenderer.DEPTH_TEXTURE_TYPE]);
}
if (this._enableNormal) {
this._normalIndex = count;
count++;
textureNames.push("gBuffer_Normal");
textureTypesAndFormats.push(this._textureTypesAndFormats[GeometryBufferRenderer.NORMAL_TEXTURE_TYPE]);
}
if (this._enablePosition) {
this._positionIndex = count;
count++;
textureNames.push("gBuffer_Position");
textureTypesAndFormats.push(this._textureTypesAndFormats[GeometryBufferRenderer.POSITION_TEXTURE_TYPE]);
}
if (this._enableVelocity) {
this._velocityIndex = count;
count++;
textureNames.push("gBuffer_Velocity");
textureTypesAndFormats.push(this._textureTypesAndFormats[GeometryBufferRenderer.VELOCITY_TEXTURE_TYPE]);
}
if (this._enableVelocityLinear) {
this._velocityLinearIndex = count;
count++;
textureNames.push("gBuffer_VelocityLinear");
textureTypesAndFormats.push(this._textureTypesAndFormats[GeometryBufferRenderer.VELOCITY_LINEAR_TEXTURE_TYPE]);
}
if (this._enableReflectivity) {
this._reflectivityIndex = count;
count++;
textureNames.push("gBuffer_Reflectivity");
textureTypesAndFormats.push(this._textureTypesAndFormats[GeometryBufferRenderer.REFLECTIVITY_TEXTURE_TYPE]);
}
if (this._enableScreenspaceDepth) {
this._screenspaceDepthIndex = count;
count++;
textureNames.push("gBuffer_ScreenspaceDepth");
textureTypesAndFormats.push(this._textureTypesAndFormats[GeometryBufferRenderer.SCREENSPACE_DEPTH_TEXTURE_TYPE]);
}
if (this._enableIrradiance) {
this._irradianceIndex = count;
count++;
textureNames.push("gBuffer_Irradiance");
textureTypesAndFormats.push(this._textureTypesAndFormats[GeometryBufferRenderer.IRRADIANCE_TEXTURE_TYPE]);
}
return [count, textureNames, textureTypesAndFormats];
}
_createRenderTargets() {
const engine = this._scene.getEngine();
const [count, textureNames, textureTypesAndFormat] = this._assignRenderTargetIndices();
let type = 0;
if (engine._caps.textureFloat && engine._caps.textureFloatLinearFiltering) {
type = 1;
}
else if (engine._caps.textureHalfFloat && engine._caps.textureHalfFloatLinearFiltering) {
type = 2;
}
const dimensions = this._ratioOrDimensions.width !== undefined
? this._ratioOrDimensions
: { width: engine.getRenderWidth() * this._ratioOrDimensions, height: engine.getRenderHeight() * this._ratioOrDimensions };
const textureTypes = [];
const textureFormats = [];
const samplingModes = [];
for (const typeAndFormat of textureTypesAndFormat) {
if (typeAndFormat) {
textureTypes.push(typeAndFormat.textureType);
textureFormats.push(typeAndFormat.textureFormat);
samplingModes.push(typeAndFormat.samplingMode ?? 2);
}
else {
textureTypes.push(type);
textureFormats.push(5);
samplingModes.push(2);
}
}
this._normalsAreUnsigned =
textureTypes[GeometryBufferRenderer.NORMAL_TEXTURE_TYPE] === 11 ||
textureTypes[GeometryBufferRenderer.NORMAL_TEXTURE_TYPE] === 13;
this._multiRenderTarget = new MultiRenderTarget("gBuffer", dimensions, count, this._scene, { generateMipMaps: false, generateDepthTexture: true, types: textureTypes, formats: textureFormats, samplingModes, depthTextureFormat: this._depthFormat }, textureNames.concat("gBuffer_DepthBuffer"));
if (!this.isSupported) {
return;
}
this._multiRenderTarget.wrapU = Texture.CLAMP_ADDRESSMODE;
this._multiRenderTarget.wrapV = Texture.CLAMP_ADDRESSMODE;
this._multiRenderTarget.refreshRate = 1;
this._multiRenderTarget.renderParticles = false;
this._multiRenderTarget.renderList = null;
// Depth is always the first texture in the geometry buffer renderer!
const layoutAttachmentsAll = [true];
const layoutAttachmentsAllButDepth = [false];
const layoutAttachmentsDepthOnly = [true];
for (let i = 1; i < count; ++i) {
layoutAttachmentsAll.push(true);
layoutAttachmentsDepthOnly.push(false);
layoutAttachmentsAllButDepth.push(true);
}
const attachmentsAll = engine.buildTextureLayout(layoutAttachmentsAll);
const attachmentsAllButDepth = engine.buildTextureLayout(layoutAttachmentsAllButDepth);
const attachmentsDepthOnly = engine.buildTextureLayout(layoutAttachmentsDepthOnly);
this._multiRenderTarget.onClearObservable.add((engine) => {
engine.bindAttachments(this.useSpecificClearForDepthTexture ? attachmentsAllButDepth : attachmentsAll);
engine.clear(this._clearColor, true, true, true);
if (this.useSpecificClearForDepthTexture) {
engine.bindAttachments(attachmentsDepthOnly);
engine.clear(this._clearDepthColor, true, true, true);
}
engine.bindAttachments(attachmentsAll);
});
this._resizeObserver = engine.onResizeObservable.add(() => {
if (this._multiRenderTarget) {
const dimensions = this._ratioOrDimensions.width !== undefined
? this._ratioOrDimensions
: { width: engine.getRenderWidth() * this._ratioOrDimensions, height: engine.getRenderHeight() * this._ratioOrDimensions };
this._multiRenderTarget.resize(dimensions);
}
});
// Custom render function
const renderSubMesh = (subMesh) => {
const renderingMesh = subMesh.getRenderingMesh();
const effectiveMesh = subMesh.getEffectiveMesh();
const scene = this._scene;
const engine = scene.getEngine();
const material = subMesh.getMaterial();
if (!material) {
return;
}
effectiveMesh._internalAbstractMeshDataInfo._isActiveIntermediate = false;
// Velocity
if ((this._enableVelocity || this._enableVelocityLinear) && !this._previousTransformationMatrices[effectiveMesh.uniqueId]) {
this._previousTransformationMatrices[effectiveMesh.uniqueId] = {
world: Matrix.Identity(),
viewProjection: scene.getTransformMatrix(),
};
if (renderingMesh.skeleton) {
const bonesTransformations = renderingMesh.skeleton.getTransformMatrices(renderingMesh);
this._previousBonesTransformationMatrices[renderingMesh.uniqueId] = this._copyBonesTransformationMatrices(bonesTransformations, new Float32Array(bonesTransformations.length));
}
}
// Managing instances
const batch = renderingMesh._getInstancesRenderList(subMesh._id, !!subMesh.getReplacementMesh());
if (batch.mustReturn) {
return;
}
const hardwareInstancedRendering = engine.getCaps().instancedArrays && (batch.visibleInstances[subMesh._id] !== null || renderingMesh.hasThinInstances);
const world = effectiveMesh.getWorldMatrix();
if (this.isReady(subMesh, hardwareInstancedRendering)) {
const drawWrapper = subMesh._getDrawWrapper();
if (!drawWrapper) {
return;
}
const effect = drawWrapper.effect;
engine.enableEffect(drawWrapper);
if (!hardwareInstancedRendering) {
renderingMesh._bind(subMesh, effect, material.fillMode);
}
if (!this._useUbo) {
effect.setMatrix("viewProjection", scene.getTransformMatrix());
effect.setMatrix("view", scene.getViewMatrix());
this._scene.bindEyePosition(effect, "vEyePosition");
}
else {
BindSceneUniformBuffer(effect, this._scene.getSceneUniformBuffer());
this._scene.finalizeSceneUbo();
}
let sideOrientation;
const instanceDataStorage = renderingMesh._instanceDataStorage;
if (!instanceDataStorage.isFrozen && (material.backFaceCulling || material.sideOrientation !== null)) {
const mainDeterminant = effectiveMesh._getWorldMatrixDeterminant();
sideOrientation = material._getEffectiveOrientation(renderingMesh);
if (mainDeterminant < 0) {
sideOrientation = sideOrientation === Material.ClockWiseSideOrientation ? Material.CounterClockWiseSideOrientation : Material.ClockWiseSideOrientation;
}
}
else {
sideOrientation = renderingMesh._effectiveSideOrientation;
}
material._preBind(drawWrapper, sideOrientation);
// Alpha test
if (material.needAlphaTestingForMesh(effectiveMesh)) {
const alphaTexture = material.getAlphaTestTexture();
if (alphaTexture) {
effect.setTexture("diffuseSampler", alphaTexture);
effect.setMatrix("diffuseMatrix", alphaTexture.getTextureMatrix());
}
}
// Bump
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);
effect.setFloat2("vTangentSpaceParams", material.invertNormalMapX ? -1.0 : 1.0, material.invertNormalMapY ? -1.0 : 1.0);
}
// Reflectivity
if (this._enableReflectivity) {
// for PBR materials: cf. https://doc.babylonjs.com/features/featuresDeepDive/materials/using/masterPBR
if (material.getClassName() === "PBRMetallicRoughnessMaterial") {
// if it is a PBR material in MetallicRoughness Mode:
if (material.metallicRoughnessTexture !== null) {
effect.setTexture("reflectivitySampler", material.metallicRoughnessTexture);
effect.setMatrix("reflectivityMatrix", material.metallicRoughnessTexture.getTextureMatrix());
}
if (material.metallic !== null) {
effect.setFloat("metallic", material.metallic);
}
if (material.roughness !== null) {
effect.setFloat("glossiness", 1.0 - material.roughness);
}
if (material.baseTexture !== null) {
effect.setTexture("albedoSampler", material.baseTexture);
effect.setMatrix("albedoMatrix", material.baseTexture.getTextureMatrix());
}
if (material.baseColor !== null) {
effect.setColor3("albedoColor", material.baseColor);
}
}
else if (material.getClassName() === "PBRSpecula