19

Basically, I want the setup where I could go to preserveDrawingBuffer=true, render the scene once, grab the screenshot, and go back. However, this poses two problems:

  • there is no method in renderer to dispose all the buffers,
  • canvas goes black if I do

    renderer = new THREE.WebGLRenderer({canvas:renderer.domElement,preserveDrawingBuffer:true});

How do I do this properly?

EDIT: I did not find the way to toggle this, so I had to clone scene and create second renderer instead to make the screenshot. See https://github.com/mrdoob/three.js/issues/189

makc
  • 1,050
  • 1
  • 9
  • 20
  • why would you want to toggle it? – gaitat Jun 03 '15 at 21:20
  • As gaitat asked, why do you need to toggle it? You don't need preserveDrawingBuffer=true to grab screenshots. – gman Jun 04 '15 at 04:18
  • preserveDrawingBuffer=true causes rendering bugs on some machines, preserveDrawingBuffer=false makes screenshots all black – makc Jun 04 '15 at 07:17
  • 1
    I believe @gman is correct. With `preserveDrawingBuffer` set to `false`, try re-rendering immediately prior, like so: `renderer.render( scene, camera ); var dataUrl = renderer.domElement.toDataURL( "image/jpeg" );` – WestLangley Jun 04 '15 at 14:07

2 Answers2

42

You don't need preserveDrawingBuffer: true to take a screenshot. What you need is to take the screenshot immediately after rendering. A screenshot is guaranteed to work as long as you take it after rendering but before exiting the current event.

So for example this will always work

renderer.render( scene, camera );
var screenshot = renderer.domElement.toDataURL();

Whereas this will only work randomly if you're luck

someElement.addEventListener('click', function() {
   // this is not immediately after rendering. you have no
   // idea when it is relative to rendering.
   var screenshot = renderer.domElement.toDataURL();
});

Most of the THREE.js examples have a render function if you need to take a screenshot when the user requests one you could do this

someElement.addEventListener('click', function() {
   render();
   var screenshot = renderer.domElement.toDataURL();
});

Or you could do this

var takeScreenshot;

function render() {
   ...
   if (takeScreenshot) {
     takeScreenshot = false;
     var screenshot = renderer.domElement.toDataURL();
   }
}

someElement.addEventListener('click', function() {
   takeScreenshot = true;
});

Or any number of other ways to just make sure you take the screenshot immediately after rendering.

antont
  • 2,676
  • 1
  • 19
  • 21
gman
  • 100,619
  • 31
  • 269
  • 393
  • 2
    Do you have a source for "guaranteed to work as long as you take it after rendering but before exiting the current event"? I'm curious if that's actually guaranteed by the WebGL specification. – John Apr 03 '18 at 16:16
  • Thank you for this answer!! I was not excited about having to set `preserveDrawingBuffer` to `true` so this was a great solution! Even works with post-processing! – Dave Teply Sep 23 '22 at 12:45
1

I have also encountered this problem.my screenshot is blank in threejs webgl canvas, because not set renderer = new THREE.WebGLRenderer({canvas:renderer.domElement,preserveDrawingBuffer:true});, Here is my way to toggle preserveDrawingBuffer.

let canvas = this.renderer.domElement;
    canvas.getContext('webgl' , {preserveDrawingBuffer: true});
this.render();
var data = {
  image: canvas.toDataURL(),
};
canvas.getContext('webgl' , {preserveDrawingBuffer: false});
  • Which browser do change the CanvasContext's init options like that? If it really does work on one of your browsers, that's a bug. (Ps: tried on FF and Chrome on osX and it doesn't work.) – Kaiido Sep 16 '18 at 06:03
  • Yes,I'm wrong and you are right, the context only can first init, cannot change anymore, and in my situation, " this.render()" can solve my problem, and I will continue to investigate the solution. – finland Xie Sep 16 '18 at 07:42