mstreffo
mstreffo

Reputation: 805

three.js failing to use pre-created canvas

I want to use a pre-created canvas with three.js. From the tutorials and other posts I've read, this should work:

const canvas = document.createElement('canvas');
const ctx = canvas.getContext("2d");    // <--- This causes the error below!
const renderer = new THREE.WebGLRenderer( { canvas: canvas } );

However, in my browser console (Safari v14 and Chrome v86), I get the following error:

THREE.WebGLRenderer: Error creating WebGL context.

I've also tried adding

<canvas id='myCanvas'></canvas>

and using:

const canvas = document.getElementById("myCanvas");
const ctx = canvas.getContext("2d");    // <--- This causes the same error!
const renderer = new THREE.WebGLRenderer({
   canvas: canvas,
});

and get the same issues.

I've also tried adding:

window.onload = function() {
  ...
};

to ensure the DOM has loaded, etc.

If I remove the getContext("2d") lines then it works?

I'm using three.js version 0.120.

Why does this cause an issue?

Upvotes: 2

Views: 1825

Answers (1)

TheJim01
TheJim01

Reputation: 8876

Three.js only throws this error in one place, and luckily it's doing something very simple: Getting a context from a canvas. It uses HTMLCanvasElement.getContext to do this, and only throws the error if the result is null.

HTMLCanvasElement.getContext will only allow you to request one context type. You can request the same type (or compatible, in the case of webgl and webgl2) again, but it will return the original context created on the canvas. After the first request establishes the in-use context, subsequent requests for incompatible types will return null.

let ctx_2d_1 = mycanvas.getContext( '2d' )
let ctx_2d_2 = mycanvas.getContext( '2d' )
console.log( ctx_2d_1 === ctx_2d_2 ) // true
let ctx_2d = mycanvas.getContext( '2d' )
let ctx_webgl = mycanvas.getContext( 'webgl' )

console.log( ctx_2d ) // CanvasRenderingContext2D
console.log( ctx_webgl  ) // null
let ctx_webgl = mycanvas.getContext( 'webgl' )
let ctx_2d = mycanvas.getContext( '2d' )

console.log( ctx_webgl  ) // WebGLRenderingContext
console.log( ctx_2d ) // null

Because you are creating the 2d context before calling the WebGLRenderer constructor, the constructor can't get a valid WebGLRenderingContext from the canvas. Instead, it gets a null, and so it throws the error.

If you want to draw 2D on top of 3D (or vice versa), you will either need to layer canvases of different contexts, or use a plane to render the 2D information as a texture within the WebGL (three.js) context.

Upvotes: 5

Related Questions