How do you save an image from a Three.js canvas?
Asked Answered
S

3

61

How do you save an image from a Three.js canvas?

I'm attempting to use Canvas2Image but it doesn't like to play with Threejs. Since the canvas isn't defined until it has a div to attach the canvas object to.

http://ajaxian.com/archives/canvas2image-save-out-your-canvas-data-to-images

Seminole answered 21/3, 2013 at 21:23 Comment(1)
I'm not sure you can. 2D canvas supports this, but I believe that a WebGL canvas does not.Empathic
E
95

Since the toDataURL is a method of canvas html element, that will work for 3d context too. But you have to take care of couple of things.

  1. Make sure when the 3D context is initialized you set preserveDrawingBuffer flag to true, like so:

    var context = canvas.getContext("experimental-webgl", {preserveDrawingBuffer: true});
    
  2. Then user canvas.toDataURL() to get the image

In threejs you would have to do the following when the renderer is instantiated:

new THREE.WebGLRenderer({
    preserveDrawingBuffer: true 
});

Also, keep in mind this can have performance implications. (Read: https://github.com/mrdoob/three.js/pull/421#issuecomment-1792008)

This is only for webgl renderer, in case of threejs canvasRenderer though, you can simply do renderer.domElement.toDataURL(); directly, no initialization parameter needed.

My webgl experiment: http://jsfiddle.net/TxcTr/3/ press 'p' to screenshot.

Props to gaitat, I just followed the link in his comment to get to this answer.

Echoism answered 22/3, 2013 at 5:51 Comment(1)
Dinesh, do you know if this would also work in nodejs too or not ?Ichinomiya
Z
40

I read the conversation posted by Dinesh (https://github.com/mrdoob/three.js/pull/421#issuecomment-1792008) and came up with a solution that won't slow down your application.

    function render() { 
        requestAnimationFrame(render);
        renderer.render(scene, camera);
        if(getImageData == true){
            imgData = renderer.domElement.toDataURL();
            getImageData = false;
        }
    } 

With this you can leave the preserveDrawingBuffer-Flag at false and still get the image from THREE.js. Simply set getImageData to true and call render() and you are good to go.

getImageData = true;
render();
console.debug(imgData);

Hope this helps people like me who need the high fps :)

Zared answered 19/8, 2014 at 18:55 Comment(7)
Basically it turns out that with threejs you need 2 calls one after another: renderer.render(scene, camera); renderer.domElement.toDataURL(); In many cases the simplest approach may be just to put them in some seperate function outside of the normal rendering loop.Sufficient
Use these settings to prevent having too much memory usage .toDataURL( 'image/jpeg', 1.0 ); otherwise the imgData contains a raw image. It saved me from 12MB to 400Kb of spacePeipeiffer
I'm trying this version, but unfortunately I'm getting a black image. Does the render function in requestAnimationFrame call the function it is located in, or is this a render function provided by three (so renderer.render)?Baliol
@DSz it's been 6 years but if I remember correctly the render function is constantly calling itself to redraw (like games do for example) so animated objects or camera movement would show up on screen. This loop will check if it needs to genereate a "screenshot" everytime. Calling render() one additional time shouldn't have any impact on this loop. But again it has been 6 years. Chances are high you're using a newer version of three.js where things might be different.Zared
Thanks for coming back and answering. I'll try exploring more how to solve it!Baliol
I'm also getting a black image with this method. @DSz did you get a way of doing this?Prent
You need first to add "new THREE.WebGLRenderer({ preserveDrawingBuffer: true });" after creating the renderer on your code and then it will workPrent
T
-2

use canvas to create the url, and then the same can be downloaded

function createImage(saveAsFileName) {

    var canvas = document.getElementById("canvas");

    var url = canvas.toDataURL();

    var link = document.createElement('a');

    link.setAttribute('href', url);
    link.setAttribute('target', '_blank');
    link.setAttribute('download', saveAsFileName);

    link.click();
}
Thecla answered 5/11, 2020 at 9:0 Comment(1)
On vanilla three.js this code actually only returns a black png imagePrent

© 2022 - 2024 — McMap. All rights reserved.