Three.js - 如何检查对象是否在球体后面(不可见)

时间:2015-03-28 11:51:47

标签: javascript three.js

我在表面上有一个带有对象(引脚)的球体(球体),DOM元素(标签)是从引脚位置到2d世界的计算结果。

我的问题是,当引脚位于地球后面时(使用鼠标拖动或动画),我需要隐藏DOM中的标签,以便在没有引脚的情况下看不到文本标签。

我的逻辑是,如果我能得到3D世界中的引脚告诉我它是否在地球后面,那么我可以隐藏与该引脚相关的标签。

完整代码

Codepen

我一起研究的功能:

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) {
            // ?
        }

    }
}

我已经通过很多帖子进行了研究,但无法真正得到所需的结果:

我已经通过mouse XY position as a ray得到了它的工作,但是无法真正获得一个可以为所有引脚进行持续渲染的工作解决方案。

2 个答案:

答案 0 :(得分:5)

您想知道相机可以看到球体表面上的哪些点。

想象一下相机中与球体相切的线条。设L为从摄像机到切点的直线长度。

相机只能看到球体上比L更接近相机的点。

L的公式为L = sqrt( D^2 - R^2 ),其中D是从相机到球体中心的距离,R是球体半径。

答案 1 :(得分:1)

WestLangley 的代码表单解决方案。如果你觉得他的答案最好,请给他接受的答案。

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";
        }
    }
}

奇怪的是,它仍然容易受到相机声像错误的影响。非常奇怪,但它仍然比我的Projection-to-LOOKAT解决方案更好。

MY OLD ANSWER:

我会假设它是这样的,但这似乎没有按预期工作。

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

我接近这个解决方案,但它仍然不完美。下面的代码是它找到沿着LOOKAT方向的距离到一个引脚(cameraToPinProjection)的距离,并将它与沿着LOOKAT方向到地球的距离(cameraToEarthProjection)进行比较。 如果cameraToPinProjection&gt; cameraToEarthProjection它意味着引脚沿着LOOKAT方向位于地球中心的后面(然后我隐藏了引脚)。

你会发现有一个“0.8”因子我将cameraToEarth投影乘以。这是为了使它略短。试验它。

它并不完美,因为当你旋转地球时,你会注意到有时标签不会按照你喜欢的方式行事,我不知道如何解决。

我希望这会有所帮助。

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";
        }

    }
}