2
votes

I'm trying to determine whether a point in space is visible to the camera or hidden behind other objects in the scene. I'm doing this by casting a ray from the position of the camera to that point in space and testing if that ray is intersected by an set of intersectable objects.

My problem is no intersections occur until the camera position itself intersects one of the objects in the set of intersectable objects.

I've created a jsfiddle in which, if an intersection is detected, a line is drawn from the camera position to the position in space i'm testing for visibility. Currently I believe, the line is only draw at specific points where the camera position intersects the set of intersectable objects.

How do I get the intersections to be registered as they should be, without having to have the camera position intersect objects in the set of intersectable objects?

the code:

    var container;
    var camera, controls, scene, renderer;

    init();
    animate();

    function init() {

        camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000);
        camera.position.z = 1000;

        controls = new THREE.OrbitControls(camera);

        controls.rotateSpeed = 1.0;
        controls.zoomSpeed = 1.2;
        controls.panSpeed = 0.8;

        controls.noZoom = false;
        controls.noPan = false;

        controls.staticMoving = true;
        controls.dynamicDampingFactor = 0.3;

        controls.keys = [65, 83, 68];

        controls.addEventListener('change', render);

        // world

        scene = new THREE.Scene();

        var testObject_G = new THREE.CubeGeometry(100, 100, 5);
        var testObject_M = new THREE.MeshBasicMaterial({
            color: 0xBBBBBB
        });
        var testObject_Mesh = new THREE.Mesh(testObject_G, testObject_M);
        testObject_Mesh.position.x = -150;
        scene.add(testObject_Mesh);
        var testObject_Mesh2 = new THREE.Mesh(testObject_G, testObject_M);
        testObject_Mesh2.position.x = 0;
        scene.add(testObject_Mesh2);
        var testObject_Mesh3 = new THREE.Mesh(testObject_G, testObject_M);
        testObject_Mesh3.position.x = 150;
        scene.add(testObject_Mesh3);


        scene2 = new THREE.Object3D();

        // renderer

        renderer = new THREE.WebGLRenderer({
            antialias: true
        });
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.setClearColor(0xffffff, 1);
        container = document.getElementById('container');
        container.appendChild(renderer.domElement);



        //

        window.addEventListener('resize', onWindowResize, false);

    }

    function onWindowResize() {

        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();

        renderer.setSize(window.innerWidth, window.innerHeight);

        controls.handleResize();

        render();

    }

    function animate() {

        requestAnimationFrame(animate);
        controls.update();

    }

    function render() {

        renderer.render(scene, camera);
        castRays();

    }

    function castRays() {

        // rays

        var direction = new THREE.Vector3(0, 200, -200);

        var startPoint = camera.position.clone();

        var ray = new THREE.Raycaster(startPoint, direction);

        scene.updateMatrixWorld(); // required, since you haven't rendered yet

        var rayIntersects = ray.intersectObjects(scene.children, true);

        if (rayIntersects[0]) {
            console.log(rayIntersects[0]);

            var material = new THREE.LineBasicMaterial({
              color: 0x0000ff
            });
            var geometry = new THREE.Geometry();
            geometry.vertices.push(new THREE.Vector3(ray.ray.origin.x, ray.ray.origin.y, ray.ray.origin.z));
            geometry.vertices.push(new THREE.Vector3(ray.ray.direction.x, ray.ray.direction.y, ray.ray.direction.z));
            var line = new THREE.Line(geometry, material);
            scene2.add( line );

        }

        scene.add(scene2);

    }

Thank you

2

2 Answers

4
votes

For anyone currently seeing this thread, THREE.Projector has been replaced.

Three.js THREE.Projector has been moved to

The code below handles a 3D vector. If you go to the link above, the first commenter provided the code for a 2D vector.

var vector = new THREE.Vector3();
var raycaster = new THREE.Raycaster();
var dir = new THREE.Vector3();

...

if ( camera instanceof THREE.OrthographicCamera ) {

    vector.set( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1, - 1 ); // z = - 1 important!

    vector.unproject( camera );

    dir.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld );

    raycaster.set( vector, dir );

} else if ( camera instanceof THREE.PerspectiveCamera ) {

   vector.set( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1, 0.5 ); // z = 0.5 important!

   vector.unproject( camera );

   raycaster.set( camera.position, vector.sub( camera.position ).normalize());

}

var intersects = raycaster.intersectObjects( objects, recursiveFlag );`
2
votes

Your idea of casting a ray is good, however raycasting in three.js already does this :

 mouse.x = ( ( event.clientX - renderer.domElement.offsetLeft ) / renderer.domElement.width ) * 2 - 1;
 mouse.y = - ( ( event.clientY - renderer.domElement.offsetTop ) / renderer.domElement.height ) * 2 + 1;

this formula maps a pixel coordinate from Screen Space to a point in Normalized Device Coordinate ( NDC ) Space.

projector.unprojectVector( vector, camera );

maps a point from NDC Space to a point in World space

Raycaster then creates a ray from the camera position through that world point.

Here is your working Fiddle in which I changed the way of raycasting in your scene and this works, all you have to do now is creating a ray with the right coordinates that I provided you.

r.68