Question

I'm using Three.js to draw 3D models onto a webgl canvas renderer above simple DOM elements, and I need to do collision detection between them. My currently working method is to use renderer.domElement.toDataURL(), load this as an imageData object then draw this onto a separate 2D canvas context, then pull the getImageData() pixel array and iterate through using this awesome pixel collision function.

This is incredibly slow, and pulls my frame rate down to a nearly unplayable ~5-8 FPS. Without running this hit detection I get about 40-50 FPS.

My best guess is that the slowdown is the incredibly unwieldy toDataURL()->load image->drawImage()->getImageData().

My question becomes: Is there a better way to access the flattened 2D pixel data available on the WebGL canvas? or perhaps a better method of extrapolating my 3D object coordinates without parallax? Honestly, any way to get some kind of collision detection faster than I'm currently doing it will be immensely useful.

EDIT: the WebGL context.readPixels() works great for me, and is super fast compared to my previous kludge. Though it should be noted that the data array is mirrored top-to-bottom as compared with a regular image pixel data array. I simply flipped my collision routine Y value check and got this to work, though others may get tripped up in trickier ways. Good luck!

Was it helpful?

Solution

You can use gl.readPixels:

// Render your scene first then...
var left = 0;
var top = 0;
var width = canvas.width;
var height = canvas.height;
var pixelData = new Uint8Array(width * height * 4);
gl.readPixels(left, top, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixelData);

pixelData now contains the scene's pixel data as unsigned bytes (0-255) laid out as [R, G, B, A, R, G, B, A...] Should be the same data as getImageData but at a much lower cost.

[EDIT:]

I forgot to mention that if you're going to be doing this, you'll want to create your WebGL context with the preserveDrawingBuffer option, like so:

var gl = canvas.getContext("experimental-webgl", {preserveDrawingBuffer: true});

This prevents WebGL's inner workings from clearing the buffer before you get to it (which would result in you reading a whole lot of empty pixels). Enabling this option has the potential to slow down your rendering, but it should still be loads faster than 5-8 FPS! :)

OTHER TIPS

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



 var gl = renderer.getContext()
 var buf = new Uint8Array(200 * 200 * 4);
 gl.readPixels(0, 0, 200, 200, gl.RGBA, gl.UNSIGNED_BYTE, buf);


console.log(buf);

this works fine.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top