manual/en/webxr-basics.html
Making a VR app in three.js is pretty simple. You basically just have to tell three.js you want to use WebXR. If you think about it a few things about WebXR should be clear. Which way the camera is pointing is supplied by the VR system itself since the user turns their head to choose a direction to look. Similarly the field of view and aspect will be supplied by the VR system since each system has a different field of view and display aspect.
Let's take an example from the article on making a responsive webpage and make it support VR.
Before we get started you're going to need a VR capable device like an Android smartphone, Google Daydream, Oculus Go, Oculus Rift, Vive, Samsung Gear VR., an iPhone with a WebXR browser.
Next, if you are running locally you need to run a simple web server like is covered in the article on setting up.
If the device you are using to view VR is not the same computer you're running on you need to serve your webpage via https or else the browser will not allow using the WebXR API. The server mentioned in the article on setting up called Servez has an option to use https. Check it and start the server.
The note the URLs. You need the one that is your computer's local ipaddress. It will usually start with 192, 172 or 10. Type that full address, including the https:// part into your VR device's browser. Note: Your computer and your VR device need to be on the same local network or WiFi and you probably need to be on a home network. note: Many cafes are setup to disallow this kind of machine to machine connection.
You'll be greeted with an error something like the one below. Click "advanced" and then click proceed.
Now you can run your examples.
If you're really going to do WebXR development another thing you should learn about is remote debugging so that you can see console warnings, errors, and of course actually debug your code.
If you just want to see the code work below you can just run the code from this site.
The first thing we need to do is include the VR support after including three.js
import * as THREE from 'three';
+import {VRButton} from 'three/addons/webxr/VRButton.js';
Then we need to enable three.js's WebXR support and add its VR button to our page
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({antialias: true, canvas});
+ renderer.xr.enabled = true;
+ document.body.appendChild(VRButton.createButton(renderer));
We need to let three.js run our render loop. Until now we have used a requestAnimationFrame loop but to support VR we need to let three.js handle our render loop for us. We can do that by calling WebGLRenderer.setAnimationLoop and passing a function to call for the loop.
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
cubes.forEach((cube, ndx) => {
const speed = 1 + ndx * .1;
const rot = time * speed;
cube.rotation.x = rot;
cube.rotation.y = rot;
});
renderer.render(scene, camera);
- requestAnimationFrame(render);
}
-requestAnimationFrame(render);
+renderer.setAnimationLoop(render);
There is one more detail. We should probably set a camera height that's kind of average for a standing user.
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
+camera.position.set(0, 1.6, 0);
and move the cubes up to be in front of the camera
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
cube.position.x = x;
+cube.position.y = 1.6;
+cube.position.z = -2;
We set them to z = -2 since the camera will now be at z = 0 and camera defaults to looking down the -z axis.
This brings up an extremely important point. Units in VR are in meters. In other words One Unit = One Meter. This means the camera is 1.6 meters above 0. The cube's centers are 2 meters in front of the camera. Each cube is 1x1x1 meter large. This is important because VR needs to adjust things to the user in the real world. That means we need the units used in three.js to match the user's own movements.
And with that we should get 3 spinning cubes in front of the camera with a button to enter VR.
click here to open in a separate window
I find that VR works better if we have something surrounding the camera like room for reference so let's add a simple grid cubemap like we covered in the article on backgrounds. We'll just use the same grid texture for each side of the cube which will give as a grid room.
const scene = new THREE.Scene();
+{
+ const loader = new THREE.CubeTextureLoader();
+ const texture = loader.load([
+ 'resources/images/grid-1024.png',
+ 'resources/images/grid-1024.png',
+ 'resources/images/grid-1024.png',
+ 'resources/images/grid-1024.png',
+ 'resources/images/grid-1024.png',
+ 'resources/images/grid-1024.png',
+ ]);
+ scene.background = texture;
+}
That's better.
click here to open in a separate window
Note: To actually see VR you will need a WebXR compatible device. I believe most Android Phones can support WebXR using Chrome or Firefox. For iOS you might be able to use this WebXR App though in general WebXR support on iOS is unsupported as of May 2019.
To use WebXR on Android or iPhone you'll need a VR Headset for phones. You can get them for anywhere from $5 for one made of cardboard to $100. Unfortunately I don't know which ones to recommend. I've purchased 6 of them over the years and they are all of varying quality. I've never paid more than about $25.
Just to mention some of the issues
Do they fit your phone
Can they focus for your face
Are they too reflective
Are the comfortable on your face.
Can they support your glasses.
I really can't make any recommendations unfortunately. Google has some cheap recommendations made from cardboard some of them as low as $5 so maybe start there and if you enjoy it then consider upgrading. $5 is like the price of 1 coffee so seriously, give it try!
There are also 3 basic types of devices.
3 degrees of freedom (3dof), no input device
3 degrees of freedom (3dof) with 1 input device (3dof)
6 degrees of freedom (6dof) with input devices (6dof)
With all that covered I don't for sure know which devices will work with WebXR. I'm 99% sure that most Android phones will work when running Chrome. You may need to turn on WebXR support in about:flags. I also know Google Daydream will also work and similarly you need to enable WebXR support in about:flags. Oculus Rift, Vive, and Vive Pro will work via Chrome or Firefox. I'm less sure about Oculus Go and Oculus Quest as both of them use custom OSes but according to the internet they both appear to work.
Okay, after that long detour about VR Devices and WebXR there's some things to cover
If three.js adds some support to do both I'll try to update this article. Until then you might need 2 versions of your site OR pass in a flag in the URL, something like
https://mysite.com/mycooldemo?allowvr=true
Then we could add some links in to switch modes
<body>
<canvas id="c"></canvas>
+ <div class="mode">
+ <a href="?allowvr=true" id="vr">Allow VR</a>
+ <a href="?" id="nonvr">Use Non-VR Mode</a>
+ </div>
</body>
and some CSS to position them
body {
margin: 0;
}
#c {
width: 100%;
height: 100%;
display: block;
}
+.mode {
+ position: absolute;
+ right: 1em;
+ top: 1em;
+}
in your code you could use that parameter like this
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({antialias: true, canvas});
- renderer.xr.enabled = true;
- document.body.appendChild(VRButton.createButton(renderer));
const fov = 75;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 5;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(0, 1.6, 0);
+ const params = (new URL(document.location)).searchParams;
+ const allowvr = params.get('allowvr') === 'true';
+ if (allowvr) {
+ renderer.xr.enabled = true;
+ document.body.appendChild(VRButton.createButton(renderer));
+ document.querySelector('#vr').style.display = 'none';
+ } else {
+ // no VR, add some controls
+ const controls = new OrbitControls(camera, canvas);
+ controls.target.set(0, 1.6, -2);
+ controls.update();
+ document.querySelector('#nonvr').style.display = 'none';
+ }
Whether that's good or bad I don't know. I have a feeling the differences between what's needed for VR and what's needed for non-VR are often very different so for all but the most simple things maybe 2 separate pages are better? You'll have to decide.
Note for various reasons this will not work in the live editor on this site so if you want to check it out click here. It should start in non-VR mode and you can use the mouse or fingers to move the camera. Clicking "Allow VR" should switch to allow VR mode and you should be able to click "Enter VR" if you're on a VR device.
As you can see getting started in VR is pretty easy but actually making something shippable in VR will require lots of decision making and design.
This was a pretty brief intro into VR with three.js. We'll cover some of the input methods in future articles.