Three js and webGL support

As of version r69 of Three.js the 2d canvas software renderer has been removed from the core of threejs itself, and moved to the examples folder. It is still possible to use it of course it just needs to be added as an additional asset for a project on top of just three js by itself. It would seem that the motivation behind doing so was because support for webGL is now pretty good in general when it comes to modern web browsers which mode people who visit my website do in fact use.

For the most part these days there is no need to bother with the 2d canvas powered software renderer as the built in webgl renderer will work just fine on most clients, but if for some reason you do want to add more robust support for older clients that do not have great web gl support than the software renderer will have to be added in, and feature testing for web gl will need to be preformed. I can not say that I bother with this myself, but never the less there is the question of that small minority of people using outdated browsers and having code not break. So then in this post I thought I would touch base on this sort of topic for those that might be interested.

WebGL in three.js and what to know before hand

This is a post on feature testing for web gl, and using the software renderer in three js in the event that there is no webGL support at all. This is a not a getting started post with three.js, webGL, or javaScript in general. So then I assume that you have at least some background with three.js and javaScript.

More on the webGL renderer

In this post I am mostly writing about feature testing for webGL and then doing something different in the event that webGL is not supported. However I am not really getting into the webGL renderer in detail as I have wrote a post on the webGL renderer in detail a while back and there is therefore no need to do so here.

Source Code is also up on Github

The source code examples that I am writing about here can also be found up on Github.

Version numbers matter

When writing this post for the first time I was using revision 104 of three.js, and the last time I cam around to do some editing I was using r146 of threejs. So a lot has changed between those two revision numbers, far more that I can write about when it comes to this single little blog post on the subject. However I will say that as of revision r141+ it is now assumed that a client has webgl2 support, which will case erros when working on a project on a client system that does not. So I find myself using the WebGl1Renderer now as my default go to renderer that is built into the threejs core.

On top of all of this I am also using some additional assets in the renderer’s folder of the js folder in the examples folder of the three.js repo. When it comes to rendering a three js scene with a renderer other than the built in webGL renderer additional assets must be used to provide that additional way of rendering. To further complicate matters there the options of what there are to work with in terms of software rendering will differ from one revision to the next. When it came to r104 the 2d canvas renderer was still an option when added as an additional external file which was called the software-renderer.js file. However in later revisions of threejs it would seem the best option would be to use the SVGRenderer as of r146+.

1 - The webgl-r0.js file

Here is my webgl.js file that just contains one method that is just a slightly modified version of what is also in the three.js repo. This method just simply returns true of the client supports webGL 1, and false if that is not the case with the main is web gl method. In addition to this I also have a method that will just return true or false in the event that a client supports webgl2 or not.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// webgl.js - r0 - plain javaScript webgl test tools based on the threejs JSM module found
// at : https://github.com/mrdoob/three.js/blob/r146/examples/jsm/capabilities/WebGL.js
const WebGL = {};
// can the client use WEBGL1
WebGL.isWebGL = function () {
try {
var canvas = document.createElement('canvas');
return !!(window['WebGLRenderingContext'] &&
(canvas.getContext('webgl') ||
canvas.getContext('experimental-webgl')));
} catch (e) {
return false;
}
};
// can the client use WEBGL2
WebGL.isWebGL2 = function () {
try {
const canvas = document.createElement( 'canvas' );
return !! ( window.WebGL2RenderingContext && canvas.getContext( 'webgl2' ) );
} catch ( e ) {
return false;
}
};

If you want to use the official webgl.js file here is a link to that in the repo. The file adds some additional functionality but for the most part it is just to feature test for webGL. Feature testing for just webGL by itself is not always enough also, for example a client count support webGL by itself, but that does not mean that all the features of the three js webGL renderer will work as expected. I can confirm that this is sometimes that case with some clients.

1.1 - Using the SVGRenderer (threejs r146) demo

As of revision r146 of threejs it would seem that the best alternative option to that of the web gl renderer would be the SVG Renderer. It would seem that the 2d canvas renderer is not longer being supported at all, so that might only be an option when using older revisions of threejs, or if for some reason one can get that working on later revisions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//-------- ----------
// SCENE, CAMERA
//-------- ----------
const scene = new THREE.Scene();
scene.background = new THREE.Color(0,0,0);
const container = (document.getElementById('demo') || document.body);
const camera = new THREE.PerspectiveCamera(50, 32 / 24, 0.1, 1000);
camera.position.set(2, 2, 2);
camera.lookAt(0, 0, 0);
//-------- ----------
// MESH
//-------- ----------
const mesh = new THREE.Mesh(
new THREE.SphereGeometry(1, 30, 30),
new THREE.MeshNormalMaterial());
scene.add(mesh);
//-------- ----------
// IS WEBGL TEST
//-------- ----------
console.log('using threejs r' + THREE.REVISION);
if (WebGL.isWebGL()) {
// if we have webGl so I will default to using webgl1, but might replace
// with the webgl 2 renderer if there
console.log('We have webgl.');
let renderer = new THREE.WebGL1Renderer();
if(WebGL.isWebGL2()){
console.log('We have webgl 2');
renderer = new THREE.WebGLRenderer();
}
renderer.setSize(640, 480, false);
container.appendChild(renderer.domElement);
renderer.render(scene, camera);
} else {
console.log('We do not have webgl, so I am using THREE.SVGRenderer');
const renderer = new THREE.SVGRenderer();
renderer.setSize(640, 480);
container.appendChild(renderer.domElement);
renderer.render(scene, camera);
}

1.2 - Software renderer (threejs r104) demo

Here I have the setup.js file that makes use of the webGL method in webgl.js, as well as the additional assets depending if webGl is supported or not.

In order to get this demo to work on top of using r104 of three.js the projector.js and softwareRenderer.js files will also need to be loaded in as well. Projector.js will need to be loaded after three.js and before softwareRenderer.js as that file depends on projector.js and projector.js depends on three.js

So in the html of this example I am linking to three.js like normal, but I am also linking to the additional assets in the github repo as well. I am also linking to some additional assets that feature test for webGL and render a scene differently depending on the state of webGL support in the client which are as follows.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<html>
<head>
<title>test_threejs demos</title>
</head>
<body>
<script src="/js/threejs/0.104.0/three.min.js" ></script>
<h1>Three.js webGL test</h1>
<div id="demo"></div>
<script src="/js/threejs/0.104.0/three.min.js"></script>
<script src="/js/threejs/0.104.0/projector.js"></script>
<script src="/js/threejs/0.104.0/software-renderer.js"></script>
<script src="/forpost/threejs-webgl/js/webgl-r0.js"></script>
<script src="/forpost/threejs-webgl/r0-2-software/setup.js"></script>
</body>
</html>

The demo involves creating a mesh with an array of materials the first of which is the Depth Material that works great in the Software Renderer and the second material is the standard material that is one of the several materials that will respond to a light source in three js.

The Material index property of a Face3 instance in three js is what can be used to set the material in this array of materials to use when skinning A Face3 instance in a geometry. So in the event that webGL is supported then the Standard Material will be used else the Depth material will be used as a fall back or sorts that will work okay with the Software Renderer.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//-------- ----------
// SCENE, CAMERA
//-------- ----------
const scene = new THREE.Scene();
scene.background = new THREE.Color(0,0,0);
const container = (document.getElementById('demo') || document.body);
const camera = new THREE.PerspectiveCamera(50, 32 / 24, 0.1, 1000);
camera.position.set(2, 2, 2);
camera.lookAt(0, 0, 0);
//-------- ----------
// MESH
//-------- ----------
const mesh = new THREE.Mesh(
new THREE.SphereGeometry(1, 30, 30),
new THREE.MeshNormalMaterial());
scene.add(mesh);
//-------- ----------
// IS WEBGL TEST
//-------- ----------
console.log('using threejs r' + THREE.REVISION);
if (WebGL.isWebGL()) {
// if we have webGl so I will default to using webgl1, but might replace
// with the webgl 2 renderer if there
console.log('We have webgl.');
let renderer = new THREE.WebGLRenderer();
renderer.setSize(640, 480, false);
container.appendChild(renderer.domElement);
renderer.render(scene, camera);
} else {
// in the event that webgl is not supported I can give an error message to the
// use, or in this case use some other option for rendering
console.log('We do not have webgl, so I am using THREE.SoftwareRenderer');
const renderer = new THREE.SoftwareRenderer();
renderer.setSize(640, 480);
container.appendChild(renderer.domElement);
renderer.render(scene, camera);
}

Conclusion

Even if a client does support webgl that does not mean that all the webgl features will work as expected. A simple check if webgl is there or not will result in a true response with a simple feature test for webgl alone, but things will still not render as expected.

Also when it comes to trying to get things work on both new and old platforms it is not always a question of using this late version of three.js, and then using the 2d canvas renderer with a later version of three.js as a later version of three.js might very well break on older platforms. Doing this sort of thing can prove to be very time consuming, and often it is just not worth the hassle.