Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to quickly find if a point is obscured in a complex scene?

I have a complex 3D scene that I need to display HTML elements on top of, based on a 3D coordinate. (I'm simply overlaying a div tag on top and positioning it with CSS.) However, I also need to partially hide it (e.g., making it transparent) when the 3D coordinate is obscured by a model (or phrased in another way, when it's not visible in the camera). These models may be have many hundreds of thousands of faces, and I need a way to find out if it's obscured that's fast enough to be run many times per second.

Currently, I am using Three.js's built-in raytracer, with the following code:

// pos   = vector with (normalized) x, y coordinates on canvas
// dir   = vector from camera to target point

const raycaster = new THREE.Raycaster();
const d = dir.length(); // distance to point
let intersects = false;
raycaster.setFromCamera(pos, camera);
const intersections = raycaster.intersectObject(modelObject, true);
if (intersections.length > 0 && intersections[0].distance < d)
    intersects = true;

// if ray intersects at a point closer than d, then the target point is obscured
// otherwise it is visible

However, this is very slow (frame rate drops from 50 fps to 8 fps) on these complex models. I've been looking for better ways to do this, but so far I haven't found any that work well in this case.

Are there any better, more effective ways of finding out if a point is visible or obscured by models in the scene?

like image 471
Frxstrem Avatar asked Apr 20 '16 13:04

Frxstrem


1 Answers

I am not aware of any really quick way, but you do have a few options. I don't know enough about three.js to tell you how to do it with that library, but speaking about WebGL in general...

If you can use WebGL 2.0, you can use occlusion queries. This boils down to

var query = gl.createQuery();
gl.beginQuery(gl.ANY_SAMPLES_PASSED, query);
// ... draw a small quad at the specified 3d position ...
gl.endQuery(gl.ANY_SAMPLES_PASSED);
// some time later, typically a few frames later (after a fraction of a second)
if (gl.getQueryParameter(query, gl.QUERY_RESULT_AVAILABLE))
{
     gl.getQueryParameter(query, gl.QUERY_RESULT);
}

Note though, that the result of the query is only available a few frames later.

If WebGl 2.0 is not an option, then you should probably draw the scene to a framebuffer, where you attach your own texture to use in place of the normal z-buffer. There is an extension to use proper depth textures (more details here), but where that is not possible you could always fall back to drawing your scene with a fragment shader that outputs the depth of each pixel.

You can then use gl.ReadPixels() on the depth texture. Again, be aware of the latency for the GPU->CPU transfer, that's always going to be significant.

Having said all that, depending on what your DOM objects look like, it could be far easier and quicker to render your DOM objects into a texture and draw that texture using a quad as part of your 3d scene.

like image 70
Gio Avatar answered Oct 04 '22 17:10

Gio