three.js如何在平面内旋转相机

5

我在我的应用程序中有一个相机:

camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
camera.position.z = 1;
camera.position.y = -5;
camera.rotateOnAxis(new THREE.Vector3(1, 0, 0), degInRad(90));
camera.up = new THREE.Vector3(0, 0, 1);  

在“渲染”函数中的这些代码必须在我按下键时旋转相机:
if (leftPressed) {
    camera.rotateOnAxis((new THREE.Vector3(0, 1, 0)).normalize(), degInRad(1));
} else if (rightPressed) {
    camera.rotateOnAxis((new THREE.Vector3(0, 1, 0)).normalize(), degInRad(-1));
}
if (upPressed) {
    camera.rotateOnAxis((new THREE.Vector3(1, 0, 0)).normalize(), degInRad(1));
} else if (downPressed) {
    camera.rotateOnAxis((new THREE.Vector3(1, 0, 0)).normalize(), degInRad(-1));
}  

相机旋转了,但不是我需要的方式。我希望相机旋转像FPS(第一人称射击游戏)在平面上那样。请看图片以理解我的意思...
我试图使用sin(1)和cos(1),但无法理解rotateOnAxis是如何工作的,因为translate函数能够完美地在相机所看到的方向移动。
P.S.
这里有一个three.js的文档,也许会有所帮助。
我使用KeyboardJS来处理键盘事件。
这是一个degInRad函数:
function degInRad(deg) {
    return deg * Math.PI / 180;
}  

JSFiddle链接

相机旋转

O - 相机位置
O-O1 - 当前相机方向
R1 - 当前旋转方向
R - 我想要的方向
抱歉图片质量一般。


考虑使用/修改 THREE.PointerLockControls,而不是其他方法。请参见 http://threejs.org/examples/misc_controls_pointerlock.html。请注意,在指针锁源代码中,如何将相机添加为两个对象的子级/孙级,以实现所需效果。 - WestLangley
@WestLangley,很不错的链接,谢谢,但是我不需要用鼠标移动相机。而且 camera 只在 controls = new THREE.PointerLockControls( camera ); scene.add( controls.getObject() ); 中使用 - 是 PointerLockControls 内部对象添加到场景中,而不是 camera 本身,并且在 onWindowResize 函数中 - 只是应用了新的属性,在 renderer.render( scene, camera ); 中 - 这里使用的是原始的 camera 变量。至于您所说的“子/孙”是什么意思?我真的不理解,您能解释一下吗?提前致谢。 - ostapische
请注意,PointerLockControls 如何根据您的需求旋转相机,但它使用鼠标而不是键盘。为了获得这种效果,它使用 pitchObjectyawObject 技术。请参见源代码。这就是我所说的“子/孙”意思。您不必使用 PointerLockControls;您可以自己实现类似的逻辑。 - WestLangley
@WestLangley 这是我的失误。我没有在页面上看到 PointerLockControls 的源代码。现在我会尝试一下,如果一切正常,我会回答问题或者询问您是否有错误。 - ostapische
@WestLangley 我写了一个工作示例在这里。但我不明白为什么three.js的开发人员在代码中使用那些pitchObject.rotation.x = Math.max( - PI_2, Math.min( PI_2, pitchObject.rotation.x ) );的结构。当我取消注释我的代码中的这些行时,垂直移动效果变差。你能解释一下吗?谢谢。 - ostapische
显示剩余4条评论
2个回答

14

通过设置camera.rotation.order = 'YXZ';,您可能会得到想要的结果。


2
+1. 是的,只需直接递增/递减camera.rotation.x.y即可。 - WestLangley
我认为这是一个更好的解决方案,谢谢。现在我看到Object3D.rotationEuler类型而不是Vector3类型... - ostapische

5
相信您所需要的概念是相机架设置。在这种方法中,您首先建立一个Object3D作为“颈部”,然后将相机附加到该对象上并进行所需的旋转。然后,您可以应用旋转到Object3D以查看周围的情况,而不会出现晃动效果。进一步地,您可以将该“颈部”对象嵌套在另一个Object3D中,以充当“身体”,并对该对象应用平移以在场景中移动。通过这种方式,您拥有了一个基本的相机控制器架构。我修改了您的fiddle如下:

编辑:实施更改以更好地适应@ostapische使用情况。这是fiddle的链接:link

function degInRad(deg) {
    return deg * Math.PI / 180;
}
/////////////////////////////////
function render() {
    requestAnimationFrame(render);
    if (leftPressed) {
        neck.rotation.y += degInRad(1);
    } else if (rightPressed) {
        neck.rotation.y -= degInRad(1);
    }
    if (upPressed) {
        camera.rotation.x += degInRad(1);
    } else if (downPressed) {
        camera.rotation.x -= degInRad(1);
    }
    renderer.render(scene, camera);
}
/////////////////////////////////
var scene, camera, renderer, grass, neck;
var leftPressed = false;
var rightPressed = false;
var upPressed = false;
var downPressed = false;
/////////////////////////////////
window.onload = function() {
    scene = new THREE.Scene();
    camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
    cameraHolder = new THREE.Object3D();
    cameraHolder.add( camera );
    renderer = new THREE.WebGLRenderer();
    renderer.rendererSize = {width: window.innerWidth, height: window.innerHeight, quality: 100, maxQuality: 400, minQuality: 20};
    renderer.setSize( renderer.rendererSize.width, renderer.rendererSize.height );
    document.body.appendChild( renderer.domElement );

    var texture, material, geometry, element;

    material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
    material.side = THREE.DoubleSide;
    geometry = new THREE.PlaneGeometry(24, 24);   
    grass = new THREE.Mesh( geometry, material );
    grass.name = "grass";
    scene.add( grass );

    neck = new THREE.Object3D();
    neck.rotateOnAxis(new THREE.Vector3(1, 0, 0), degInRad(90));
    neck.up = new THREE.Vector3(0, 0, 1);
    neck.position.z = 1;
    neck.position.y = -5;


    neck.add(camera);
    scene.add(neck);    

    KeyboardJS.on('left', function() { leftPressed = true; }, function() { leftPressed = false; });
    KeyboardJS.on('right', function() { rightPressed = true; }, function() { rightPressed = false; });
    KeyboardJS.on('up', function() { upPressed = true; }, function() { upPressed = false; });
    KeyboardJS.on('down', function() { downPressed = true; }, function() { downPressed = false; });

    render();
}

我本来想提到PointLockControl或FirstPersonControl,但我看到WestLangley已经很好地建议了。无论如何祝你好运。


你的代码中有错误,但是@WestLangley给了我一个好主意,可以查看PointerLockControls的源代码。你能否将此链接添加到你的答案中或修改你的代码以使其正常工作,并且我可以批准答案。 - ostapische
我们完成了修改后的代码更新。感谢您的考虑,也感谢@WestLangley提供的可靠建议。 - Darryl_Lehmann

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接