I have situation illustrated on image above, name displayed over the player. Text is rendered as 3d rectangle and rotated to face camera as billboard. (I am using this article http://blogs.msdn.com/b/shawnhar/archive/2011/01/12/spritebatch-billboards-in-a-3d-world.aspx) I just want to have size of the text always the same. Are there any common approach to adaptively scale object based on distance?

Scale problem

  • 2
    \$\begingroup\$ Draw your text as a screen space overlay rather than an object that is being transformed from an alternate coordinate system. \$\endgroup\$ – Evan Feb 17 '14 at 22:09

Objects in 3D space are transformed using world/view/projection matrices to give positions in projection space. You should be familiar with these from using BasicEffect.

You can use these matrices yourself to transform a position into projection space:

Matrix transform = (worldMatrix * viewMatrix * projMatrix);
Vector3 modelAttachPoint = Vector3.Zero; // where you want the label, in model space
Vector3 projPosition = Vector3.Transform(modelAttachPoint, transform);

Projection space runs from (-1,-1) in the bottom left corner of the viewport, to (1,1) in the top right (and from 0 to 1 on the Z axis for depth).

We could use simple maths to convert our projected position into pixel coordinates. But it's even easier to just take the matrix that SpriteBatch uses to go from pixel coordinates to projected coordinates, and invert it.

Fortunately - that very article you linked gives us the SpriteBatch projection matrix:

Viewport viewport = GraphicsDevice.Viewport;
Matrix spriteBatchMatrix = Matrix.CreateTranslation(-0.5f, -0.5f, 0) * 
    Matrix.CreateOrthographicOffCenter(0, viewport.Width, viewport.Height, 0, 0, 1);

And we can use it like so:

Vector3 pixelPosition = Vector3.Transform(projPosition, spriteBatchMatrix.Invert());

Of course, that position won't necessarily line up with pixel boundaries (which you want for nice, sharp text). Also it has an unnecessary Z-coordinate. So let's finish up with something you can pass into SpriteBatch:

Vector2 spritePosition = new Vector2((float)Math.Floor(pixelPosition.X), 

(Note: code in this answer is completely untested!)


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.