Skip to content

Commit 76f14bb

Browse files
authored
Merge pull request #200 from cabanier/viewportscaling
Add sample file to demonstrate viewport scaling
2 parents 97550eb + fabcd3f commit 76f14bb

File tree

2 files changed

+226
-2
lines changed

2 files changed

+226
-2
lines changed

index.html

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,12 @@ <h2 class='tagline'>Sample Pages</h2>
196196
description: 'Demonstrates using the Hand Tracking API to track the user\'s hands.'},
197197

198198
{ title: 'Framebuffer Scaling', category: 'Performance',
199-
path: 'framebuffer-scaling.html',
200-
description: 'Demonstrates scaling a layer\'s framebuffer to statically control performance or quality.' },
199+
path: 'framebuffer-scaling.html',
200+
description: 'Demonstrates scaling a layer\'s framebuffer to statically control performance or quality.' },
201+
202+
{ title: 'Viewport Scaling', category: 'Performance',
203+
path: 'viewport-scaling.html',
204+
description: 'Demonstrates dynamcially scaling a layer\'s viewport to control performance at the expense of quality.' },
201205

202206
{ title: '360 Stereo Photos', category: 'Content',
203207
path: '360-photos.html',

viewport-scaling.html

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
<!doctype html>
2+
<!--
3+
Copyright 2024 The Immersive Web Community Group
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy of
6+
this software and associated documentation files (the "Software"), to deal in
7+
the Software without restriction, including without limitation the rights to
8+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9+
the Software, and to permit persons to whom the Software is furnished to do so,
10+
subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21+
-->
22+
<html>
23+
<head>
24+
<meta charset='utf-8'>
25+
<meta name='viewport' content='width=device-width, initial-scale=1, user-scalable=no'>
26+
<meta name='mobile-web-app-capable' content='yes'>
27+
<meta name='apple-mobile-web-app-capable' content='yes'>
28+
<link rel='icon' type='image/png' sizes='32x32' href='favicon-32x32.png'>
29+
<link rel='icon' type='image/png' sizes='96x96' href='favicon-96x96.png'>
30+
<link rel='stylesheet' href='css/common.css'>
31+
32+
<title>Immersive Viewport scaling</title>
33+
</head>
34+
<body>
35+
<header>
36+
<details open>
37+
<summary>Viewport scaling</summary>
38+
<p>
39+
This sample demonstrates how a session can request to render to
40+
a smaller area of its layer. This can be used to dynamically
41+
lower the number of pixels that need to be rendered. This can
42+
help experiences that are fragment bound for a short amount
43+
of time.
44+
<a class="back" href="./">Back</a>
45+
<br/>
46+
Use projection layer: <input id='useLayer' type='checkbox'/>
47+
</p>
48+
</details>
49+
</header>
50+
<main style='text-align: center;'>
51+
<p>Click 'Enter XR' to see content</p>
52+
</main>
53+
<script type="module">
54+
import {WebXRButton} from './js/util/webxr-button.js';
55+
import {Scene, WebXRView} from './js/render/scenes/scene.js';
56+
import {Renderer, createWebGLContext} from './js/render/core/renderer.js';
57+
import {Gltf2Node} from './js/render/nodes/gltf2.js';
58+
import {SkyboxNode} from './js/render/nodes/skybox.js';
59+
import {QueryArgs} from './js/util/query-args.js';
60+
61+
import WebXRPolyfill from './js/third-party/webxr-polyfill/build/webxr-polyfill.module.js';
62+
if (QueryArgs.getBool('usePolyfill', true)) {
63+
let polyfill = new WebXRPolyfill();
64+
}
65+
66+
// XR globals.
67+
let xrButton = null;
68+
let xrRefSpace = null;
69+
70+
// WebGL scene globals.
71+
let gl = null;
72+
let renderer = null;
73+
let scene = new Scene();
74+
let scale = 1;
75+
let scale_down = true;
76+
let useLayerCheckbox = document.getElementById('useLayer');
77+
let useLayer = false;
78+
// layer support
79+
let xrGLFactory = null;
80+
let xrFramebuffer = null;
81+
let proj_layer = null;
82+
let count = true;
83+
84+
scene.addNode(new Gltf2Node({url: 'media/gltf/space/space.gltf'}));
85+
scene.addNode(new SkyboxNode({url: 'media/textures/milky-way-4k.png'}));
86+
87+
function initXR() {
88+
xrButton = new WebXRButton({
89+
onRequestSession: onRequestSession,
90+
onEndSession: onEndSession
91+
});
92+
document.querySelector('header').appendChild(xrButton.domElement);
93+
94+
if (navigator.xr) {
95+
navigator.xr.isSessionSupported('immersive-vr').then((supported) => {
96+
xrButton.enabled = supported;
97+
});
98+
}
99+
}
100+
101+
function onRequestSession() {
102+
useLayer = useLayerCheckbox.checked;
103+
return navigator.xr.requestSession('immersive-vr', {
104+
requiredFeatures: useLayer?['layers']:[],
105+
}).then(onSessionStarted);
106+
}
107+
108+
function onSessionStarted(session) {
109+
xrButton.setSession(session);
110+
111+
session.addEventListener('end', onSessionEnded);
112+
113+
gl = createWebGLContext({
114+
xrCompatible: true, webgl2: true
115+
});
116+
117+
renderer = new Renderer(gl);
118+
119+
scene.setRenderer(renderer);
120+
121+
if (useLayer) {
122+
xrFramebuffer = gl.createFramebuffer();
123+
xrGLFactory = new XRWebGLBinding(session, gl);
124+
} else {
125+
let gllayer = new XRWebGLLayer(session, gl);
126+
gllayer.fixedFoveation = 1.0;
127+
session.updateRenderState({ baseLayer: gllayer });
128+
}
129+
130+
session.requestReferenceSpace('local').then((refSpace) => {
131+
xrRefSpace = refSpace;
132+
133+
if (useLayer) {
134+
proj_layer = xrGLFactory.createProjectionLayer({ stencil: false });
135+
proj_layer.fixedFoveation = 1;
136+
session.updateRenderState({ layers: [proj_layer] });
137+
}
138+
session.requestAnimationFrame(onXRFrame);
139+
});
140+
141+
session.onselectstart = () => {
142+
count = false;
143+
}
144+
session.onselectend = () => {
145+
count = true;
146+
}
147+
}
148+
149+
function onEndSession(session) {
150+
session.end();
151+
}
152+
153+
function onSessionEnded(event) {
154+
xrButton.setSession(null);
155+
156+
renderer = null;
157+
}
158+
159+
function onXRFrame(t, frame) {
160+
let session = frame.session;
161+
162+
scene.startFrame();
163+
scene.updateInputSources(frame, xrRefSpace);
164+
165+
session.requestAnimationFrame(onXRFrame);
166+
167+
let pose = frame.getViewerPose(xrRefSpace);
168+
169+
if (pose) {
170+
if (useLayer) {
171+
let views = [];
172+
for (let view of pose.views) {
173+
view.requestViewportScale(scale);
174+
let subImage = xrGLFactory.getViewSubImage(proj_layer, view);
175+
subImage.framebuffer = xrFramebuffer;
176+
let viewport = subImage.viewport;
177+
gl.bindFramebuffer(gl.FRAMEBUFFER, xrFramebuffer);
178+
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, subImage.colorTexture, 0);
179+
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, subImage.depthStencilTexture, 0);
180+
181+
views.push(new WebXRView(view, subImage, viewport));
182+
}
183+
scene.drawViewArray(views);
184+
} else {
185+
let glLayer = session.renderState.baseLayer;
186+
187+
gl.bindFramebuffer(gl.FRAMEBUFFER, glLayer.framebuffer);
188+
189+
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
190+
191+
for (let view of pose.views) {
192+
view.requestViewportScale(scale);
193+
194+
let viewport = glLayer.getViewport(view);
195+
gl.viewport(viewport.x, viewport.y,
196+
viewport.width, viewport.height);
197+
198+
scene.draw(view.projectionMatrix, view.transform);
199+
}
200+
}
201+
202+
if (count)
203+
scale += scale_down ? -0.004 : 0.004;
204+
205+
if (scale >= 1) {
206+
scale = 1;
207+
scale_down = true;
208+
} else if (scale <= 0.125) {
209+
scale = 0.125;q
210+
scale_down = false;
211+
}
212+
}
213+
214+
scene.endFrame();
215+
}
216+
217+
initXR();
218+
</script>
219+
</body>
220+
</html>

0 commit comments

Comments
 (0)