I was trying to add some normal canvas options like rectfill and filter to my main threejs canvas
var canvas = document.getElementById(“Main_Canvas”); //threejs canvas
var ctx = canvas.getContext(‘2d’);
ctx.fillStyle=“rgba(255,0,0,0.5)”;
I do this in my main render loop
but I get this error
TypeError: Cannot set properties of null (setting ‘fillStyle’)
Any ideas as to how to pull this off? Seems if there is a way it might be a way to add postprocessing with canvas filters, just not sure if this is even possible.
Thanks
Jeff (aka MrBodean)
Whenever a context is created on a canvas, you can only get the same context. For example, if you get a WebGL context, you cannot get a 2D context from the same canvas. This is fixed in the specification of the canvas elements.
Possible ways to mimic what you need:
- use CSS filters on the canvas – they work fine, you can do blur, saturate, grayscale the canvas
- render the Three.js scene, get an image and draw the image on another 2D canvas, which can use all canvas 2D features
3 Likes
The error you’re encountering suggests that the getContext('2d')
call is returning null, which means you can’t manipulate the canvas as a 2D context. This is because Three.js uses WebGL, which takes over the canvas entirely.
However, there are ways to achieve what you’re trying to do. Here are a few approaches:
- Use Three.js post-processing effects: Three.js has its own post-processing system that you can use to apply various effects. This is generally the preferred method when working with Three.js.
- Render to an offscreen canvas: You can render your Three.js scene to an offscreen canvas, then draw that onto a 2D canvas where you can apply additional effects.
- Use a shader: You can create a custom shader to apply effects directly in WebGL.
Here’s an example of how you might use the second approach:
// Your existing Three.js setup
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Create an offscreen canvas for Three.js rendering
const offscreenCanvas = document.createElement('canvas');
offscreenCanvas.width = window.innerWidth;
offscreenCanvas.height = window.innerHeight;
const offscreenRenderer = new THREE.WebGLRenderer({ canvas: offscreenCanvas });
// Create a 2D canvas for post-processing
const canvas2D = document.createElement('canvas');
canvas2D.width = window.innerWidth;
canvas2D.height = window.innerHeight;
document.body.appendChild(canvas2D);
const ctx2D = canvas2D.getContext('2d');
// Your Three.js scene setup goes here
// ...
function animate() {
requestAnimationFrame(animate);
// Render your Three.js scene to the offscreen canvas
offscreenRenderer.render(scene, camera);
// Draw the offscreen canvas onto the 2D canvas
ctx2D.drawImage(offscreenCanvas, 0, 0);
// Apply 2D canvas effects
ctx2D.fillStyle = "rgba(255,0,0,0.5)";
ctx2D.fillRect(0, 0, canvas2D.width, canvas2D.height);
// Apply canvas filters if desired
ctx2D.filter = 'blur(5px)';
ctx2D.drawImage(canvas2D, 0, 0);
ctx2D.filter = 'none'; // Reset the filter
}
animate();
This approach allows you to render your Three.js scene and then apply 2D canvas operations on top of it. However, be aware that this method can be performance-intensive, especially for complex scenes or effects.
For better performance and more advanced effects, I’d recommend looking into Three.js’s built-in post-processing system. It provides a wide range of effects that can be applied efficiently within the WebGL context.
I’m not sure this can be helpful to you.
Thanks.
Thanks guys, will give those a shot