Three.js:在场景角落显示世界坐标轴

23

我想在相机的右下角像Maya一样显示箭头,表示世界坐标的方向(x、y、z),这样当围绕物体旋转相机或穿过场景移动时,仍然可以识别世界坐标的方向。


我已经尝试了两种不同的方法,但都没有成功。

我有一个对象,它有三个箭头作为子元素,使用THREE.ArrowHelper类创建,我们将其称为XYZ。第一种方法是将XYZ设为场景的子元素,并为其提供一个位置,该位置从相机的当前位置加上一个偏移量,指向相机指向的方向并进行调整,使其出现在我想要的屏幕角落而不是屏幕中央。我几乎完成了这项工作,箭头保持正确的旋转,但位置有些奇怪,我停止了这条路,因为在移动相机时它非常“抖动”。我不确定是否是性能问题还是其他问题。

第二种方法是将XYZ设为相机的子元素,并进行局部位置偏移,然后反转相机的旋转并将反转的旋转应用于XYZ,以使其与世界坐标匹配。使用这种方法时,我似乎接近成功,但我无法同时得到正确的位置和旋转。

我目前正在使用代码XYZ.matrix.extractRotation(camera.matrixWorldInverse);来为我提供XYZ的正确方向,但其位置不正确。如果我使用XYZ.matrixWorld.extractRotation(camera.matrixWorldInverse);,则位置保持良好(附加到相机),但方向不会改变。

如果有人有一个快速的方法让这个工作起来,那将不胜感激。如果你有比我追求的更好的方法,那也会很有帮助。

4个回答

25

在新的three.js版本中,您可以使用:

var axesHelper = new THREE.AxesHelper( 5 );
scene.add( axesHelper );

参考资料:AxesHelper


21
注意:此答案适用于旧版本的Three.js。

诀窍是添加第二个场景和第二个摄像机,该摄像机具有与原始摄像机相同的方向,但保持与原点指定的距离。

camera2.position.copy( camera.position );
camera2.position.sub( controls.target );
camera2.position.setLength( CAM_DISTANCE );
camera2.lookAt( scene2.position );

three.js r.69


错过了那个链接。我会研究这种方法并回复你的。不过,我在想添加第二个相机是否会比让我上面提到的任一方法工作更降低性能? - wackozacko
成功了!不过我得移除 camera2.position.sub( controls.target ); 这行代码,因为OribtControls.js并没有维护目标位置,但是除此之外解决方案基本上是一样的。 - wackozacko
在r.58中使用controls.center - WestLangley

9

var camera, scene, renderer, geometry, material, mesh, axisHelper, localToCameraAxesPlacement;

init();
animate();

function init() {

    scene = new THREE.Scene();

    camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000);
    camera.position.z = 500;
    camera.up = new THREE.Vector3(0,0,1);
    scene.add(camera);
    
    axisHelper = new THREE.AxisHelper( 0.1 )
    localToCameraAxesPlacement = new THREE.Vector3(0.45 * camera.aspect,-0.45, -2); // make sure to update this on window resize
  scene.add(axisHelper)

    geometry = new THREE.CubeGeometry(200, 200, 200);
    material = new THREE.MeshNormalMaterial();

    mesh = new THREE.Mesh(geometry, material);
    scene.add(mesh);

    renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);

    document.body.appendChild(renderer.domElement);

}

function animate() {

    requestAnimationFrame(animate);
    render();

}

var t = 0
function render() {
    t++
    
    camera.position.x = Math.cos(t/80) * 500
    camera.position.y = Math.sin(t/80) * 500
    camera.lookAt(mesh.position)
    
    camera.updateMatrixWorld()
    var axesPlacement = camera.localToWorld(localToCameraAxesPlacement.clone())
  axisHelper.position.copy(axesPlacement);

    renderer.render(scene, camera);

}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/87/three.min.js"></script>

这是一种“快速且不太规范的”方法,在不创建额外场景的情况下添加AxisHelper。它通过在每次渲染时将轴辅助器移动到相机前方来实现。

// Initialize
var scene = getScene();
axisHelper = new THREE.AxisHelper( 0.1 );
var localToCameraAxesPlacement = new THREE.Vector3(0.45 * camera.aspect,-0.45,-2); // make sure to update this on window resize
scene.add(axisHelper);

// On every render
var camera = getCamera();
camera.updateMatrixWorld();
var axesPlacement = camera.localToWorld(localToCameraAxesPlacement.clone());
axisHelper.position.copy(axesPlacement);

2
你每秒钟实例化了多少个 Vector3?创建一个并重复使用它。 - WestLangley
@trusktr 抱歉耽搁了,我已经添加了一个可运行的代码片段。 - seveibar

3

我知道有点晚,但这是我的codesandbox解决方案,可以呈现多个场景:https://codesandbox.io/s/eager-glade-46ung

它可能有些不稳定,因为我刚接触JS。这基于来自threejsfundamentals的教程,使用renderer.setScissor函数,以正确呈现屏幕中的不同部分。这也意味着你可以使用此设置创建一种调试窗口...也许一个用于坐标轴助手,同时跟踪主要场景。

脚本的重要部分是定义绘图将发生的区域。这是通过在0-1空间中保持测量,然后使用画布大小来适当地缩放它们来完成的。

var nleft = Math.floor(canvas.width * left);
var nbottom = Math.floor(canvas.height * bottom);
var nwidth = Math.floor(canvas.width * width);
var nheight = Math.floor(canvas.height * height);

它完美地工作,不需要任何额外的渲染器。 - Jordi Espada

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