最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Three.js - How to check if object is behind a sphere (not visible) - Stack Overflow

programmeradmin5浏览0评论

I have a sphere (globe) with objects (pins) on the surface with DOM elements (labels) what are calculated from the pin position to 2d world.

My problem is that when the pins go behind the globe (with mouse dragging or animation) then I need to hide labels which are in DOM so that the text label isn’t visible without the pin.

My logic is that if I can get the pin which is in 3D world to tell me if it’s behind the globe then I can hide the label associated with the pin.

Codepen with whole the code.

The function that I have researched together:

function checkPinVisibility() {

    var startPoint = camera.position.clone();

    for (var i = 0; i < pins.length; i++) {

        var direction = pins[i].position.clone();
        var directionVector = direction.sub(startPoint);

        raycaster.set(startPoint, directionVector.clone().normalize());

        var intersects = raycaster.intersectObject(pins[i]);

        if (intersects.length > 0) {
            // ?
        }

    }
}

I have researched through many posts but can’t really get the result needed:

  • ThreeJS: How to detect if an object is rendered/visible
  • Three.js - How to check if an object is visible to the camera
  • /

I have gotten it work by mouse XY position as a ray, but can’t really get a working solution with constant rendering for all the pins.

I have a sphere (globe) with objects (pins) on the surface with DOM elements (labels) what are calculated from the pin position to 2d world.

My problem is that when the pins go behind the globe (with mouse dragging or animation) then I need to hide labels which are in DOM so that the text label isn’t visible without the pin.

My logic is that if I can get the pin which is in 3D world to tell me if it’s behind the globe then I can hide the label associated with the pin.

Codepen with whole the code.

The function that I have researched together:

function checkPinVisibility() {

    var startPoint = camera.position.clone();

    for (var i = 0; i < pins.length; i++) {

        var direction = pins[i].position.clone();
        var directionVector = direction.sub(startPoint);

        raycaster.set(startPoint, directionVector.clone().normalize());

        var intersects = raycaster.intersectObject(pins[i]);

        if (intersects.length > 0) {
            // ?
        }

    }
}

I have researched through many posts but can’t really get the result needed:

  • ThreeJS: How to detect if an object is rendered/visible
  • Three.js - How to check if an object is visible to the camera
  • http://soledadpenades./articles/three-js-tutorials/object-picking/

I have gotten it work by mouse XY position as a ray, but can’t really get a working solution with constant rendering for all the pins.

Share Improve this question edited May 23, 2017 at 12:32 CommunityBot 11 silver badge asked Mar 28, 2015 at 11:51 RaikRaik 3863 silver badges6 bronze badges 2
  • your pin elements must also be 3d elements. you can not mix 3d with 2d DOM elements like that. Use a THREE.Sprite for your labels. – gaitat Commented Mar 28, 2015 at 16:02
  • 1 There lies my problem that I have to use DOM elements for the text labels not sprites. My logic is that if I can get the pin which is in 3D world to tell me if it’s behind the globe then I can hide the label associated with the pin. – Raik Commented Mar 28, 2015 at 17:11
Add a ment  | 

2 Answers 2

Reset to default 8

You want to know which points on the surface of a sphere are visible to the camera.

Imagine a line from the camera that is tangent to the sphere. Let L be the length of the line from the camera to the tangent point.

The camera can only see points on the sphere that are closer to the camera than L.

The formula for L is L = sqrt( D^2 - R^2 ), where D is the distance from the camera to the sphere center, and R is the sphere radius.

WestLangley's solution in code form. Please give him the accepted answer if you feel his answer the best.

function checkPinVisibility() {
    var cameraToEarth = earth.position.clone().sub(camera.position);
    var L = Math.sqrt(Math.pow(cameraToEarth.length(), 2) - Math.pow(earthGeometry.parameters.radius, 2));

    for (var i = 0; i < pins.length; i++) { 

        var cameraToPin = pins[i].position.clone().sub(camera.position);

        if(cameraToPin.length() > L) { 
            pins[i].domlabel.style.visibility = "hidden";
        } else { 
            pins[i].domlabel.style.visibility = "visible";
        }
    }
}

Oddly enough it is still susceptible to that camera pan error. Very weird, but it's still better than my Projection-onto-LOOKAT solution.

MY OLD ANSWER:

I would have assumed its something like this, but this doesn't seem to work as expected.

  if (intersects.length > 0) {
       pins[i].domlabel.style.visibility = "visible";
   } else {
       pins[i].domlabel.style.visibility = "hidden";
   }

I got close with this solution, but its still not perfect. What the code below does is it finds the distance along the LOOKAT direction of the camera to a pin (cameraToPinProjection) and pares it with the distance along the LOOKAT direction to the earth (cameraToEarthProjection). If cameraToPinProjection > cameraToEarthProjection it means the pin is behind the centre of the earth along the LOOKAT direction (and then I hide the pin).

You will realise there's a "0.8" factor I multiply the cameraToEarth projection by. This is to make it slightly shorter. Experiment with it.

Its not perfect because as you rotate the Earth around you will notice that sometimes labels don't act the way you'd like them, I'm not sure how to fix.

I hope this helps.

function checkPinVisibility() {
    var LOOKAT = new THREE.Vector3( 0, 0, -1 );
    LOOKAT.applyQuaternion( camera.quaternion );

    var cameraToEarth = earth.position.clone().sub(camera.position);
    var angleToEarth = LOOKAT.angleTo(cameraToEarth);

    var cameraToEarthProjection = LOOKAT.clone().normalize().multiplyScalar(0.8 * cameraToEarth.length() * Math.cos(angleToEarth));

    var startPoint = camera.position.clone();

    for (var i = 0; i < pins.length; i++) {

        var cameraToPin = pins[i].position.clone().sub(camera.position);
        var angleToPin = LOOKAT.angleTo(cameraToPin);

        var cameraToPinProjection = LOOKAT.clone().normalize().multiplyScalar(cameraToPin.length() * Math.cos(angleToPin));

        if(cameraToPinProjection.length() > cameraToEarthProjection.length()) {
            pins[i].domlabel.style.visibility = "hidden";
        } else { 
            pins[i].domlabel.style.visibility = "visible";
        }

    }
}
发布评论

评论列表(0)

  1. 暂无评论