Skip to content

Commit 0c1185d

Browse files
authored
WebGPURenderer: Improve ArrayCamera performance and fixes (#30313)
* wip arraycamera * Update webgl_camera_array2.html * updated approach * Update WebGLBackend.js * added webgpu support * update example * revision * update camera array * Update Camera.js * update * Update webgpu_camera_array.html * cleanup * rev
1 parent a13e4b0 commit 0c1185d

File tree

10 files changed

+345
-113
lines changed

10 files changed

+345
-113
lines changed
-36 KB
Loading

examples/webgpu_camera_array.html

Lines changed: 45 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<link type="text/css" rel="stylesheet" href="main.css">
88
</head>
99
<body>
10+
1011
<script type="importmap">
1112
{
1213
"imports": {
@@ -22,51 +23,43 @@
2223

2324
import * as THREE from 'three';
2425

26+
import Stats from 'three/addons/libs/stats.module.js';
27+
2528
let camera, scene, renderer;
2629
let mesh;
30+
let stats;
31+
2732
const AMOUNT = 6;
2833

2934
init();
3035

3136
function init() {
3237

33-
const ASPECT_RATIO = window.innerWidth / window.innerHeight;
34-
35-
const WIDTH = ( window.innerWidth / AMOUNT );
36-
const HEIGHT = ( window.innerHeight / AMOUNT );
38+
const subCameras = [];
3739

38-
const cameras = [];
40+
for ( let i = 0; i < AMOUNT * AMOUNT; i ++ ) {
3941

40-
for ( let y = 0; y < AMOUNT; y ++ ) {
42+
const subCamera = new THREE.PerspectiveCamera( 40, 1, 0.1, 10 );
43+
subCamera.viewport = new THREE.Vector4();
4144

42-
for ( let x = 0; x < AMOUNT; x ++ ) {
43-
44-
const subcamera = new THREE.PerspectiveCamera( 40, ASPECT_RATIO, 0.1, 10 );
45-
subcamera.viewport = new THREE.Vector4( Math.floor( x * WIDTH ), Math.floor( y * HEIGHT ), Math.ceil( WIDTH ), Math.ceil( HEIGHT ) );
46-
subcamera.position.x = ( x / AMOUNT ) - 0.5;
47-
subcamera.position.y = 0.5 - ( y / AMOUNT );
48-
subcamera.position.z = 1.5;
49-
subcamera.position.multiplyScalar( 2 );
50-
subcamera.lookAt( 0, 0, 0 );
51-
subcamera.updateMatrixWorld();
52-
cameras.push( subcamera );
53-
54-
}
45+
subCameras.push( subCamera );
5546

5647
}
5748

58-
camera = new THREE.ArrayCamera( cameras );
49+
camera = new THREE.ArrayCamera( subCameras );
5950
camera.position.z = 3;
6051

52+
updateCameras();
53+
6154
scene = new THREE.Scene();
6255

6356
scene.add( new THREE.AmbientLight( 0x999999 ) );
6457

6558
const light = new THREE.DirectionalLight( 0xffffff, 3 );
6659
light.position.set( 0.5, 0.5, 1 );
6760
light.castShadow = true;
68-
light.shadow.camera.zoom = 4; // tighter shadow map
6961
light.shadow.bias = - 0.001;
62+
light.shadow.camera.zoom = 4; // tighter shadow map
7063
scene.add( light );
7164

7265
const geometryBackground = new THREE.PlaneGeometry( 100, 100 );
@@ -77,15 +70,15 @@
7770
background.position.set( 0, 0, - 1 );
7871
scene.add( background );
7972

80-
const geometryCylinder = new THREE.BoxGeometry();
73+
const geometryCylinder = new THREE.CylinderGeometry( 0.5, 0.5, 1, 32 );
8174
const materialCylinder = new THREE.MeshPhongMaterial( { color: 0xff0000 } );
8275

8376
mesh = new THREE.Mesh( geometryCylinder, materialCylinder );
8477
mesh.castShadow = true;
8578
mesh.receiveShadow = true;
8679
scene.add( mesh );
8780

88-
renderer = new THREE.WebGPURenderer( { antialias: true } );
81+
renderer = new THREE.WebGPURenderer( /*{ forceWebGL: true }*/ );
8982
renderer.setPixelRatio( window.devicePixelRatio );
9083
renderer.setSize( window.innerWidth, window.innerHeight );
9184
renderer.setAnimationLoop( animate );
@@ -96,13 +89,18 @@
9689

9790
window.addEventListener( 'resize', onWindowResize );
9891

92+
//
93+
94+
stats = new Stats();
95+
document.body.appendChild( stats.dom );
96+
9997
}
10098

101-
function onWindowResize() {
99+
function updateCameras() {
102100

103101
const ASPECT_RATIO = window.innerWidth / window.innerHeight;
104-
const WIDTH = ( window.innerWidth / AMOUNT );
105-
const HEIGHT = ( window.innerHeight / AMOUNT );
102+
const WIDTH = window.innerWidth / AMOUNT;
103+
const HEIGHT = window.innerHeight / AMOUNT;
106104

107105
camera.aspect = ASPECT_RATIO;
108106
camera.updateProjectionMatrix();
@@ -112,28 +110,42 @@
112110
for ( let x = 0; x < AMOUNT; x ++ ) {
113111

114112
const subcamera = camera.cameras[ AMOUNT * y + x ];
113+
subcamera.copy( camera ); // copy fov, aspect ratio, near, far from the root camera
115114

116-
subcamera.viewport.set(
117-
Math.floor( x * WIDTH ),
118-
Math.floor( y * HEIGHT ),
119-
Math.ceil( WIDTH ),
120-
Math.ceil( HEIGHT ) );
121-
122-
subcamera.aspect = ASPECT_RATIO;
115+
subcamera.viewport.set( Math.floor( x * WIDTH ), Math.floor( y * HEIGHT ), Math.ceil( WIDTH ), Math.ceil( HEIGHT ) );
123116
subcamera.updateProjectionMatrix();
124117

118+
subcamera.position.x = ( x / AMOUNT ) - 0.5;
119+
subcamera.position.y = 0.5 - ( y / AMOUNT );
120+
subcamera.position.z = 1.5 + ( ( x + y ) * .5 );
121+
subcamera.position.multiplyScalar( 2 );
122+
123+
subcamera.lookAt( 0, 0, 0 );
124+
subcamera.updateMatrixWorld();
125+
125126
}
126127

127128
}
128129

130+
}
131+
132+
function onWindowResize() {
133+
134+
updateCameras();
135+
129136
renderer.setSize( window.innerWidth, window.innerHeight );
130137

131138
}
132139

133140
function animate() {
134141

142+
mesh.rotation.x += 0.005;
143+
mesh.rotation.z += 0.01;
144+
135145
renderer.render( scene, camera );
136146

147+
stats.update();
148+
137149
}
138150

139151
</script>

src/Three.TSL.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ export const bypass = TSL.bypass;
9393
export const cache = TSL.cache;
9494
export const call = TSL.call;
9595
export const cameraFar = TSL.cameraFar;
96+
export const cameraIndex = TSL.cameraIndex;
9697
export const cameraNear = TSL.cameraNear;
9798
export const cameraNormalMatrix = TSL.cameraNormalMatrix;
9899
export const cameraPosition = TSL.cameraPosition;

src/cameras/ArrayCamera.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ class ArrayCamera extends PerspectiveCamera {
99
this.isArrayCamera = true;
1010

1111
this.cameras = array;
12+
this.index = 0;
1213

1314
}
1415

src/nodes/accessors/Camera.js

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
import { uniform } from '../core/UniformNode.js';
2-
import { renderGroup } from '../core/UniformGroupNode.js';
2+
import { renderGroup, sharedUniformGroup } from '../core/UniformGroupNode.js';
33
import { Vector3 } from '../../math/Vector3.js';
4+
import { Fn } from '../tsl/TSLBase.js';
5+
import { uniformArray } from './UniformArrayNode.js';
46

57
/** @module Camera **/
68

9+
/**
10+
* TSL object that represents the current `index` value of the camera if used ArrayCamera.
11+
*
12+
* @type {UniformNode<uint>}
13+
*/
14+
export const cameraIndex = /*@__PURE__*/ uniform( 'uint' ).setGroup( sharedUniformGroup( 'cameraIndex' ) ).vertexStage();
15+
716
/**
817
* TSL object that represents the `near` value of the camera used for the current render.
918
*
@@ -23,7 +32,33 @@ export const cameraFar = /*@__PURE__*/ uniform( 'float' ).label( 'cameraFar' ).s
2332
*
2433
* @type {UniformNode<mat4>}
2534
*/
26-
export const cameraProjectionMatrix = /*@__PURE__*/ uniform( 'mat4' ).label( 'cameraProjectionMatrix' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.projectionMatrix );
35+
export const cameraProjectionMatrix = /*@__PURE__*/ ( Fn( ( { camera } ) => {
36+
37+
let cameraProjectionMatrix;
38+
39+
if ( camera.isArrayCamera ) {
40+
41+
const matrices = [];
42+
43+
for ( const subCamera of camera.cameras ) {
44+
45+
matrices.push( subCamera.projectionMatrix );
46+
47+
}
48+
49+
const cameraProjectionMatrices = uniformArray( matrices ).setGroup( renderGroup ).label( 'cameraProjectionMatrices' );
50+
51+
cameraProjectionMatrix = cameraProjectionMatrices.element( cameraIndex ).toVar( 'cameraProjectionMatrix' );
52+
53+
} else {
54+
55+
cameraProjectionMatrix = uniform( 'mat4' ).label( 'cameraProjectionMatrix' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.projectionMatrix );
56+
57+
}
58+
59+
return cameraProjectionMatrix;
60+
61+
} ).once() )();
2762

2863
/**
2964
* TSL object that represents the inverse projection matrix of the camera used for the current render.
@@ -37,7 +72,33 @@ export const cameraProjectionMatrixInverse = /*@__PURE__*/ uniform( 'mat4' ).lab
3772
*
3873
* @type {UniformNode<mat4>}
3974
*/
40-
export const cameraViewMatrix = /*@__PURE__*/ uniform( 'mat4' ).label( 'cameraViewMatrix' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.matrixWorldInverse );
75+
export const cameraViewMatrix = /*@__PURE__*/ ( Fn( ( { camera } ) => {
76+
77+
let cameraViewMatrix;
78+
79+
if ( camera.isArrayCamera ) {
80+
81+
const matrices = [];
82+
83+
for ( const subCamera of camera.cameras ) {
84+
85+
matrices.push( subCamera.matrixWorldInverse );
86+
87+
}
88+
89+
const cameraViewMatrices = uniformArray( matrices ).setGroup( renderGroup ).label( 'cameraViewMatrices' );
90+
91+
cameraViewMatrix = cameraViewMatrices.element( cameraIndex ).toVar( 'cameraViewMatrix' );
92+
93+
} else {
94+
95+
cameraViewMatrix = uniform( 'mat4' ).label( 'cameraViewMatrix' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.matrixWorldInverse );
96+
97+
}
98+
99+
return cameraViewMatrix;
100+
101+
} ).once() )();
41102

42103
/**
43104
* TSL object that represents the world matrix of the camera used for the current render.

src/renderers/common/RenderObject.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,26 @@ class RenderObject {
376376

377377
}
378378

379+
/**
380+
* Returns a binding group by group name of this render object.
381+
*
382+
* @param {String} name - The name of the binding group.
383+
* @return {BindGroup?} The bindings.
384+
*/
385+
getBindingGroup( name ) {
386+
387+
for ( const bindingGroup of this.getBindings() ) {
388+
389+
if ( bindingGroup.name === name ) {
390+
391+
return bindingGroup;
392+
393+
}
394+
395+
}
396+
397+
}
398+
379399
/**
380400
* Returns the index of the render object's geometry.
381401
*

src/renderers/common/Renderer.js

Lines changed: 13 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1229,9 +1229,19 @@ class Renderer {
12291229
if ( camera.coordinateSystem !== coordinateSystem ) {
12301230

12311231
camera.coordinateSystem = coordinateSystem;
1232-
12331232
camera.updateProjectionMatrix();
12341233

1234+
if ( camera.isArrayCamera ) {
1235+
1236+
for ( const subCamera of camera.cameras ) {
1237+
1238+
subCamera.coordinateSystem = coordinateSystem;
1239+
subCamera.updateProjectionMatrix();
1240+
1241+
}
1242+
1243+
}
1244+
12351245
}
12361246

12371247
//
@@ -2596,47 +2606,11 @@ class Renderer {
25962606
*/
25972607
_renderObjects( renderList, camera, scene, lightsNode, passId = null ) {
25982608

2599-
// process renderable objects
2600-
26012609
for ( let i = 0, il = renderList.length; i < il; i ++ ) {
26022610

2603-
const renderItem = renderList[ i ];
2604-
2605-
const { object, geometry, material, group, clippingContext } = renderItem;
2606-
2607-
if ( camera.isArrayCamera ) {
2608-
2609-
const cameras = camera.cameras;
2610-
2611-
for ( let j = 0, jl = cameras.length; j < jl; j ++ ) {
2611+
const { object, geometry, material, group, clippingContext } = renderList[ i ];
26122612

2613-
const camera2 = cameras[ j ];
2614-
2615-
if ( object.layers.test( camera2.layers ) ) {
2616-
2617-
const vp = camera2.viewport;
2618-
const minDepth = ( vp.minDepth === undefined ) ? 0 : vp.minDepth;
2619-
const maxDepth = ( vp.maxDepth === undefined ) ? 1 : vp.maxDepth;
2620-
2621-
const viewportValue = this._currentRenderContext.viewportValue;
2622-
viewportValue.copy( vp ).multiplyScalar( this._pixelRatio ).floor();
2623-
viewportValue.minDepth = minDepth;
2624-
viewportValue.maxDepth = maxDepth;
2625-
this._currentRenderContext.viewport = true;
2626-
2627-
this.backend.updateViewport( this._currentRenderContext );
2628-
2629-
this._currentRenderObjectFunction( object, scene, camera2, geometry, material, group, lightsNode, clippingContext, passId );
2630-
2631-
}
2632-
2633-
}
2634-
2635-
} else {
2636-
2637-
this._currentRenderObjectFunction( object, scene, camera, geometry, material, group, lightsNode, clippingContext, passId );
2638-
2639-
}
2613+
this._currentRenderObjectFunction( object, scene, camera, geometry, material, group, lightsNode, clippingContext, passId );
26402614

26412615
}
26422616

0 commit comments

Comments
 (0)