Skip to content

Commit 77adfd8

Browse files
committed
optimize WebXR render path
1 parent e07583b commit 77adfd8

File tree

6 files changed

+331
-25
lines changed

6 files changed

+331
-25
lines changed
32.9 KB
Loading

examples/webgpu_xr_native_layers.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@
305305
window.addEventListener( 'resize', onWindowResize );
306306

307307
// set up rollercoaster
308-
rollercoasterLayer = renderer.xr.createCylinderLayer( 1, Math.PI / 2, 2, new THREE.Vector3( 0, 1.5, - 0.5 ), new THREE.Quaternion(), 1024, 1024, renderRollercoaster );
308+
rollercoasterLayer = renderer.xr.createCylinderLayer( 1, Math.PI / 2, 2, new THREE.Vector3( 0, 1.5, - 0.5 ), new THREE.Quaternion(), 1500, 1000, renderRollercoaster );
309309
scene.add( rollercoasterLayer );
310310

311311
rcscene = new THREE.Scene();
@@ -410,7 +410,7 @@
410410
funfairs.push( rcmesh );
411411

412412
// set up horse animation
413-
horseLayer = renderer.xr.createQuadLayer( 1, 1, new THREE.Vector3( - 1.5, 1.5, - 1.5 ), new THREE.Quaternion(), 1024, 1024, renderQuad );
413+
horseLayer = renderer.xr.createQuadLayer( 1, 1, new THREE.Vector3( - 1.5, 1.5, - 1.5 ), new THREE.Quaternion(), 800, 800, renderQuad );
414414
scene.add( horseLayer );
415415

416416
horseLayer.geometry = new THREE.CircleGeometry( .5, 64 );
@@ -473,7 +473,7 @@
473473

474474
const bbox = new THREE.Box3().setFromObject( guiScene );
475475

476-
guiLayer = renderer.xr.createQuadLayer( 1.2, .8, new THREE.Vector3( 1.5, 1.5, - 1.5 ), new THREE.Quaternion(), 1024, 1024, renderGui );
476+
guiLayer = renderer.xr.createQuadLayer( 1.2, .8, new THREE.Vector3( 1.5, 1.5, - 1.5 ), new THREE.Quaternion(), 1280, 800, renderGui );
477477
scene.add( guiLayer );
478478

479479
guiCamera.left = bbox.min.x;

examples/webgpu_xr_rollercoaster.html

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<title>three.js vr - roller coaster</title>
5+
<meta charset="utf-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
7+
<link type="text/css" rel="stylesheet" href="main.css">
8+
</head>
9+
<body>
10+
<script type="importmap">
11+
{
12+
"imports": {
13+
"three": "../build/three.webgpu.js",
14+
"three/webgpu": "../build/three.webgpu.js",
15+
"three/addons/": "./jsm/"
16+
}
17+
}
18+
</script>
19+
20+
<script type="module">
21+
22+
import * as THREE from 'three';
23+
24+
import {
25+
RollerCoasterGeometry,
26+
RollerCoasterShadowGeometry,
27+
RollerCoasterLiftersGeometry,
28+
TreesGeometry,
29+
SkyGeometry
30+
} from 'three/addons/misc/RollerCoaster.js';
31+
import { VRButton } from 'three/addons/webxr/VRButton.js';
32+
33+
let mesh, material, geometry;
34+
35+
const renderer = new THREE.WebGPURenderer( { antialias: true, forceWebGL: true, colorBufferType: THREE.UnsignedByteType, multiview: false } );
36+
renderer.setPixelRatio( window.devicePixelRatio );
37+
renderer.setSize( window.innerWidth, window.innerHeight );
38+
renderer.setAnimationLoop( animate );
39+
renderer.xr.enabled = true;
40+
renderer.xr.setReferenceSpaceType( 'local' );
41+
document.body.appendChild( renderer.domElement );
42+
43+
document.body.appendChild( VRButton.createButton( renderer ) );
44+
45+
//
46+
47+
const scene = new THREE.Scene();
48+
scene.background = new THREE.Color( 0xf0f0ff );
49+
50+
const light = new THREE.HemisphereLight( 0xfff0f0, 0x60606, 3 );
51+
light.position.set( 1, 1, 1 );
52+
scene.add( light );
53+
54+
const train = new THREE.Object3D();
55+
scene.add( train );
56+
57+
const camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.1, 500 );
58+
train.add( camera );
59+
60+
// environment
61+
62+
geometry = new THREE.PlaneGeometry( 500, 500, 15, 15 );
63+
geometry.rotateX( - Math.PI / 2 );
64+
65+
const positions = geometry.attributes.position.array;
66+
const vertex = new THREE.Vector3();
67+
68+
for ( let i = 0; i < positions.length; i += 3 ) {
69+
70+
vertex.fromArray( positions, i );
71+
72+
vertex.x += Math.random() * 10 - 5;
73+
vertex.z += Math.random() * 10 - 5;
74+
75+
const distance = ( vertex.distanceTo( scene.position ) / 5 ) - 25;
76+
vertex.y = Math.random() * Math.max( 0, distance );
77+
78+
vertex.toArray( positions, i );
79+
80+
}
81+
82+
geometry.computeVertexNormals();
83+
84+
material = new THREE.MeshLambertMaterial( {
85+
color: 0x407000
86+
} );
87+
88+
mesh = new THREE.Mesh( geometry, material );
89+
scene.add( mesh );
90+
91+
geometry = new TreesGeometry( mesh );
92+
material = new THREE.MeshBasicMaterial( {
93+
side: THREE.DoubleSide, vertexColors: true
94+
} );
95+
mesh = new THREE.Mesh( geometry, material );
96+
scene.add( mesh );
97+
98+
geometry = new SkyGeometry();
99+
material = new THREE.MeshBasicMaterial( { color: 0xffffff } );
100+
mesh = new THREE.Mesh( geometry, material );
101+
scene.add( mesh );
102+
103+
//
104+
105+
const PI2 = Math.PI * 2;
106+
107+
const curve = ( function () {
108+
109+
const vector = new THREE.Vector3();
110+
const vector2 = new THREE.Vector3();
111+
112+
return {
113+
114+
getPointAt: function ( t ) {
115+
116+
t = t * PI2;
117+
118+
const x = Math.sin( t * 3 ) * Math.cos( t * 4 ) * 50;
119+
const y = Math.sin( t * 10 ) * 2 + Math.cos( t * 17 ) * 2 + 5;
120+
const z = Math.sin( t ) * Math.sin( t * 4 ) * 50;
121+
122+
return vector.set( x, y, z ).multiplyScalar( 2 );
123+
124+
},
125+
126+
getTangentAt: function ( t ) {
127+
128+
const delta = 0.0001;
129+
const t1 = Math.max( 0, t - delta );
130+
const t2 = Math.min( 1, t + delta );
131+
132+
return vector2.copy( this.getPointAt( t2 ) )
133+
.sub( this.getPointAt( t1 ) ).normalize();
134+
135+
}
136+
137+
};
138+
139+
} )();
140+
141+
geometry = new RollerCoasterGeometry( curve, 1500 );
142+
material = new THREE.MeshPhongMaterial( {
143+
vertexColors: true
144+
} );
145+
mesh = new THREE.Mesh( geometry, material );
146+
scene.add( mesh );
147+
148+
geometry = new RollerCoasterLiftersGeometry( curve, 100 );
149+
material = new THREE.MeshPhongMaterial();
150+
mesh = new THREE.Mesh( geometry, material );
151+
mesh.position.y = 0.1;
152+
scene.add( mesh );
153+
154+
geometry = new RollerCoasterShadowGeometry( curve, 500 );
155+
material = new THREE.MeshBasicMaterial( {
156+
color: 0x305000, depthWrite: false, transparent: true
157+
} );
158+
mesh = new THREE.Mesh( geometry, material );
159+
mesh.position.y = 0.1;
160+
scene.add( mesh );
161+
162+
const funfairs = [];
163+
164+
//
165+
166+
geometry = new THREE.CylinderGeometry( 10, 10, 5, 15 );
167+
material = new THREE.MeshLambertMaterial( {
168+
color: 0xff8080
169+
} );
170+
mesh = new THREE.Mesh( geometry, material );
171+
mesh.position.set( - 80, 10, - 70 );
172+
mesh.rotation.x = Math.PI / 2;
173+
scene.add( mesh );
174+
175+
funfairs.push( mesh );
176+
177+
geometry = new THREE.CylinderGeometry( 5, 6, 4, 10 );
178+
material = new THREE.MeshLambertMaterial( {
179+
color: 0x8080ff
180+
} );
181+
mesh = new THREE.Mesh( geometry, material );
182+
mesh.position.set( 50, 2, 30 );
183+
scene.add( mesh );
184+
185+
funfairs.push( mesh );
186+
187+
//
188+
189+
window.addEventListener( 'resize', onWindowResize );
190+
191+
function onWindowResize() {
192+
193+
camera.aspect = window.innerWidth / window.innerHeight;
194+
camera.updateProjectionMatrix();
195+
196+
renderer.setSize( window.innerWidth, window.innerHeight );
197+
198+
}
199+
200+
//
201+
202+
const position = new THREE.Vector3();
203+
const tangent = new THREE.Vector3();
204+
205+
const lookAt = new THREE.Vector3();
206+
207+
let velocity = 0;
208+
let progress = 0;
209+
210+
let prevTime = performance.now();
211+
212+
function animate() {
213+
214+
const time = performance.now();
215+
const delta = time - prevTime;
216+
217+
for ( let i = 0; i < funfairs.length; i ++ ) {
218+
219+
funfairs[ i ].rotation.y = time * 0.0004;
220+
221+
}
222+
223+
//
224+
225+
progress += velocity;
226+
progress = progress % 1;
227+
228+
position.copy( curve.getPointAt( progress ) );
229+
position.y += 0.3;
230+
231+
train.position.copy( position );
232+
233+
tangent.copy( curve.getTangentAt( progress ) );
234+
235+
velocity -= tangent.y * 0.0000001 * delta;
236+
velocity = Math.max( 0.00004, Math.min( 0.0002, velocity ) );
237+
238+
train.lookAt( lookAt.copy( position ).sub( tangent ) );
239+
240+
//
241+
242+
renderer.render( scene, camera );
243+
244+
prevTime = time;
245+
246+
}
247+
248+
</script>
249+
250+
</body>
251+
</html>

src/renderers/common/Renderer.js

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,8 @@ class Renderer {
314314
*/
315315
this._scissor = new Vector4( 0, 0, this._width, this._height );
316316

317+
this._forceViewPort = false;
318+
317319
/**
318320
* Whether the scissor test should be enabled or not.
319321
*
@@ -1250,6 +1252,7 @@ class Renderer {
12501252
frameBufferTarget.scissor.multiplyScalar( this._pixelRatio );
12511253
frameBufferTarget.scissorTest = this._scissorTest;
12521254
frameBufferTarget.multiview = outputRenderTarget !== null ? outputRenderTarget.multiview : false;
1255+
frameBufferTarget.resolveDepthBuffer = outputRenderTarget !== null ? outputRenderTarget.resolveDepthBuffer : true;
12531256

12541257
return frameBufferTarget;
12551258

@@ -1380,7 +1383,7 @@ class Renderer {
13801383
renderContext.viewportValue.height >>= activeMipmapLevel;
13811384
renderContext.viewportValue.minDepth = minDepth;
13821385
renderContext.viewportValue.maxDepth = maxDepth;
1383-
renderContext.viewport = renderContext.viewportValue.equals( _screen ) === false;
1386+
renderContext.viewport = renderContext.viewportValue.equals( _screen ) === false || this._forceViewPort;
13841387

13851388
renderContext.scissorValue.copy( scissor ).multiplyScalar( pixelRatio ).floor();
13861389
renderContext.scissor = this._scissorTest && renderContext.scissorValue.equals( _screen ) === false;
@@ -1688,6 +1691,7 @@ class Renderer {
16881691

16891692
this.domElement.width = Math.floor( width * pixelRatio );
16901693
this.domElement.height = Math.floor( height * pixelRatio );
1694+
this._forceViewPort = false;
16911695

16921696
this.setViewport( 0, 0, width, height );
16931697

@@ -1701,17 +1705,24 @@ class Renderer {
17011705
* @param {number} width - The width in logical pixels.
17021706
* @param {number} height - The height in logical pixels.
17031707
* @param {boolean} [updateStyle=true] - Whether to update the `style` attribute of the canvas or not.
1708+
* @param {boolean} [updateDomElement=true] - Whether to update the underlying canvas element's pixel store.
17041709
*/
1705-
setSize( width, height, updateStyle = true ) {
1710+
setSize( width, height, updateStyle = true, updateDomElement = true ) {
17061711

17071712
// Renderer can't be resized while presenting in XR.
17081713
if ( this.xr && this.xr.isPresenting ) return;
17091714

17101715
this._width = width;
17111716
this._height = height;
17121717

1713-
this.domElement.width = Math.floor( width * this._pixelRatio );
1714-
this.domElement.height = Math.floor( height * this._pixelRatio );
1718+
if ( updateDomElement ) {
1719+
1720+
this.domElement.width = Math.floor( width * this._pixelRatio );
1721+
this.domElement.height = Math.floor( height * this._pixelRatio );
1722+
1723+
}
1724+
1725+
this._forceViewPort = ! updateDomElement;
17151726

17161727
if ( updateStyle === true ) {
17171728

0 commit comments

Comments
 (0)