18
votes

This is probably a very basic problem, but I haven't found a solution yet and it's been bugging me. I'd like to show arrows indicating the world coordinate directions (x, y, z) in the bottom right hand corner of the camera like is done in Maya, so that when rotating the camera around an object, or moving through a scene, you can still identify the directions of the world coordinates. I've tried to accomplish this using two different approaches and neither has worked so far.

I have an object with three arrows as children using the THREE.ArrowHelper class, we'll call it XYZ for the moment. The first approach is to make XYZ a child of the scene and provide it a position calculated from the camera's current position plus an offset in the direction the camera is pointing and adjust so it appears down in the corner I want it to instead of in the center of the screen. I've almost got this working, as in the arrows maintain the correct rotation, but the position is a little bit funny, and I stopped going down this route because it was really 'jittery' when moving the camera around. I'm not sure whether it was a performance issue or something else.

The second method is to make XYZ a child of the camera with a local position offset and then reverse the rotation of the camera and apply the reversed rotation to XYZ so it matches world coordinates. I seem to be close using this method, but I can either get the position correct, or the rotation correct, not both.

I'm currently using the code XYZ.matrix.extractRotation( camera.matrixWorldInverse ); to provide me with the correct orientation for XYZ, but it's positioning is off. If I use XYZ.matrixWorld.extractRotation( camera.matrixWorldInverse ); then the position remains fine (attached to camera) but the orientation doesn't change.

If anyone has a quick hack to get this working it'd be much appreciated. If you've got a better method, than the one's I'm pursuing then that would also be helpful.

4

4 Answers

20
votes

This has come up before here.

The trick is to add a second scene with a second camera, which has the same orientation as the original camera, but maintains a specified distance from the origin.

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

EDIT: Updated fiddle: http://jsfiddle.net/aqnL1mx9/

three.js r.69

19
votes

With new three.js versions you can use:

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

Reference: AxesHelper

6
votes

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>

This is a "quick and dirty" way to add an AxisHelper without creating an additional scene. It works by moving the axis helper in front of the camera on every render.

// 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
votes

I know it is a little late, but here is my codesandbox with a solution which renders multiple scenes https://codesandbox.io/s/eager-glade-46ung.

It might be a little janky, new to JS, and all. This is based on the tutorial from threejsfundamentals. It uses the renderer.setScissor functions, in order to properly render different portions of the scree. This also means that you could use this set-up to create a debug window of sorts... perhaps one for axes helpers, while you keep track of your main scene.

The important part of the script is defining the regions where drawing will occur. This is done by keeping measurement in 0-1 space, then using the canvas size to scale them appropriately.

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);