2

I'm trying to render a WebGL image to a canvas using the Three.js library and then use the toDataURL() method on that canvas after the rendering. What I get as I examine the string from toDataURL is just a tiny sphere that is the light source in my scene and the 3D models that are rendered don't appear. The code is mainly taken from one of the examples on the Three.js site. Is there a way to use toDataURL after the whole rendering actually finished?

    <script type="text/javascript">
            var canvas = document.getElementById("drawing");
            var hash;

            var SCREEN_WIDTH = canvas.width;
            var SCREEN_HEIGHT = canvas.height;

            var container, stats;

            var camera, scene, canvasRenderer, webglRenderer;

            var loader;

            var mesh, zmesh, lightMesh;

            var directionalLight, pointLight;

            var render_canvas = 1, render_gl = 1;
            var has_gl = 0;

            render_canvas = !has_gl;

            function addMesh(geometry, scale, x, y, z, rx, ry, rz, material) {
                    mesh = new THREE.Mesh(geometry, material);

                    mesh.scale.set(scale, scale, scale);
                    mesh.position.set(x, y, z);
                    mesh.rotation.set(rx, ry, rz);

                    scene.add(mesh);
            }

            function init() {
                    camera = new THREE.PerspectiveCamera(50, SCREEN_WIDTH
                                    / SCREEN_HEIGHT, 1, 100000);
                    camera.position.z = 1500;

                    scene = new THREE.Scene();

                    // LIGHTS

                    var ambient = new THREE.AmbientLight(0x101010);
                    scene.add(ambient);

                    directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
                    directionalLight.position.set(1, 1, 2).normalize();
                    scene.add(directionalLight);

                    pointLight = new THREE.PointLight(0xffaa00);
                    pointLight.position.set(0, 0, 0);
                    scene.add(pointLight);

                    // light representation

                    sphere = new THREE.SphereGeometry(100, 16, 8, 1);
                    lightMesh = new THREE.Mesh(sphere, new THREE.MeshBasicMaterial({
                            color : 0xffaa00
                    }));
                    lightMesh.scale.set(0.05, 0.05, 0.05);
                    lightMesh.position = pointLight.position;
                    scene.add(lightMesh);

                    if (render_gl) {
                            try {
                                    webglRenderer = new THREE.WebGLRenderer({
                                            canvas : canvas,
                                            antialias : true,
                                            alpha : true
                                    });
                                    webglRenderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
                                    has_gl = 1;

                            } catch (e) {
                                    alert("browser Doesn't support webGL");
                                    return;
                            }
                    }

                    loader = new THREE.BinaryLoader(true);

                    loader.load('/static/lucy/Lucy100k_bin.js', function(geometry,
                                    materials) {

                            addMesh(geometry, 0.75, 900, 0, 0, 0, 0, 0,
                                            new THREE.MeshPhongMaterial({
                                                    ambient : 0x030303,
                                                    color : 0x030303,
                                                    specular : 0x990000,
                                                    shininess : 30
                                            }));
                            addMesh(geometry, 0.75, 300, 0, 0, 0, 0, 0,
                                            new THREE.MeshFaceMaterial(materials));
                            addMesh(geometry, 0.75, -300, 0, 0, 0, 0, 0,
                                            new THREE.MeshPhongMaterial({
                                                    ambient : 0x030303,
                                                    color : 0x111111,
                                                    specular : 0xffaa00,
                                                    shininess : 10
                                            }));
                            addMesh(geometry, 0.75, -900, 0, 0, 0, 0, 0,
                                            new THREE.MeshPhongMaterial({
                                                    ambient : 0x030303,
                                                    color : 0x555555,
                                                    specular : 0x666666,
                                                    shininess : 10
                                            }));

                    });

            }

            function animate() {
                    requestAnimationFrame(animate);
                    webglRenderer.render(scene, camera);
            }

            init();
            animate();

            var dataurl = canvas.toDataURL();
    </script>
izikgo
  • 479
  • 7
  • 12

3 Answers3

3

hmmm, well I thought that placing toDataURL() after renderer.render(scene,cam); works fine, but you can set preserverDrawingBuffer: true in the renderer parameters and it works flawlessly although preserverDrawingBuffer cann have a negative impact on resources or performance. See here: http://learningthreejs.com/blog/2011/09/03/screenshot-in-javascript/

GuyGood
  • 1,377
  • 1
  • 9
  • 12
  • still not working :/... I just get the sphere and nothing else – izikgo Sep 15 '13 at 16:15
  • my answer is correct. it is just that your meshes weren't loaded to begin with. Otherwise, the correct answer to use dataURL is just to set preserverDrawingbuffer or put it directly after rendering takes places and before a new frame starts. This could be accomplished with a simple button but of course not "on start" as in your example ;) – GuyGood Sep 16 '13 at 10:02
0

Ok I got it. I needed to add the "preserveDrawingBuffer : true" and to put my screenshot code at the end of the callback function of the loader.

izikgo
  • 479
  • 7
  • 12
  • Yep, the meshes load asynchronously and pop into view when they're done loading, so your first few frames won't have the meshes present. Moving the screenshot code to the end of the loader callback will wait until that's not a problem any more (although you may want to render the scene one more time first as otherwise you may take the shot before the loaded meshes paint). – IceCreamYou Sep 16 '13 at 08:07
0

create WebGL context with the preserveDrawingBuffer option.

var gl = canvas.getContext("experimental-webgl", {preserveDrawingBuffer: true});
var pixelData = new Uint8Array(width * height * 4);
gl.readPixels(left, top, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixelData);
console.log(pixelData);

see this question

Community
  • 1
  • 1
cuixiping
  • 24,167
  • 8
  • 82
  • 93