Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TAA reprojection #6048

Merged
merged 16 commits into from
Mar 7, 2024
Binary file added examples/assets/models/pbr-house.glb
Binary file not shown.
5 changes: 5 additions & 0 deletions examples/assets/models/pbr-house.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
The house model has been obtained from this address:
https://sketchfab.com/3d-models/house-03-pbr-c56521b89188460a99235dec8bcd0ed3

It's distributed under CC license:
https://creativecommons.org/licenses/by/4.0/
11 changes: 7 additions & 4 deletions examples/src/examples/graphics/post-processing/example.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const createOptions = new pc.AppOptions();
createOptions.graphicsDevice = device;
createOptions.mouse = new pc.Mouse(document.body);
createOptions.touch = new pc.TouchDevice(document.body);
createOptions.keyboard = new pc.Keyboard(window);

createOptions.componentSystems = [
pc.RenderComponentSystem,
Expand Down Expand Up @@ -143,7 +144,9 @@ assetListLoader.load(() => {

// add orbit camera script with a mouse and a touch support
cameraEntity.addComponent('script');
cameraEntity.script.create('orbitCamera', {

// add orbit camera script with a mouse and a touch support
cameraEntity.script.create("orbitCamera", {
attributes: {
inertiaFactor: 0.2,
focusEntity: mosquitoEntity,
Expand All @@ -154,7 +157,6 @@ assetListLoader.load(() => {
cameraEntity.script.create('orbitCameraInputMouse');
cameraEntity.script.create('orbitCameraInputTouch');

// position the camera in the world
cameraEntity.setLocalPosition(0, 40, -220);
cameraEntity.lookAt(0, 0, 100);
app.root.addChild(cameraEntity);
Expand Down Expand Up @@ -218,9 +220,9 @@ assetListLoader.load(() => {
samples: 0, // number of samples for multi-sampling
sceneColorMap: true, // true if the scene color should be captured

// disabled by default as this is WIP
// disabled TAA as it currently does not handle dynamic objects
prepassEnabled: false,
taaEnabled: false // true if temporal anti-aliasing should be used
taaEnabled: false
};

const setupRenderPass = () => {
Expand All @@ -245,6 +247,7 @@ assetListLoader.load(() => {
const taaEnabled = data.get('data.taa.enabled');
if (noPasses || taaEnabled !== currentOptions.taaEnabled) {
currentOptions.taaEnabled = taaEnabled;
currentOptions.prepassEnabled = taaEnabled;

// create new pass
setupRenderPass();
Expand Down
6 changes: 6 additions & 0 deletions examples/src/examples/graphics/taa/config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* @type {import('../../../../types.mjs').ExampleConfig}
*/
export default {
WEBGPU_ENABLED: true
};
59 changes: 59 additions & 0 deletions examples/src/examples/graphics/taa/controls.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import * as pc from 'playcanvas';

/**
* @param {import('../../../app/components/Example.mjs').ControlOptions} options - The options.
* @returns {JSX.Element} The returned JSX Element.
*/
export function controls({ observer, ReactPCUI, React, jsx, fragment }) {
const { BindingTwoWay, BooleanInput, LabelGroup, Panel, SliderInput } = ReactPCUI;
return fragment(
jsx(
Panel,
{ headerText: 'Scene Rendering' },
jsx(
LabelGroup,
{ text: 'resolution' },
jsx(SliderInput, {
binding: new BindingTwoWay(),
link: { observer, path: 'data.scene.scale' },
min: 0.5,
max: 1,
precision: 1
})
),
jsx(
LabelGroup,
{ text: 'Bloom' },
jsx(BooleanInput, {
type: 'toggle',
binding: new BindingTwoWay(),
link: { observer, path: 'data.scene.bloom' }
})
)
),
jsx(
Panel,
{ headerText: 'TAA' },
jsx(
LabelGroup,
{ text: 'enabled' },
jsx(BooleanInput, {
type: 'toggle',
binding: new BindingTwoWay(),
link: { observer, path: 'data.taa.enabled' }
})
),
jsx(
LabelGroup,
{ text: 'jitter' },
jsx(SliderInput, {
binding: new BindingTwoWay(),
link: { observer, path: 'data.taa.jitter' },
min: 0,
max: 1,
precision: 2
})
)
)
);
}
203 changes: 203 additions & 0 deletions examples/src/examples/graphics/taa/example.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
import * as pc from 'playcanvas';
import * as pcx from 'playcanvas-extras';
import { data } from '@examples/observer';
import { deviceType, rootPath } from '@examples/utils';

const canvas = document.getElementById('application-canvas');
if (!(canvas instanceof HTMLCanvasElement)) {
throw new Error('No canvas found');
}

const assets = {
orbit: new pc.Asset('script', 'script', { url: rootPath + '/static/scripts/camera/orbit-camera.js' }),
house: new pc.Asset('house', 'container', { url: rootPath + '/static/assets/models/pbr-house.glb' }),
envatlas: new pc.Asset(
'env-atlas',
'texture',
{ url: rootPath + '/static/assets/cubemaps/table-mountain-env-atlas.png' },
{ type: pc.TEXTURETYPE_RGBP, mipmaps: false }
)
};

const gfxOptions = {
deviceTypes: [deviceType],
glslangUrl: rootPath + '/static/lib/glslang/glslang.js',
twgslUrl: rootPath + '/static/lib/twgsl/twgsl.js',

// disable anti-aliasing as TAA is used to smooth edges
antialias: false
};

const device = await pc.createGraphicsDevice(canvas, gfxOptions);
const createOptions = new pc.AppOptions();
createOptions.graphicsDevice = device;
createOptions.mouse = new pc.Mouse(document.body);
createOptions.touch = new pc.TouchDevice(document.body);

createOptions.componentSystems = [
pc.RenderComponentSystem,
pc.CameraComponentSystem,
pc.LightComponentSystem,
pc.ScriptComponentSystem
];
createOptions.resourceHandlers = [
pc.TextureHandler,
pc.ContainerHandler,
pc.ScriptHandler
];

const app = new pc.AppBase(canvas);
app.init(createOptions);

// Set the canvas to fill the window and automatically change resolution to be the same as the canvas size
app.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW);
app.setCanvasResolution(pc.RESOLUTION_AUTO);

// Ensure canvas is resized when window changes size
const resize = () => app.resizeCanvas();
window.addEventListener('resize', resize);
app.on('destroy', () => {
window.removeEventListener('resize', resize);
});

const assetListLoader = new pc.AssetListLoader(Object.values(assets), app.assets);
assetListLoader.load(() => {
app.start();

// setup skydome with low intensity
app.scene.envAtlas = assets.envatlas.resource;
app.scene.skyboxMip = 0;
app.scene.exposure = 1.0;

// render in HDR mode
app.scene.toneMapping = pc.TONEMAP_LINEAR;
app.scene.gammaCorrection = pc.GAMMA_NONE;
mvaligursky marked this conversation as resolved.
Show resolved Hide resolved

// create an instance of the house and add it to the scene
const houseEntity = assets.house.resource.instantiateRenderEntity();
houseEntity.setLocalScale(100, 100, 100);
app.root.addChild(houseEntity);

// Create an Entity with a camera component
const cameraEntity = new pc.Entity();
cameraEntity.addComponent('camera', {
nearClip: 10,
farClip: 600,
fov: 80
});

// add orbit camera script with a mouse and a touch support
cameraEntity.addComponent('script');
cameraEntity.script.create("orbitCamera", {
attributes: {
inertiaFactor: 0.2,
focusEntity: houseEntity,
distanceMax: 400,
frameOnStart: true
}
});
cameraEntity.script.create("orbitCameraInputMouse");
cameraEntity.script.create("orbitCameraInputTouch");
cameraEntity.setLocalPosition(0, 40, -220);
cameraEntity.lookAt(0, 0, 100);
app.root.addChild(cameraEntity);

// add a shadow casting directional light
const lightColor = new pc.Color(1, 1, 1);
const light = new pc.Entity();
light.addComponent('light', {
type: 'directional',
color: lightColor,
intensity: 0.2,
range: 700,
shadowResolution: 4096,
shadowDistance: 600,
castShadows: true,
shadowBias: 0.2,
normalOffsetBias: 0.05
});
app.root.addChild(light);
light.setLocalEulerAngles(40, 10, 0);

// ------ Custom render passes set up ------

const currentOptions = {
camera: cameraEntity.camera, // camera used to render those passes
samples: 0, // number of samples for multi-sampling
// sceneColorMap: true, // true if the scene color should be captured
sceneColorMap: false,

// enable the pre-pass to generate the depth buffer, which is needed by the TAA
prepassEnabled: true,

// enable temporal anti-aliasing
taaEnabled: true
};

const setupRenderPass = () => {
// destroy existing pass if any
if (cameraEntity.camera.renderPasses.length > 0) {
cameraEntity.camera.renderPasses[0].destroy();
}

// Use a render pass camera frame, which is a render pass that implements typical rendering of a camera.
// Internally this sets up additional passes it needs, based on the options passed to it.
const renderPassCamera = new pcx.RenderPassCameraFrame(app, currentOptions);

const composePass = renderPassCamera.composePass;
composePass.toneMapping = data.get('data.scene.tonemapping');
composePass.bloomIntensity = 0.02;

// and set up these rendering passes to be used by the camera, instead of its default rendering
cameraEntity.camera.renderPasses = [renderPassCamera];
};

// ------

const applySettings = () => {

// if settings require render passes to be re-created
const noPasses = cameraEntity.camera.renderPasses.length === 0;
const taaEnabled = data.get('data.taa.enabled');
if (noPasses || taaEnabled !== currentOptions.taaEnabled) {
currentOptions.taaEnabled = taaEnabled;

// create new pass
setupRenderPass();
}


// apply all runtime settings
const renderPassCamera = cameraEntity.camera.renderPasses[0];
renderPassCamera.renderTargetScale = data.get('data.scene.scale');
renderPassCamera.bloomEnabled = data.get('data.scene.bloom');

// taa - enable camera jitter if taa is enabled
cameraEntity.camera.jitter = taaEnabled ? data.get('data.taa.jitter') : 0;
};

// apply UI changes
let initialValuesSetup = false;
data.on('*:set', () => {
if (initialValuesSetup) applySettings();
});

// set initial values
data.set('data', {
scene: {
scale: 1,
bloom: true,
tonemapping: pc.TONEMAP_ACES
},
taa: {
enabled: currentOptions.taaEnabled,
jitter: 1
}
});

// apply initial settings after all values are set
initialValuesSetup = true;
applySettings();
});

export { app };
Binary file added examples/thumbnails/graphics_taa_large.webp
Binary file not shown.
Binary file added examples/thumbnails/graphics_taa_small.webp
Binary file not shown.
Loading
Loading