【问题标题】:Rotate camera to face object without messing up lat lon values旋转相机以面对对象而不会弄乱纬度值
【发布时间】:2015-01-12 03:46:17
【问题描述】:

我用来控制我的相机的代码通过移动一个矢量(camera.target)然后将相机设置为查看该矢量来工作。在这种情况下 camera.lookAt(camera.target);。 onDocumentMouseMove 函数计算经度和纬度,这有助于设置 camera.target x、y 和 z 值。下面是一个事情如何运作的例子。按下任意键将导致相机旋转到一个对象,但如果您下次通过拖动鼠标移动相机时这样做,相机将自动跳转到击键前的最后一个位置。这是因为为了让鼠标移动相机,它必须跟踪纬度和经度。所以我遇到的问题是在击键后计算新的纬度位置。我在想是否可以从 lon 和 lat 计算目标 x、y、z 值,而不是可以从 x、y、z 值反向计算 lat 和 lon。不幸的是,数学超出了我的能力范围。我为这个问题增加了赏金。任何帮助将不胜感激。 http://codepen.io/anon/pen/GgRXJz

        var spriteImg, material, geometry;
        var camera, scene, renderer;
        var keyboard = new THREEx.KeyboardState();
        var fov = 70,
        texture_placeholder,
        isUserInteracting = false,
        onMouseDownMouseX = 0, onMouseDownMouseY = 0,
        lon = 0, onMouseDownLon = 0,
        lat = 0, onMouseDownLat = 0,
        phi = 0, theta = 0;

        init();
        animate();

        function init() {

            var container;

            container = document.getElementById( 'container' );

            camera = new THREE.PerspectiveCamera( fov, window.innerWidth / window.innerHeight, 1, 1100 );
            camera.target = new THREE.Vector3( 0, 0, 0 );

            scene = new THREE.Scene();

            renderer = new THREE.WebGLRenderer();
            renderer.setClearColor(0xffffff, 1);
            renderer.setSize( window.innerWidth, window.innerHeight );
            container.appendChild( renderer.domElement );

            document.addEventListener( 'mousedown', onDocumentMouseDown, false );
            document.addEventListener( 'mousemove', onDocumentMouseMove, false );
            document.addEventListener( 'mouseup', onDocumentMouseUp, false );
            window.addEventListener( 'resize', onWindowResize, false );
            stats = new Stats();
            stats.domElement.style.position = 'absolute';
            stats.domElement.style.bottom = '0px';
            stats.domElement.style.left = '0px';
            stats.domElement.style.zIndex = 100;
            container.appendChild( stats.domElement );

            material = new THREE.MeshBasicMaterial( { color: 0x0125fd} );
            geometry = new THREE.PlaneGeometry(50, 50, 3, 3);
            blue1 = new THREE.Mesh(geometry, material);
            blue1.position.set(200,100,200);
            scene.add(blue1);
            blue1.lookAt( camera.position );

            blue2 = new THREE.Mesh(geometry, material);
            blue2.position.set(-200,-100,-200);
            blue2.lookAt( camera.position );
            scene.add(blue2);

        }

        function onWindowResize() {

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

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

        }

        function onDocumentMouseDown( event ) {

            isUserInteracting = true;

            onPointerDownPointerX = event.clientX;
            onPointerDownPointerY = event.clientY;

            onPointerDownLon = lon;
            onPointerDownLat = lat;

        }

        function onDocumentMouseMove( event ) {
            if ( isUserInteracting ) {
                lon = ( event.clientX - onPointerDownPointerX ) * 0.3 + onPointerDownLon;
                lat = ( onPointerDownPointerY - event.clientY ) * 0.3 + onPointerDownLat;
            }
        }

        function onDocumentMouseUp( event ) {
            isUserInteracting = false;
        }

        function animate() {
            requestAnimationFrame( animate );
            render();
            update();
        }   

        function update()
        {           
            stats.update();
        }

        function render() {

            lat = Math.max( - 85, Math.min( 85, lat ) );
            phi = THREE.Math.degToRad( 90 - lat );
            theta = THREE.Math.degToRad( lon );

            camera.target.x = 500 * Math.sin( phi ) * Math.cos( theta );
            camera.target.y = 500 * Math.cos( phi );
            camera.target.z = 500 * Math.sin( phi ) * Math.sin( theta );

            camera.lookAt( camera.target );

            renderer.render( scene, camera );
        }

【问题讨论】:

    标签: javascript 3d three.js


    【解决方案1】:

    要从 x,y,z 坐标计算纬度和经度,您需要反转您的方法。演示请见updated code-pen

    原文:

    lat = Math.max( - 85, Math.min( 85, lat ) );
    phi = THREE.Math.degToRad( 90 - lat );
    theta = THREE.Math.degToRad( lon );
    
    camera.target.x = 500 * Math.sin( phi ) * Math.cos( theta );
    camera.target.y = 500 * Math.cos( phi );
    camera.target.z = 500 * Math.sin( phi ) * Math.sin( theta );
    

    逆向:

    摘自original code-pen 的片段,在update() 方法内的document.body.onkeydown 动作事件侦听器上找到——在演示中使用以下代码修改了此方法

    //Since you are looking at a given shape on keypress, decided to inverse from that
    //shape's position.
    camera.lookAt( blue1.position );
    //Added the following four lines of code
    phi = Math.acos((blue1.position.y)/500);
    theta = Math.acos((blue1.position.x)/(500 * Math.sin(phi)))
    lon = THREE.Math.radToDeg(theta);
    lat = 90-THREE.Math.radToDeg(phi);
    

    代数中的细节

    鉴于我在学校里总是因为只显示解决方案而获得停靠点,以下是我如何从 x、y 和 z 逆向/求解纬度和经度

    xyz 代表您给定的观点(camera.target.x<object>.position.x 等)。

    phi = Φ, theta = Θ

    求解Θ

    1. x = 500 * sin (Φ) * cos(Θ)
    2. x/(500 * sin(Φ)) = 500 * sin (Φ) * cos(Θ) / (500 * sin(Φ))
    3. x/(500 * sin(Φ)) = cos(Θ)
    4. arccos(x/(500 * sin(Φ))) = arccos(cos(Θ))
    5. arccos(x/(500 * sin(Φ))) = Θ

    求Φ

    1. y = 500 * cos(Φ)
    2. y/500 = 500 * cos(Φ) / 500
    3. y/500 = cos(Φ)
    4. arccos(y/500) = arccos(cos(Φ))
    5. arccos(y/500) = Φ

    求解lon

    1. Θ = degToRad(lon)
    2. radToDeg(Θ) = degToRad(degToRad(lon))
    3. radToDeg(Θ) = lon

    求解lat

    1. Φ = degToRad(90 - lat);
    2. radToDeg(Φ) = radToDeg(degToRad( 90 - lat ))
    3. radToDeg(Φ) = 90 - 纬度
    4. radToDeg(Φ) - 90 = 90 - 纬度 - 90
    5. radToDeg(Φ) - 90 = -lat
    6. -1*(radToDeg(Φ) - 90) = -1*(-lat)
    7. -radToDeg(Φ + 90 = 纬度
    8. 90 - radToDeg(Φ) = 纬度
    9. 90 - radToDeg(Φ) = 纬度

    【讨论】:

    • 谢谢,camera position出现了一个bug,你可以在jsfiddle.net/42qeojs0看到它(取消注释init末尾的3行camera position)
    • 是的,我改变了原点,但为什么这会影响你的计算?
    • 如果取消注释这些行,当您第一次移动相机时,它不会从正方形的位置开始移动(但在它运行良好之后)
    • 感谢您的澄清。我花了一些时间研究它。问题是你正在改变原点,这改变了我用来回答这个问题的数学(将球坐标转换为矩形时三角函数的变化)。如果您要手动更改它,您需要考虑原点处 X、Y 和 Z 的变化。纬度/经度计算需要重新进行,因为值不正确,因为原点不在 0,0,0 并且 X、Y 和 Z 不再是三角形边的长度,可以这么说。这是一个更复杂的问题,需要更复杂的答案。
    • 三个维度的笛卡尔坐标系,而不是矩形(二维的笛卡尔坐标)。如果我有空闲时间,我会再看一下——我可以看到在这里回答这个问题(尽管超出了范围)仍然可以使这篇文章受益。
    【解决方案2】:

    camera.lookAt(yourObject.position);//?

    假设我说对了,问题是这部分:“一种旋转相机以查看对象的方法”

    顺便说一句,我认为在您的代码中,您如何修改 camera.target 并调用 camera.lookAt 令人困惑。我认为当你调用lookAt时你不需要修改目标,反之亦然。

    【讨论】:

    • 如果您查看 codepen 示例,您会看到此代码允许您使用鼠标单击并拖动以旋转相机。在 onDocumentMouseDown 和 onDocumentMouseMove 函数中有一个 lon 和一个 lat 变量,用于创建 phi 和 theta。然后使用 phi 和 theta 设置目标的 xyz 值。并且相机设置为查看这些坐标。我应该提到的是我可以使用 camera.lookAt(yourObject.position);旋转相机但是如果我这样做,下次我点击屏幕时,相机会跳到最后知道的 lon lat。
    • 我确实检查了您的示例,但并没有真正理解这个想法是什么。实际上仍然没有得到你想要的......你已经知道如何将凸轮旋转到一个对象(这是问题的标题)。当用户点击之后你希望发生什么?
    • 最后一条评论空间不足。哈哈。所以我正在寻找的是一种计算我想要查看的对象的经纬度位置的方法,或者一种计算相机旋转后经纬度的方法。任何帮助将不胜感激。我在想是否可以从 lon 和 lat 计算目标 x、y、z 值,而不是可以从 x、y、z 值反向计算 lat 和 lon。不幸的是,数学超出了我的能力范围。
    • 好的,这里是示例的更新版本。按下任意键将导致相机旋转到对象,但如果您下次通过拖动鼠标移动相机时这样做,相机将自动跳转到击键前的最后一个位置。这是因为为了让鼠标移动相机,它必须跟踪纬度和经度。所以我遇到的问题是在击键后计算新的纬度位置。 codepen.io/anon/pen/GgRXJz
    • 是的,我昨晚正在考虑这个问题,但是还有很多其他代码依赖于 lat lon 系统。找到一种从 camera.target xyz 计算纬度的方法是理想的。
    猜你喜欢
    • 1970-01-01
    • 2014-05-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-24
    • 2018-06-14
    • 2018-01-30
    相关资源
    最近更新 更多