Zach Berry

Designer & Developer

Tracking 3D Objects in 2D with three.js

I've been playing with the fantastic three.js library lately, and for a project I needed a way to determine where an object in 3D space was on the page in 2D. I've gotten something working after reading through articles, documentation and commentary on three.js' github issues page.

In the example below I have a div (the red circle) that's being absolutely positioned on top of where I've determined the sphere to be.

This works with both Orthographic and Perspective cameras. You can take a look at the full code, but I've copied the relevant 3d-to-2d code here. First, here's tracking with an Orthographic camera:

var visibleWidth, visibleHeight, p, v, percX, percY, left, top;

visibleWidth = orthoCamera.right - orthoCamera.left;
visibleHeight = orthoCamera.top - orthoCamera.bottom;

// this will give us position relative to the world
p = sphere.matrixWorld.getPosition().clone();

// determine where in the visible area the sphere is,
// with percX=0 meaning the left edge and 1 meaning the right
// and percY=0 meaning top and 1 meaning bottom
percX = (p.x - orthoCamera.left) / visibleWidth;
percY = 1 - ((p.y - orthoCamera.bottom) / visibleHeight);

// scale these values to our viewport size
left = percX * WIDTH;
top = percY * HEIGHT;

// position the overlay so that it's center is on top of
// the sphere we're tracking
$trackingOverlay
    .css('left', (left - $trackingOverlay.width() / 2) + 'px')
    .css('top', (top - $trackingOverlay.height() / 2) + 'px');

Here is the same idea with the Perspective camera:

var p, v, percX, percY, left, top;

// this will give us position relative to the world
p = sphere.matrixWorld.getPosition().clone();

// projectVector will translate position to 2d
v = projector.projectVector(p, perspectiveCamera);

// translate our vector so that percX=0 represents
// the left edge, percX=1 is the right edge,
// percY=0 is the top edge, and percY=1 is the bottom edge.
percX = (v.x + 1) / 2;
percY = (-v.y + 1) / 2;

// scale these values to our viewport size
left = percX * WIDTH;
top = percY * HEIGHT;

// position the overlay so that it's center is on top of
// the sphere we're tracking
$trackingOverlay
    .css('left', (left - $trackingOverlay.width() / 2) + 'px')
    .css('top', (top - $trackingOverlay.height() / 2) + 'px');

I hope this proves useful to someone. Let me know if you have a better technique or if I missed anything.

comments powered by Disqus