@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.
426 lines (425 loc) • 18.2 kB
JavaScript
import { Texture } from "../../Materials/Textures/texture.js";
import { RenderTargetTexture } from "../../Materials/Textures/renderTargetTexture.js";
import "../../Engines/Extensions/engine.multiRender.js";
/**
* A multi render target, like a render target provides the ability to render to a texture.
* Unlike the render target, it can render to several draw buffers (render textures) in one draw.
* This is specially interesting in deferred rendering or for any effects requiring more than
* just one color from a single pass.
*/
export class MultiRenderTarget extends RenderTargetTexture {
/**
* Get if draw buffers (render textures) are currently supported by the used hardware and browser.
*/
get isSupported() {
return this._engine?.getCaps().drawBuffersExtension ?? false;
}
/**
* Get the list of textures generated by the multi render target.
*/
get textures() {
return this._textures;
}
/**
* Gets the number of textures in this MRT. This number can be different from `_textures.length` in case a depth texture is generated.
*/
get count() {
return this._count;
}
/**
* Get the depth texture generated by the multi render target if options.generateDepthTexture has been set
*/
get depthTexture() {
return this._textures[this._textures.length - 1];
}
/**
* Set the wrapping mode on U of all the textures we are rendering to.
* Can be any of the Texture. (CLAMP_ADDRESSMODE, MIRROR_ADDRESSMODE or WRAP_ADDRESSMODE)
*/
set wrapU(wrap) {
if (this._textures) {
for (let i = 0; i < this._textures.length; i++) {
this._textures[i].wrapU = wrap;
}
}
}
/**
* Set the wrapping mode on V of all the textures we are rendering to.
* Can be any of the Texture. (CLAMP_ADDRESSMODE, MIRROR_ADDRESSMODE or WRAP_ADDRESSMODE)
*/
set wrapV(wrap) {
if (this._textures) {
for (let i = 0; i < this._textures.length; i++) {
this._textures[i].wrapV = wrap;
}
}
}
/**
* Instantiate a new multi render target texture.
* A multi render target, like a render target provides the ability to render to a texture.
* Unlike the render target, it can render to several draw buffers (render textures) in one draw.
* This is specially interesting in deferred rendering or for any effects requiring more than
* just one color from a single pass.
* @param name Define the name of the texture
* @param size Define the size of the buffers to render to
* @param count Define the number of target we are rendering into
* @param scene Define the scene the texture belongs to
* @param options Define the options used to create the multi render target
* @param textureNames Define the names to set to the textures (if count \> 0 - optional)
*/
constructor(name, size, count, scene, options, textureNames) {
const generateMipMaps = options && options.generateMipMaps ? options.generateMipMaps : false;
const generateDepthTexture = options && options.generateDepthTexture ? options.generateDepthTexture : false;
const depthTextureFormat = options && options.depthTextureFormat ? options.depthTextureFormat : 15;
const doNotChangeAspectRatio = !options || options.doNotChangeAspectRatio === undefined ? true : options.doNotChangeAspectRatio;
const drawOnlyOnFirstAttachmentByDefault = options && options.drawOnlyOnFirstAttachmentByDefault ? options.drawOnlyOnFirstAttachmentByDefault : false;
super(name, size, scene, generateMipMaps, doNotChangeAspectRatio, undefined, undefined, undefined, undefined, undefined, undefined, undefined, true);
if (!this.isSupported) {
this.dispose();
return;
}
this._textureNames = textureNames;
const types = [];
const samplingModes = [];
const useSRGBBuffers = [];
const formats = [];
const targetTypes = [];
const faceIndex = [];
const layerIndex = [];
const layerCounts = [];
this._initTypes(count, types, samplingModes, useSRGBBuffers, formats, targetTypes, faceIndex, layerIndex, layerCounts, options);
const generateDepthBuffer = !options || options.generateDepthBuffer === undefined ? true : options.generateDepthBuffer;
const generateStencilBuffer = !options || options.generateStencilBuffer === undefined ? false : options.generateStencilBuffer;
const samples = options && options.samples ? options.samples : 1;
this._multiRenderTargetOptions = {
samplingModes: samplingModes,
generateMipMaps: generateMipMaps,
generateDepthBuffer: generateDepthBuffer,
generateStencilBuffer: generateStencilBuffer,
generateDepthTexture: generateDepthTexture,
depthTextureFormat: depthTextureFormat,
types: types,
textureCount: count,
useSRGBBuffers: useSRGBBuffers,
samples,
formats: formats,
targetTypes: targetTypes,
faceIndex: faceIndex,
layerIndex: layerIndex,
layerCounts: layerCounts,
labels: textureNames,
label: name,
};
this._count = count;
this._drawOnlyOnFirstAttachmentByDefault = drawOnlyOnFirstAttachmentByDefault;
if (count > 0) {
this._createInternalTextures();
this._createTextures(textureNames);
}
}
_initTypes(count, types, samplingModes, useSRGBBuffers, formats, targets, faceIndex, layerIndex, layerCounts, options) {
for (let i = 0; i < count; i++) {
if (options && options.types && options.types[i] !== undefined) {
types.push(options.types[i]);
}
else {
types.push(options && options.defaultType ? options.defaultType : 0);
}
if (options && options.samplingModes && options.samplingModes[i] !== undefined) {
samplingModes.push(options.samplingModes[i]);
}
else {
samplingModes.push(Texture.BILINEAR_SAMPLINGMODE);
}
if (options && options.useSRGBBuffers && options.useSRGBBuffers[i] !== undefined) {
useSRGBBuffers.push(options.useSRGBBuffers[i]);
}
else {
useSRGBBuffers.push(false);
}
if (options && options.formats && options.formats[i] !== undefined) {
formats.push(options.formats[i]);
}
else {
formats.push(5);
}
if (options && options.targetTypes && options.targetTypes[i] !== undefined) {
targets.push(options.targetTypes[i]);
}
else {
targets.push(3553);
}
if (options && options.faceIndex && options.faceIndex[i] !== undefined) {
faceIndex.push(options.faceIndex[i]);
}
else {
faceIndex.push(0);
}
if (options && options.layerIndex && options.layerIndex[i] !== undefined) {
layerIndex.push(options.layerIndex[i]);
}
else {
layerIndex.push(0);
}
if (options && options.layerCounts && options.layerCounts[i] !== undefined) {
layerCounts.push(options.layerCounts[i]);
}
else {
layerCounts.push(1);
}
}
}
_createInternaTextureIndexMapping() {
const mapMainInternalTexture2Index = {};
const mapInternalTexture2MainIndex = [];
if (!this._renderTarget) {
return mapInternalTexture2MainIndex;
}
const internalTextures = this._renderTarget.textures;
for (let i = 0; i < internalTextures.length; i++) {
const texture = internalTextures[i];
if (!texture) {
continue;
}
const mainIndex = mapMainInternalTexture2Index[texture.uniqueId];
if (mainIndex !== undefined) {
mapInternalTexture2MainIndex[i] = mainIndex;
}
else {
mapMainInternalTexture2Index[texture.uniqueId] = i;
}
}
return mapInternalTexture2MainIndex;
}
/**
* @internal
*/
_rebuild(fromContextLost = false, forceFullRebuild = false, textureNames) {
if (this._count < 1 || fromContextLost) {
return;
}
const mapInternalTexture2MainIndex = this._createInternaTextureIndexMapping();
this.releaseInternalTextures();
this._createInternalTextures();
if (forceFullRebuild) {
this._releaseTextures();
this._createTextures(textureNames);
}
const internalTextures = this._renderTarget.textures;
for (let i = 0; i < internalTextures.length; i++) {
const texture = this._textures[i];
if (mapInternalTexture2MainIndex[i] !== undefined) {
this._renderTarget.setTexture(internalTextures[mapInternalTexture2MainIndex[i]], i);
}
texture._texture = internalTextures[i];
if (texture._texture) {
texture._noMipmap = !texture._texture.useMipMaps;
texture._useSRGBBuffer = texture._texture._useSRGBBuffer;
}
}
if (this.samples !== 1) {
this._renderTarget.setSamples(this.samples, !this._drawOnlyOnFirstAttachmentByDefault, true);
}
}
_createInternalTextures() {
this._renderTarget = this._getEngine().createMultipleRenderTarget(this._size, this._multiRenderTargetOptions, !this._drawOnlyOnFirstAttachmentByDefault);
this._texture = this._renderTarget.texture;
}
_releaseTextures() {
if (this._textures) {
for (let i = 0; i < this._textures.length; i++) {
this._textures[i]._texture = null; // internal textures are released by a call to releaseInternalTextures()
this._textures[i].dispose();
}
}
}
_createTextures(textureNames) {
const internalTextures = this._renderTarget.textures;
this._textures = [];
for (let i = 0; i < internalTextures.length; i++) {
const texture = new Texture(null, this.getScene());
if (textureNames?.[i]) {
texture.name = textureNames[i];
}
texture._texture = internalTextures[i];
if (texture._texture) {
texture._noMipmap = !texture._texture.useMipMaps;
texture._useSRGBBuffer = texture._texture._useSRGBBuffer;
}
this._textures.push(texture);
}
}
/**
* Replaces an internal texture within the MRT. Useful to share textures between MultiRenderTarget.
* @param texture The new texture to set in the MRT
* @param index The index of the texture to replace
* @param disposePrevious Set to true if the previous internal texture should be disposed
*/
setInternalTexture(texture, index, disposePrevious = true) {
if (!this.renderTarget) {
return;
}
if (index === 0) {
this._texture = texture;
}
this.renderTarget.setTexture(texture, index, disposePrevious);
if (!this.textures[index]) {
this.textures[index] = new Texture(null, this.getScene());
this.textures[index].name = this._textureNames?.[index] ?? this.textures[index].name;
}
this.textures[index]._texture = texture;
this.textures[index]._noMipmap = !texture.useMipMaps;
this.textures[index]._useSRGBBuffer = texture._useSRGBBuffer;
this._count = this.renderTarget.textures ? this.renderTarget.textures.length : 0;
if (this._multiRenderTargetOptions.types) {
this._multiRenderTargetOptions.types[index] = texture.type;
}
if (this._multiRenderTargetOptions.samplingModes) {
this._multiRenderTargetOptions.samplingModes[index] = texture.samplingMode;
}
if (this._multiRenderTargetOptions.useSRGBBuffers) {
this._multiRenderTargetOptions.useSRGBBuffers[index] = texture._useSRGBBuffer;
}
if (this._multiRenderTargetOptions.targetTypes && this._multiRenderTargetOptions.targetTypes[index] !== -1) {
let target = 0;
if (texture.is2DArray) {
target = 35866;
}
else if (texture.isCube) {
target = 34067;
} /*else if (texture.isCubeArray) {
target = 3735928559;
}*/
else if (texture.is3D) {
target = 32879;
}
else {
target = 3553;
}
this._multiRenderTargetOptions.targetTypes[index] = target;
}
}
/**
* Changes an attached texture's face index or layer.
* @param index The index of the texture to modify the attachment of
* @param layerIndex The layer index of the texture to be attached to the framebuffer
* @param faceIndex The face index of the texture to be attached to the framebuffer
*/
setLayerAndFaceIndex(index, layerIndex = -1, faceIndex = -1) {
if (!this.textures[index] || !this.renderTarget) {
return;
}
if (this._multiRenderTargetOptions.layerIndex) {
this._multiRenderTargetOptions.layerIndex[index] = layerIndex;
}
if (this._multiRenderTargetOptions.faceIndex) {
this._multiRenderTargetOptions.faceIndex[index] = faceIndex;
}
this.renderTarget.setLayerAndFaceIndex(index, layerIndex, faceIndex);
}
/**
* Changes every attached texture's face index or layer.
* @param layerIndices The layer indices of the texture to be attached to the framebuffer
* @param faceIndices The face indices of the texture to be attached to the framebuffer
*/
setLayerAndFaceIndices(layerIndices, faceIndices) {
if (!this.renderTarget) {
return;
}
this._multiRenderTargetOptions.layerIndex = layerIndices;
this._multiRenderTargetOptions.faceIndex = faceIndices;
this.renderTarget.setLayerAndFaceIndices(layerIndices, faceIndices);
}
/**
* Define the number of samples used if MSAA is enabled.
*/
get samples() {
return this._samples;
}
set samples(value) {
if (this._renderTarget) {
this._samples = this._renderTarget.setSamples(value);
}
else {
// In case samples are set with 0 textures created, we must save the desired samples value
this._samples = value;
}
}
/**
* Resize all the textures in the multi render target.
* Be careful as it will recreate all the data in the new texture.
* @param size Define the new size
*/
resize(size) {
this._processSizeParameter(size);
this._rebuild(false, undefined, this._textureNames);
}
/**
* Changes the number of render targets in this MRT
* Be careful as it will recreate all the data in the new texture.
* @param count new texture count
* @param options Specifies texture types and sampling modes for new textures
* @param textureNames Specifies the names of the textures (optional)
*/
updateCount(count, options, textureNames) {
this._multiRenderTargetOptions.textureCount = count;
this._count = count;
const types = [];
const samplingModes = [];
const useSRGBBuffers = [];
const formats = [];
const targetTypes = [];
const faceIndex = [];
const layerIndex = [];
const layerCounts = [];
this._textureNames = textureNames;
this._initTypes(count, types, samplingModes, useSRGBBuffers, formats, targetTypes, faceIndex, layerIndex, layerCounts, options);
this._multiRenderTargetOptions.types = types;
this._multiRenderTargetOptions.samplingModes = samplingModes;
this._multiRenderTargetOptions.useSRGBBuffers = useSRGBBuffers;
this._multiRenderTargetOptions.formats = formats;
this._multiRenderTargetOptions.targetTypes = targetTypes;
this._multiRenderTargetOptions.faceIndex = faceIndex;
this._multiRenderTargetOptions.layerIndex = layerIndex;
this._multiRenderTargetOptions.layerCounts = layerCounts;
this._multiRenderTargetOptions.labels = textureNames;
this._rebuild(false, true, textureNames);
}
_unbindFrameBuffer(engine, faceIndex) {
if (this._renderTarget) {
engine.unBindMultiColorAttachmentFramebuffer(this._renderTarget, this.isCube, () => {
this.onAfterRenderObservable.notifyObservers(faceIndex);
});
}
}
/**
* Dispose the render targets and their associated resources
* @param doNotDisposeInternalTextures if set to true, internal textures won't be disposed (default: false).
*/
dispose(doNotDisposeInternalTextures = false) {
this._releaseTextures();
if (!doNotDisposeInternalTextures) {
this.releaseInternalTextures();
}
else {
// Prevent internal texture dispose in super.dispose
this._texture = null;
}
super.dispose();
}
/**
* Release all the underlying texture used as draw buffers (render textures).
*/
releaseInternalTextures() {
const internalTextures = this._renderTarget?.textures;
if (!internalTextures) {
return;
}
for (let i = internalTextures.length - 1; i >= 0; i--) {
this._textures[i]._texture = null;
}
this._renderTarget?.dispose();
this._renderTarget = null;
}
}
//# sourceMappingURL=multiRenderTarget.js.map