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

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

本文关键字:对象 js- 何检查 Three 检查 是否      更新时间:2023-09-26

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

我的问题是,当引脚位于地球仪后面时(通过鼠标拖动或动画),我需要隐藏DOM中的标签,这样没有引脚就看不到文本标签。

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

用整个代码打开代码。

我一起研究的功能:

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

我已经研究了很多帖子,但不能真正得到所需的结果:

  • ThreeJS:如何检测对象是否呈现/可见
  • Three.js-如何检查对象是否对相机可见
  • http://soledadpenades.com/articles/three-js-tutorials/object-picking/

我已经通过鼠标XY位置将其作为光线来工作,但无法真正获得对所有引脚进行恒定渲染的工作解决方案。

您想知道球体表面上的哪些点对摄影机可见。

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

摄影机只能看到球体上比L更靠近摄影机的点。

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

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

奇怪的是,它仍然容易受到相机平移错误的影响。非常奇怪,但它仍然比我的投影到LOOKAT解决方案更好。

我以前的回答:

我本以为是这样的,但这似乎并没有像预期的那样奏效。

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

我很接近这个解决方案,但它仍然不完美。下面的代码的作用是找到相机沿LOOKAT方向到引脚的距离(cameraToPinProjection),并将其与沿LOOKET方向到地球的距离进行比较(cameraToEarthProjection)。如果cameraToPinProjection>cameraToEarthProjection,则表示引脚沿LOOKAT方向位于地球中心后面(然后我隐藏引脚)。

你会意识到,我把cameraToEarth投影乘以一个"0.8"因子。这是为了让它稍微短一点。试验一下。

这并不完美,因为当你绕着地球旋转时,你会注意到有时标签的行为并不像你想要的那样,我不知道如何修复。

我希望这能有所帮助。

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