@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.
200 lines • 6.56 kB
JavaScript
import { Camera } from "../Cameras/camera.js";
import { Halton2DSequence } from "../Maths/halton2DSequence.js";
import { Vector2 } from "../Maths/math.vector.js";
import { Engine } from "../Engines/engine.js";
import { EffectWrapper } from "../Materials/effectRenderer.js";
/**
* Simple implementation of Temporal Anti-Aliasing (TAA).
* This can be used to improve image quality for still pictures (screenshots for e.g.).
*/
export class ThinTAAPostProcess extends EffectWrapper {
_gatherImports(useWebGPU, list) {
if (useWebGPU) {
this._webGPUReady = true;
list.push(import("../ShadersWGSL/taa.fragment.js"));
}
else {
list.push(import("../Shaders/taa.fragment.js"));
}
}
/**
* Number of accumulated samples (default: 8)
*/
set samples(samples) {
if (this._samples === samples) {
return;
}
this._samples = samples;
this._hs.regenerate(samples);
}
get samples() {
return this._samples;
}
/**
* Whether the TAA is disabled
*/
get disabled() {
return this._disabled;
}
set disabled(value) {
if (this._disabled === value) {
return;
}
this._disabled = value;
this._reset();
}
/**
* The width of the texture in which to render
*/
get textureWidth() {
return this._textureWidth;
}
set textureWidth(width) {
if (this._textureWidth === width) {
return;
}
this._textureWidth = width;
this._reset();
}
/**
* The height of the texture in which to render
*/
get textureHeight() {
return this._textureHeight;
}
set textureHeight(height) {
if (this._textureHeight === height) {
return;
}
this._textureHeight = height;
this._reset();
}
/**
* Enables reprojecting the history texture with a per-pixel velocity.
* If set the "velocitySampler" has to be provided.
*/
get reprojectHistory() {
return this._reprojectHistory;
}
set reprojectHistory(reproject) {
if (this._reprojectHistory === reproject) {
return;
}
this._reprojectHistory = reproject;
this._updateEffect();
}
/**
* Clamps the history pixel to the min and max of the 3x3 pixels surrounding the target pixel.
* This can help further reduce ghosting and artifacts.
*/
get clampHistory() {
return this._clampHistory;
}
set clampHistory(clamp) {
if (this._clampHistory === clamp) {
return;
}
this._clampHistory = clamp;
this._updateEffect();
}
/**
* Constructs a new TAA post process
* @param name Name of the effect
* @param engine Engine to use to render the effect. If not provided, the last created engine will be used
* @param options Options to configure the effect
*/
constructor(name, engine = null, options) {
super({
...options,
name,
engine: engine || Engine.LastCreatedEngine,
useShaderStore: true,
useAsPostProcess: true,
fragmentShader: ThinTAAPostProcess.FragmentUrl,
uniforms: ThinTAAPostProcess.Uniforms,
samplers: ThinTAAPostProcess.Samplers,
});
this._samples = 8;
/**
* The factor used to blend the history frame with current frame (default: 0.05)
*/
this.factor = 0.05;
this._disabled = false;
this._textureWidth = 0;
this._textureHeight = 0;
/**
* Disable TAA on camera move (default: true).
* You generally want to keep this enabled, otherwise you will get a ghost effect when the camera moves (but if it's what you want, go for it!)
*/
this.disableOnCameraMove = true;
this._reprojectHistory = false;
this._clampHistory = false;
this._firstUpdate = true;
this._hs = new Halton2DSequence(this.samples);
}
/** @internal */
_reset() {
this._hs.setDimensions(this._textureWidth / 2, this._textureHeight / 2);
this._hs.next();
this._firstUpdate = true;
}
nextJitterOffset(output = new Vector2()) {
if (!this.camera || !this.camera.hasMoved) {
this._hs.next();
}
output.set(this._hs.x, this._hs.y);
return output;
}
updateProjectionMatrix() {
if (this.disabled) {
return;
}
if (this.camera && !this.camera.hasMoved) {
if (this.camera.mode === Camera.PERSPECTIVE_CAMERA) {
const projMat = this.camera.getProjectionMatrix();
projMat.setRowFromFloats(2, this._hs.x, this._hs.y, projMat.m[10], projMat.m[11]);
}
else {
// We must force the update of the projection matrix so that m[12] and m[13] are recomputed, as we modified them the previous frame
const projMat = this.camera.getProjectionMatrix(true);
projMat.setRowFromFloats(3, this._hs.x + projMat.m[12], this._hs.y + projMat.m[13], projMat.m[14], projMat.m[15]);
}
}
this._hs.next();
}
bind(noDefaultBindings = false) {
super.bind(noDefaultBindings);
if (this.disabled) {
return;
}
const effect = this._drawWrapper.effect;
effect.setFloat("factor", (this.camera?.hasMoved && this.disableOnCameraMove) || this._firstUpdate ? 1 : this.factor);
this._firstUpdate = false;
}
_updateEffect() {
const defines = [];
// There seems to be an issue where `updateEffect` sometimes doesn't include the initial samplers
const samplers = ["textureSampler", "historySampler"];
if (this._reprojectHistory) {
defines.push("#define TAA_REPROJECT_HISTORY");
samplers.push("velocitySampler");
}
if (this._clampHistory) {
defines.push("#define TAA_CLAMP_HISTORY");
}
this.updateEffect(defines.join("\n"), null, samplers);
}
}
/**
* The fragment shader url
*/
ThinTAAPostProcess.FragmentUrl = "taa";
/**
* The list of uniforms used by the effect
*/
ThinTAAPostProcess.Uniforms = ["factor"];
/**
* The list of samplers used by the effect
*/
ThinTAAPostProcess.Samplers = ["historySampler"];
//# sourceMappingURL=thinTAAPostProcess.js.map