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

Post effects #5742

Merged
merged 15 commits into from
Oct 25, 2022
18 changes: 18 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@
"normalize.css": "^8.0.1",
"pdfjs-dist": "^2.14.305",
"phoenix": "github:gfodor/phoenix-js#master",
"postprocessing": "^6.28.7",
"prop-types": "^15.7.2",
"raven-js": "^3.20.1",
"react": "^16.13.1",
Expand Down
40 changes: 30 additions & 10 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type { AElement, AScene } from "aframe";
import HubChannel from "./utils/hub-channel";
import MediaDevicesManager from "./utils/media-devices-manager";

import { EffectComposer, EffectPass } from "postprocessing";
import {
Audio,
AudioListener,
Expand All @@ -21,6 +22,7 @@ import {
WebGLRenderer
} from "three";
import { AudioSettings, SourceType } from "./components/audio-params";
import { createEffectsComposer } from "./effects";
import { DialogAdapter } from "./naf-dialog-adapter";
import { mainTick } from "./systems/hubs-systems";
import { waitForPreloads } from "./utils/preload";
Expand Down Expand Up @@ -87,6 +89,12 @@ export class App {
CURSOR: 3
};

fx: {
composer?: EffectComposer;
bloomAndTonemapPass?: EffectPass;
tonemapOnlyPass?: EffectPass;
} = {};

constructor() {
// TODO: Create accessor / update methods for these maps / set
this.world.eid2obj = new Map();
Expand Down Expand Up @@ -142,20 +150,20 @@ export class App {
event.preventDefault();
});

const enablePostEffects = this.store.state.preferences.enablePostEffects;

const renderer = new WebGLRenderer({
// TODO we should not be using alpha: false https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/WebGL_best_practices#avoid_alphafalse_which_can_be_expensive
alpha: true,
antialias: true,
depth: true,
stencil: true,
premultipliedAlpha: true,
preserveDrawingBuffer: false,
logarithmicDepthBuffer: false,
Copy link
Contributor

@takahirox takahirox Oct 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to clarify, you have removed premultipliedAlpha, preserveDrawingBuffer, and logarithmicDepthBuffer because they use the default values?

https://threejs.org/docs/#api/en/renderers/WebGLRenderer.preserveDrawingBuffer

And you have toggled stencil to false because we don't use and/or it blocks post processing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, exactly

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hope we can have a separated clean up PR from a logic addition/change PR because review can be easier.

// TODO we probably want high-performance
antialias: !enablePostEffects,
depth: !enablePostEffects,
stencil: false,
powerPreference: "high-performance",
canvas
});

// We manually handle resetting this in mainTick so that stats are correctly reported with post effects enabled
renderer.info.autoReset = false;

renderer.setPixelRatio(window.devicePixelRatio);

renderer.debug.checkShaderErrors = qsTruthy("checkShaderErrors");
Expand All @@ -166,7 +174,7 @@ export class App {

sceneEl.appendChild(renderer.domElement);

const camera = new PerspectiveCamera(80, window.innerWidth / window.innerHeight, 0.05, 10000);
const camera = new PerspectiveCamera(80, canvas.width / canvas.height, 0.05, 10000);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious, what is this change for?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah that may not actually be needed anymore. Early on when working on this I was using a static size canvas to simplify things. This value doesn't particularly matter anyway since it gets overwritten again once useResizeViewport runs, but I will ditch the change.

We end up doing way more resizing of the canvas than I would like but its a bit tricky to untangle right now since our initialization is still driven by aframe, then react, so at this point we don't actually know what the dimensions of the canvas will end up being.


const audioListener = new AudioListener();
this.audioListener = audioListener;
Expand All @@ -179,13 +187,25 @@ export class App {
};

this.world.scene = sceneEl.object3D;
const scene = sceneEl.object3D;

// We manually call scene.updateMatrixWolrd in mainTick
scene.autoUpdate = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this manual updating for post processing? Can auto update break the post processing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Post processing can call render multiple times, so it makes more sense to just manually update ourselves once. This is also something we wanted to do anyway so that we can easily see where in the frame matrix updates are happening and schedule systems before/after that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Post processing can call render multiple times, so it makes more sense to just manually update ourselves once.

OK, makese sense to me. I thought postprocessing lib set scene.autoUpdate false but if it doesn't, setting scene.autoUpdate false and manually calling update matrix world in our code makes sense.


if (enablePostEffects) {
this.fx = createEffectsComposer(canvas, renderer, camera, scene, sceneEl, this.store);
} else {
(sceneEl as any).addEventListener("rendererresize", function ({ detail }: { detail: DOMRectReadOnly }) {
renderer.setSize(detail.width, detail.height, true);
});
}
Copy link
Contributor

@takahirox takahirox Oct 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does here mean renderer resize handling is done in the effect composer if post effects is enabled? (Maybe adding comments is good)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC the resizing is handled by the composer based on the useViewport hook resize listener. We are setting the canvas style size in the hook but here we are forcing a style update that we weren't doing before and I'm not sure why.

Also in effects.ts we are also forcing a style update when updating the renderer size but we are already doing that in the useViewport hook so we are overriding that update.


// This gets called after all system and component init functions
sceneEl.addEventListener("loaded", () => {
waitForPreloads().then(() => {
this.world.time.elapsed = performance.now();
renderer.setAnimationLoop(function (_rafTime, xrFrame) {
mainTick(xrFrame, renderer, sceneEl.object3D, camera);
mainTick(xrFrame, renderer, scene, camera);
});
sceneEl.renderStarted = true;
});
Expand Down
1 change: 1 addition & 0 deletions src/assets/stylesheets/preferences-screen.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ $semi-bold: 600;
display: flex;
flex-flow: column;
align-items: center;
padding-bottom: 16px;

input[type='number'] {
-moz-appearance:textfield;
Expand Down
5 changes: 2 additions & 3 deletions src/bit-systems/camera-tool.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ function updateRenderTarget(world, camera) {

const tmpRenderTarget = renderer.getRenderTarget();
renderer.setRenderTarget(renderTarget);
renderer.clearDepth();
renderer.render(sceneEl.object3D, world.eid2obj.get(CameraTool.cameraRef[camera]));
renderer.setRenderTarget(tmpRenderTarget);

Expand Down Expand Up @@ -290,9 +291,7 @@ export function cameraToolSystem(world) {
format: THREE.RGBAFormat,
minFilter: THREE.LinearFilter,
magFilter: THREE.NearestFilter,
encoding: THREE.sRGBEncoding,
depth: false,
stencil: false
encoding: THREE.sRGBEncoding
});
renderTarget.lastUpdated = 0;
renderTarget.needsUpdate = true;
Expand Down
17 changes: 17 additions & 0 deletions src/camera-layers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// NOTE when changing be sure to check hardcoded mask values in hub.html
export enum Layers {
// These 3 layers are hardcoded in THREE
CAMERA_LAYER_DEFAULT,
CAMERA_LAYER_XR_LEFT_EYE,
CAMERA_LAYER_XR_RIGHT_EYE,

CAMERA_LAYER_REFLECTION,
CAMERA_LAYER_INSPECT,
CAMERA_LAYER_VIDEO_TEXTURE_TARGET,

CAMERA_LAYER_THIRD_PERSON_ONLY,
CAMERA_LAYER_FIRST_PERSON_ONLY,

CAMERA_LAYER_UI,
CAMERA_LAYER_FX_MASK
}
3 changes: 2 additions & 1 deletion src/components/cursor-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
import { paths } from "../systems/userinput/paths";
import { sets } from "../systems/userinput/sets";
import { getLastWorldPosition } from "../utils/three-utils";
import { Layers } from "./layers";
import { Layers } from "../camera-layers";

export function findRemoteHoverTarget(world, object3D) {
if (!object3D) return null;
Expand Down Expand Up @@ -99,6 +99,7 @@ AFRAME.registerComponent("cursor-controller", {
this.cursorVisual.renderOrder = window.APP.RENDER_ORDER.CURSOR;
this.cursorVisual.material.transparent = true;
this.cursorVisual.layers.set(Layers.CAMERA_LAYER_UI);
this.cursorVisual.layers.enable(Layers.CAMERA_LAYER_FX_MASK);
this.data.cursor.object3D.add(this.cursorVisual);

this.intersection = null;
Expand Down
16 changes: 1 addition & 15 deletions src/components/layers.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,4 @@
// NOTE when changing be sure to check hardcoded mask values in hub.html
export const Layers = {
// These 3 layers are hardcoded in THREE
CAMERA_LAYER_DEFAULT: 0,
CAMERA_LAYER_XR_LEFT_EYE: 1,
CAMERA_LAYER_XR_RIGHT_EYE: 2,

CAMERA_LAYER_REFLECTION: 3,
CAMERA_LAYER_INSPECT: 4,
CAMERA_LAYER_VIDEO_TEXTURE_TARGET: 5,

CAMERA_LAYER_THIRD_PERSON_ONLY: 6,
CAMERA_LAYER_FIRST_PERSON_ONLY: 7,
CAMERA_LAYER_UI: 8
};
import { Layers } from "../camera-layers";

/**
* Sets layer flags on the underlying Object3D
Expand Down
2 changes: 2 additions & 0 deletions src/components/media-image.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { errorTexture } from "../utils/error-texture";
import { createPlaneBufferGeometry } from "../utils/three-utils";
import { scaleToAspectRatio } from "../utils/scale-to-aspect-ratio";
import { createGIFTexture } from "../utils/gif-texture";
import { Layers } from "../camera-layers";

const textureCache = new TextureCache();
const inflightTextures = new Map();
Expand Down Expand Up @@ -129,6 +130,7 @@ AFRAME.registerComponent("media-image", {
}

this.mesh = new THREE.Mesh(geometry, material);
this.mesh.layers.set(Layers.CAMERA_LAYER_FX_MASK);
this.el.setObject3D("mesh", this.mesh);
}

Expand Down
4 changes: 2 additions & 2 deletions src/components/media-video.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* global performance THREE AFRAME NAF MediaStream setTimeout */
import configs from "../utils/configs";
import audioIcon from "../assets/images/audio.png";
import { paths } from "../systems/userinput/paths";
Expand All @@ -21,7 +20,7 @@ import { errorTexture } from "../utils/error-texture";
import { scaleToAspectRatio } from "../utils/scale-to-aspect-ratio";
import { isSafari } from "../utils/detect-safari";
import { isIOS as detectIOS } from "../utils/is-mobile";

import { Layers } from "../camera-layers";
import qsTruthy from "../utils/qs_truthy";

const ONCE_TRUE = { once: true };
Expand Down Expand Up @@ -478,6 +477,7 @@ AFRAME.registerComponent("media-video", {
}

this.mesh = new THREE.Mesh(geometry, material);
this.mesh.layers.set(Layers.CAMERA_LAYER_FX_MASK);
this.el.setObject3D("mesh", this.mesh);
}

Expand Down
2 changes: 1 addition & 1 deletion src/components/mirror.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const DEFAULT_MIRROR_GEOMETRY = new THREE.PlaneBufferGeometry();
const DEFAULT_TEXTURE_WIDTH = window.innerWidth * window.devicePixelRatio;
const DEFAULT_TEXTURE_HEIGHT = window.innerHeight * window.devicePixelRatio;

import { Layers } from "./layers";
import { Layers } from "../camera-layers";

/**
* Should need to entity that has geometry primitive
Expand Down
6 changes: 3 additions & 3 deletions src/components/player-info.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { registerComponentInstance, deregisterComponentInstance } from "../utils
import defaultAvatar from "../assets/models/DefaultAvatar.glb";
import { MediaDevicesEvents } from "../utils/media-devices-utils";
import { createHeadlessModelForSkinnedMesh } from "../utils/three-utils";
import { Layers } from "./layers";
import { Layers } from "../camera-layers";

function ensureAvatarNodes(json) {
const { nodes } = json;
Expand Down Expand Up @@ -75,7 +75,7 @@ AFRAME.registerComponent("player-info", {
const modelEl = this.el.querySelector(".model");
if (this.isLocalPlayerInfo && e.target === modelEl) {
let isSkinnedAvatar = false;
modelEl.object3D.traverse(function(o) {
modelEl.object3D.traverse(function (o) {
if (o.isSkinnedMesh) {
const headlessMesh = createHeadlessModelForSkinnedMesh(o);
if (headlessMesh) {
Expand All @@ -87,7 +87,7 @@ AFRAME.registerComponent("player-info", {
// This is to support using arbitrary models as avatars.
// TODO We can drop support for this when we go full VRM, or at least handle it earlier in the process.
if (!isSkinnedAvatar) {
modelEl.object3D.traverse(function(o) {
modelEl.object3D.traverse(function (o) {
if (o.isMesh) o.layers.set(Layers.CAMERA_LAYER_THIRD_PERSON_ONLY);
});
}
Expand Down
7 changes: 3 additions & 4 deletions src/components/video-texture-target.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { disposeTexture } from "../utils/material-utils";
import { createVideoOrAudioEl } from "../utils/media-utils";
import { findNode } from "../utils/three-utils";
import { Layers } from "./layers";
import { Layers } from "../camera-layers";

/**
* @component video-texture-source
Expand Down Expand Up @@ -35,9 +35,7 @@ AFRAME.registerComponent("video-texture-source", {
format: THREE.RGBAFormat,
minFilter: THREE.LinearFilter,
magFilter: THREE.NearestFilter,
encoding: THREE.sRGBEncoding,
depth: false,
stencil: false
encoding: THREE.sRGBEncoding
});

const texture = this.renderTarget.texture;
Expand Down Expand Up @@ -78,6 +76,7 @@ AFRAME.registerComponent("video-texture-source", {
sceneEl.object3D.autoUpdate = false;

renderer.setRenderTarget(this.renderTarget);
renderer.clearDepth();
renderer.render(sceneEl.object3D, this.camera);
renderer.setRenderTarget(null);

Expand Down
2 changes: 1 addition & 1 deletion src/components/water.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Layers } from "./layers";
import { Layers } from "../camera-layers";
import "../vendor/Water";
import waterNormalMap from "../assets/waternormals.jpg";
import HubsTextureLoader from "../loaders/HubsTextureLoader";
Expand Down
Loading