Skip to content

Commit 749ae4b

Browse files
felixtrzdmarcos
authored andcommitted
WebXR: Implement antialiased multiview using OCULUS_multiview (#15)
1 parent bb4f8f3 commit 749ae4b

File tree

9 files changed

+481
-51
lines changed

9 files changed

+481
-51
lines changed

examples/webxr_xr_ballshooter.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767

6868
//
6969

70-
renderer = new THREE.WebGLRenderer( { antialias: true } );
70+
renderer = new THREE.WebGLRenderer( { antialias: true, multiviewStereo: true } );
7171
renderer.setPixelRatio( window.devicePixelRatio );
7272
renderer.setSize( window.innerWidth, window.innerHeight );
7373
renderer.setAnimationLoop( animate );
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/**
2+
* @author fernandojsg / http://fernandojsg.com
3+
* @author Takahiro https://github.com/takahirox
4+
*/
5+
6+
import { WebGLRenderTarget } from './WebGLRenderTarget.js';
7+
8+
class WebGLMultiviewRenderTarget extends WebGLRenderTarget {
9+
10+
constructor( width, height, numViews, options = {} ) {
11+
12+
super( width, height, options );
13+
14+
this.depthBuffer = false;
15+
this.stencilBuffer = false;
16+
17+
this.numViews = numViews;
18+
19+
}
20+
21+
copy( source ) {
22+
23+
super.copy( source );
24+
25+
this.numViews = source.numViews;
26+
27+
return this;
28+
29+
}
30+
31+
}
32+
33+
WebGLMultiviewRenderTarget.prototype.isWebGLMultiviewRenderTarget = true;
34+
35+
export { WebGLMultiviewRenderTarget };

src/renderers/WebGLRenderer.js

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import { WebGLGeometries } from './webgl/WebGLGeometries.js';
3838
import { WebGLIndexedBufferRenderer } from './webgl/WebGLIndexedBufferRenderer.js';
3939
import { WebGLInfo } from './webgl/WebGLInfo.js';
4040
import { WebGLMorphtargets } from './webgl/WebGLMorphtargets.js';
41+
import { WebGLMultiview } from './webgl/WebGLMultiview.js';
4142
import { WebGLObjects } from './webgl/WebGLObjects.js';
4243
import { WebGLPrograms } from './webgl/WebGLPrograms.js';
4344
import { WebGLProperties } from './webgl/WebGLProperties.js';
@@ -71,6 +72,7 @@ class WebGLRenderer {
7172
powerPreference = 'default',
7273
failIfMajorPerformanceCaveat = false,
7374
reverseDepthBuffer = false,
75+
multiviewStereo = false,
7476
} = parameters;
7577

7678
this.isWebGLRenderer = true;
@@ -275,6 +277,7 @@ class WebGLRenderer {
275277
let extensions, capabilities, state, info;
276278
let properties, textures, cubemaps, cubeuvmaps, attributes, geometries, objects;
277279
let programCache, materials, renderLists, renderStates, clipping, shadowMap;
280+
let multiview;
278281

279282
let background, morphtargets, bufferRenderer, indexedBufferRenderer;
280283

@@ -313,6 +316,7 @@ class WebGLRenderer {
313316
renderLists = new WebGLRenderLists();
314317
renderStates = new WebGLRenderStates( extensions );
315318
background = new WebGLBackground( _this, cubemaps, cubeuvmaps, state, objects, _alpha, premultipliedAlpha );
319+
multiview = new WebGLMultiview( _this, extensions, _gl );
316320
shadowMap = new WebGLShadowMap( _this, objects, capabilities );
317321
uniformsGroups = new WebGLUniformsGroups( _gl, info, capabilities, state );
318322

@@ -1249,11 +1253,21 @@ class WebGLRenderer {
12491253

12501254
if ( _renderBackground ) background.render( scene );
12511255

1252-
for ( let i = 0, l = cameras.length; i < l; i ++ ) {
1256+
if ( xr.enabled && xr.isMultiview ) {
12531257

1254-
const camera2 = cameras[ i ];
1258+
textures.setDeferTextureUploads( true );
12551259

1256-
renderScene( currentRenderList, scene, camera2, camera2.viewport );
1260+
renderScene( currentRenderList, scene, camera, camera.cameras[ 0 ].viewport );
1261+
1262+
} else {
1263+
1264+
for ( let i = 0, l = cameras.length; i < l; i ++ ) {
1265+
1266+
const camera2 = cameras[ i ];
1267+
1268+
renderScene( currentRenderList, scene, camera2, camera2.viewport );
1269+
1270+
}
12571271

12581272
}
12591273

@@ -1779,6 +1793,7 @@ class WebGLRenderer {
17791793
materialProperties.vertexAlphas = parameters.vertexAlphas;
17801794
materialProperties.vertexTangents = parameters.vertexTangents;
17811795
materialProperties.toneMapping = parameters.toneMapping;
1796+
materialProperties.numMultiviewViews = parameters.numMultiviewViews;
17821797

17831798
}
17841799

@@ -1810,6 +1825,8 @@ class WebGLRenderer {
18101825

18111826
}
18121827

1828+
const numMultiviewViews = _currentRenderTarget && _currentRenderTarget.isWebGLMultiviewRenderTarget ? _currentRenderTarget.numViews : 0;
1829+
18131830
const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color;
18141831
const morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0;
18151832

@@ -1937,6 +1954,10 @@ class WebGLRenderer {
19371954

19381955
needsProgramChange = true;
19391956

1957+
} else if ( materialProperties.numMultiviewViews !== numMultiviewViews ) {
1958+
1959+
needsProgramChange = true;
1960+
19401961
}
19411962

19421963
} else {
@@ -1983,24 +2004,33 @@ class WebGLRenderer {
19832004

19842005
// common camera uniforms
19852006

1986-
const reverseDepthBuffer = state.buffers.depth.getReversed();
2007+
if ( program.numMultiviewViews > 0 ) {
19872008

1988-
if ( reverseDepthBuffer ) {
2009+
multiview.updateCameraProjectionMatricesUniform( camera, p_uniforms );
2010+
multiview.updateCameraViewMatricesUniform( camera, p_uniforms );
19892011

1990-
_currentProjectionMatrix.copy( camera.projectionMatrix );
2012+
} else {
19912013

1992-
toNormalizedProjectionMatrix( _currentProjectionMatrix );
1993-
toReversedProjectionMatrix( _currentProjectionMatrix );
2014+
const reverseDepthBuffer = state.buffers.depth.getReversed();
19942015

1995-
p_uniforms.setValue( _gl, 'projectionMatrix', _currentProjectionMatrix );
2016+
if ( reverseDepthBuffer ) {
19962017

1997-
} else {
2018+
_currentProjectionMatrix.copy( camera.projectionMatrix );
19982019

1999-
p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix );
2020+
toNormalizedProjectionMatrix( _currentProjectionMatrix );
2021+
toReversedProjectionMatrix( _currentProjectionMatrix );
20002022

2001-
}
2023+
p_uniforms.setValue( _gl, 'projectionMatrix', _currentProjectionMatrix );
2024+
2025+
} else {
2026+
2027+
p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix );
20022028

2003-
p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse );
2029+
}
2030+
2031+
p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse );
2032+
2033+
}
20042034

20052035
const uCamPos = p_uniforms.map.cameraPosition;
20062036

@@ -2162,8 +2192,17 @@ class WebGLRenderer {
21622192

21632193
// common matrices
21642194

2165-
p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix );
2166-
p_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix );
2195+
if ( program.numMultiviewViews > 0 ) {
2196+
2197+
multiview.updateObjectMatricesUniforms( object, camera, p_uniforms );
2198+
2199+
} else {
2200+
2201+
p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix );
2202+
p_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix );
2203+
2204+
}
2205+
21672206
p_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld );
21682207

21692208
// UBOs
@@ -2267,7 +2306,7 @@ class WebGLRenderer {
22672306

22682307
renderTargetProperties.__autoAllocateDepthBuffer = depthTexture === undefined;
22692308

2270-
if ( ! renderTargetProperties.__autoAllocateDepthBuffer ) {
2309+
if ( ! renderTargetProperties.__autoAllocateDepthBuffer && ! _currentRenderTarget.isWebGLMultiviewRenderTarget ) {
22712310

22722311
// The multisample_render_to_texture extension doesn't work properly if there
22732312
// are midframe flushes and an external depth buffer. Disable use of the extension.

src/renderers/webgl/WebGLBackground.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ function WebGLBackground( renderer, cubemaps, cubeuvmaps, state, objects, alpha,
9292
if ( boxMesh === undefined ) {
9393

9494
boxMesh = new Mesh(
95-
new BoxGeometry( 1, 1, 1 ),
95+
new BoxGeometry( 10000, 10000, 10000 ),
9696
new ShaderMaterial( {
9797
name: 'BackgroundCubeMaterial',
9898
uniforms: cloneUniforms( ShaderLib.backgroundCube.uniforms ),
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/**
2+
* @author fernandojsg / http://fernandojsg.com
3+
* @author Takahiro https://github.com/takahirox
4+
*/
5+
import { Matrix3 } from '../../math/Matrix3.js';
6+
import { Matrix4 } from '../../math/Matrix4.js';
7+
8+
class WebGLMultiview {
9+
10+
constructor( renderer, extensions, gl ) {
11+
12+
this.renderer = renderer;
13+
14+
this.DEFAULT_NUMVIEWS = 2;
15+
this.maxNumViews = 0;
16+
this.gl = gl;
17+
18+
this.extensions = extensions;
19+
20+
this.available = this.extensions.has( 'OCULUS_multiview' );
21+
22+
if ( this.available ) {
23+
24+
const extension = this.extensions.get( 'OCULUS_multiview' );
25+
26+
this.maxNumViews = this.gl.getParameter( extension.MAX_VIEWS_OVR );
27+
28+
this.mat4 = [];
29+
this.mat3 = [];
30+
this.cameraArray = [];
31+
32+
for ( var i = 0; i < this.maxNumViews; i ++ ) {
33+
34+
this.mat4[ i ] = new Matrix4();
35+
this.mat3[ i ] = new Matrix3();
36+
37+
}
38+
39+
}
40+
41+
}
42+
43+
//
44+
getCameraArray( camera ) {
45+
46+
if ( camera.isArrayCamera ) return camera.cameras;
47+
48+
this.cameraArray[ 0 ] = camera;
49+
50+
return this.cameraArray;
51+
52+
}
53+
54+
updateCameraProjectionMatricesUniform( camera, uniforms ) {
55+
56+
var cameras = this.getCameraArray( camera );
57+
58+
for ( var i = 0; i < cameras.length; i ++ ) {
59+
60+
this.mat4[ i ].copy( cameras[ i ].projectionMatrix );
61+
62+
}
63+
64+
uniforms.setValue( this.gl, 'projectionMatrices', this.mat4 );
65+
66+
}
67+
68+
updateCameraViewMatricesUniform( camera, uniforms ) {
69+
70+
var cameras = this.getCameraArray( camera );
71+
72+
for ( var i = 0; i < cameras.length; i ++ ) {
73+
74+
this.mat4[ i ].copy( cameras[ i ].matrixWorldInverse );
75+
76+
}
77+
78+
uniforms.setValue( this.gl, 'viewMatrices', this.mat4 );
79+
80+
}
81+
82+
updateObjectMatricesUniforms( object, camera, uniforms ) {
83+
84+
var cameras = this.getCameraArray( camera );
85+
86+
for ( var i = 0; i < cameras.length; i ++ ) {
87+
88+
this.mat4[ i ].multiplyMatrices( cameras[ i ].matrixWorldInverse, object.matrixWorld );
89+
this.mat3[ i ].getNormalMatrix( this.mat4[ i ] );
90+
91+
}
92+
93+
uniforms.setValue( this.gl, 'modelViewMatrices', this.mat4 );
94+
uniforms.setValue( this.gl, 'normalMatrices', this.mat3 );
95+
96+
}
97+
98+
}
99+
100+
export { WebGLMultiview };

src/renderers/webgl/WebGLProgram.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,8 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) {
497497
let prefixVertex, prefixFragment;
498498
let versionString = parameters.glslVersion ? '#version ' + parameters.glslVersion + '\n' : '';
499499

500+
const numMultiviewViews = parameters.numMultiviewViews;
501+
500502
if ( parameters.isRawShaderMaterial ) {
501503

502504
prefixVertex = [
@@ -884,6 +886,53 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) {
884886
'#define textureCubeGradEXT textureGrad'
885887
].join( '\n' ) + '\n' + prefixFragment;
886888

889+
// Multiview
890+
891+
if ( numMultiviewViews > 0 ) {
892+
893+
// TODO: fix light transforms here?
894+
895+
prefixVertex = [
896+
'#extension GL_OVR_multiview : require',
897+
'layout(num_views = ' + numMultiviewViews + ') in;',
898+
'#define VIEW_ID gl_ViewID_OVR'
899+
].join( '\n' ) + '\n' + prefixVertex;
900+
901+
prefixVertex = prefixVertex.replace(
902+
[
903+
'uniform mat4 modelViewMatrix;',
904+
'uniform mat4 projectionMatrix;',
905+
'uniform mat4 viewMatrix;',
906+
'uniform mat3 normalMatrix;'
907+
].join( '\n' ),
908+
[
909+
'uniform mat4 modelViewMatrices[' + numMultiviewViews + '];',
910+
'uniform mat4 projectionMatrices[' + numMultiviewViews + '];',
911+
'uniform mat4 viewMatrices[' + numMultiviewViews + '];',
912+
'uniform mat3 normalMatrices[' + numMultiviewViews + '];',
913+
914+
'#define modelViewMatrix modelViewMatrices[VIEW_ID]',
915+
'#define projectionMatrix projectionMatrices[VIEW_ID]',
916+
'#define viewMatrix viewMatrices[VIEW_ID]',
917+
'#define normalMatrix normalMatrices[VIEW_ID]'
918+
].join( '\n' )
919+
);
920+
921+
prefixFragment = [
922+
'#extension GL_OVR_multiview : require',
923+
'#define VIEW_ID gl_ViewID_OVR'
924+
].join( '\n' ) + '\n' + prefixFragment;
925+
926+
prefixFragment = prefixFragment.replace(
927+
'uniform mat4 viewMatrix;',
928+
[
929+
'uniform mat4 viewMatrices[' + numMultiviewViews + '];',
930+
'#define viewMatrix viewMatrices[VIEW_ID]'
931+
].join( '\n' )
932+
);
933+
934+
}
935+
887936
}
888937

889938
const vertexGlsl = versionString + prefixVertex + vertexShader;
@@ -1076,6 +1125,7 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) {
10761125
this.program = program;
10771126
this.vertexShader = glVertexShader;
10781127
this.fragmentShader = glFragmentShader;
1128+
this.numMultiviewViews = numMultiviewViews;
10791129

10801130
return this;
10811131

0 commit comments

Comments
 (0)