Expected behavior of CreateBillboard?

I ran across this while trying to point some debug text (that had a world position) at the camera, and wanted to make sure I wasn’t misunderstanding something before I opened a bug report/pull request for a fix I have… Matrix.CreateBillboard() seems to orient billboards the wrong direction, such that textures are flipped left-to-right.

In the simplest case, if I create a textured quad at the origin, in the x-y plane, with the camera positioned at positive-z looking toward the origin, and leave the identity matrix as the world transform, I get the expected orientation of the texture:

However, if I change the world matrix to be the one returned by CreateBillboard() for the given object and camera positions, the quad is reversed (same quad and uv coordinates) and becomes a backface (culling disabled for this test):

Interestingly, if I push the camera up very close to the quad and provide an explicit camera-forward vector to CreateBillboard(), it reverses the orientation of the quad, so the texture is now “correct” (with regard to my expectations, anyway) – it looks like there’s a negative sign in one code path that doesn’t exist in others.

Am I simply misunderstanding how Matrix.CreateBillboard() is supposed to behave? Or is something actually wrong here?

Happy to provide code for the above images, if necessary, but as a newbie I’m unsure of the preferred style for doing so… Code snippets in preformatted blocks? The whole Game.cs file?

Thanks for any guidance!

EDIT: Fixed an incorrect statement that the quad became a backface in the near-camera case; it’s actually in the far-camera case. Sorry for any confusion.

Yes, that’s the expected behavior and it’s mathematically correct.
You will run into the same kind of problem if you work with 3D models which are all made to face Forward in their T-pose.

Therefore, the camera is positioned at the back of the object and facing forward, correct? Which means that you made the quad’s UV facing backwards with a hardcoded rotation of 180 on the Y-axis.
You can fix that either by passing a rotated world matrix or flip the UV to have a quad that is facing forward.

In 2D it’s practical to construct all sprites with flipped UVs and ignore that detail, otherwise it would require an extra transform.

example: Let’s say that I am a a director-(camera) and you are an actor-(quad) on the stage. If I am facing Forward then you must face Backward. However from your point of view, I am the one who’s facing backward and my Left is your Right.

1 Like

Another example,
This time let’s say that you placed the camera Forward in front of the quad looking Backwards. In order to render that you would have to change the UVs to make the quad face forward. Now if you render the quad either with a simple draw, or with CreateBillboard() you would get the same results!

The drawback of that setup is that you need to transform all input from the perspective of the camera view . If you want the sprite to move right (+X), you have to move the quad left (-X) in it’s local coordinates.
That’s why in 2D we prefer a forward-looking camera, it’s more convenient. (and we also prerotate the UVs to make the sprite face backward).

1 Like

Ah, thanks very much. I had the convention for camera placement backwards, apparently. So then the purpose of flipping the polygon as the camera gets very close is just to cull objects that are too close?

That’s probably float precision error, I didn’t really understand what you have done there. Something about moving the camera up and giving an explicit forward vector?
Were you trying to force the correct answer out by giving false input? Does it still matter?

Suppose not! Thanks again.

1 Like

If for example the camera is actually facing down and you were telling it that it’s facing forward , this will case all kinds of errors , even a NaN matrix when the forward-up angle is exactly 0.

The thing that triggers it is just moving the camera very close to the object, say z-distance of 0.01; camera-forward and up were remaining the same. I had a look at the Matrix.cs source and it appears to be deliberate (there’s a block to handle the case of distance being less than some epsilon), but now that I’m understanding the camera convention, I’d guess it’s (as you suggested) to handle FP issues around the camera being nearly on top of the object.

(The explicit camera-forward vector was a red herring in this case, it turns out; that provides different behavior if you’re handling the camera with positive-z out of the screen, as I was. If you’re using camera at negative-z, looking toward positive, then CreateBillboard() assumes the conventional camera-forward for that test if you don’t provide it.)

1 Like