View more

View more

View more

### Image of the Day Submit

IOTD | Top Screenshots

### The latest, straight to your Inbox.

Subscribe to GameDev.net Direct to receive the latest updates and exclusive content.

# Bill-boarding oriented rectangle

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

18 replies to this topic

### #1Banderi  Members

Posted 22 June 2013 - 07:22 PM

Ok, here's a more complicated question.

I have two 3D points which I have to "connect" with a rectangle.. sort of what I'd do drawing a LPD3DXLINE between them, however it needs to be a 3D mesh (i.e. perspective and distance resize it). The thing is, it also needs to bill-board itself towards the camera, and here I really don't know how to do it..

I could build a sin/cosine function that rotates 4 points around 2 vectors given a width, that's easy.. but how do I take care of the bill-boarding too?

EDIT: here's an image as example...

Edited by Banderi, 22 June 2013 - 07:30 PM.

### #2BornToCode  Members

Posted 22 June 2013 - 11:11 PM

The bilboard rotation is computed based on the direction of the object looking at the camera. If you have a look at function in your code, you can recompute the up,right and look vector based on the camera position and the object that is been bilboard position.

### #3Banderi  Members

Posted 23 June 2013 - 06:13 AM

I don't have any "look at function".. but even if I had, I fail at math at this point, I don't have a clue about how the matrices and the vector computing actually work for the transformations, the projections, etc.. I just know how to apply them with DirectX APIs, up to this limit.

So not be rude at all but.. could you be a bit more specific on how to do this? '^^

Edited by Banderi, 23 June 2013 - 07:22 AM.

### #4Norman Barrows  Members

Posted 23 June 2013 - 09:11 AM

looked challenging at first, but its not that bad.

the midpoint between your 2 points is where to draw the billboard.

its coordinates are just the average of the coordinates of the two points p1 and p2:

mid.x=(p1.x+p2.x)/2;

mid.y=(p1.y+p2.y)/2;

mid.z=(p1.z+p2.z)/2;

then you need a billboard quad.

if is just something simple like a 10x10 quad, no problem.

if the distance from p1 to p2 defines the width of the billboard, its a bit more work.

so the next question is: what size quad do you want to draw?

once you have a quad mesh and a point to draw at, aiming it at the camera is trivial, billboard xr = cam xr + 180, billboard yr = - cam yr, as i recall.

so, what size quad do you need?

its not too hard to create / scale a quad based on the distance between p1 and p2 if that's what you need.

Edited by Norman Barrows, 23 June 2013 - 09:13 AM.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

### #5Banderi  Members

Posted 23 June 2013 - 10:02 AM

If the quad is the rectangle, it needs to be the distance between the points by a fixed length ("thickness"), as if I was to draw a textured stripe between them.

So yeah, it basically would need to be scaled according to their distance while maintaining its other dimension.

Edited by Banderi, 23 June 2013 - 10:03 AM.

### #6Waterlimon  Members

Posted 23 June 2013 - 10:43 AM

Make a quad, position at midpoint of p1 and p2. (trivial)

Scale it on lets say X by distance between p1 and p2. So the position matrix looks like:

(p1-p2).magnitude(), 0              , 0              , 0

0                              , 1              , 0              , 0

0                              , 0              , 1              , 0

midpoint.x                , midpoint.y, midpoint.z, 1

(thats row major i think. bottom row is position)

Now you just need the matrix that looks from billboard to camera and use some math to combine them (multiply? in my own matrix code i need to do rotation and translation separately for it to work but it was written by me so... yeah.)

For the rotation matrix youll need:

-X axis direction (direction from midpoint/p1 to p2)

-Y axis direction (cross the above and the below vectors (X axis dir and Z axis dir))

-Z axis direction (direction from midpoint to camera)

So now we only need the direction from midpoint to camera, which is simply taking the unit vector from their difference.

What you need to know:

Matrices:

[local X axis direction] ,0

[local Y axis direction] ,0

[local Z axis direction] ,0

[position                   ] , 1 //1 means position 0 means vector/direction

Cross product:

Takes 2 vectors, gives a vector that is perpendicular to both (imagine the 2 vectors form a plane. The result is the normal of the plane. The 2 input vectors dont need to be perpendicular with each other!)

Magnitude/distance:

sqrt(xDifference^2 + yDifference^2 + zDifference^2)

Unit vector/direction:

Take the difference of 2 vectors (a-b), divide by the magnitude of the result. Gives direction vector with length 1.

That should be all that you need.

o3o

### #7BornToCode  Members

Posted 23 June 2013 - 10:45 AM

You know the object position  and you know the camera position. So what you need to do is recompute the right up and look vector of the object based on the camera position relative to the object. That way the object will always look at the camera no matter what the camera position is. You do not need to mock around with the vertices position at all. I have attached a piece of code from my engine that will do just that. If you have any question, please feel free to ask. Another thing to know is that my matrix is row major. So your basic Axis's are row based not column based.

//**************************************************
//Build an billboard matrix.
//**************************************************
Matrix4x4 Matrix4x4::BillboardLookAt(const Vector3& objectPosition,const Vector3& target,const Vector3& up)
{
Vector3 lForward = (target-objectPosition).normalized();
Vector3 lRight = Vector3::Cross(up,lForward).normalized();
Vector3 lUp = Vector3::Cross(lForward,lRight).normalized();
return Matrix4x4::From(lRight,lUp,lForward,Vector3::one,objectPosition);
}

//**************************************************
//Build an matrix from a given set of vectors.
//**************************************************
Matrix4x4 Matrix4x4::From(const Vector3& right,const Vector3& up,const Vector3& forward,const Vector3& scale,const Vector3& position)
{
Matrix4x4 m = Matrix4x4::identity;
m.Array[0]=right.x*scale.x;
m.Array[1]=right.y*scale.x;
m.Array[2]=right.z*scale.x;
m.Array[4]=up.x*scale.y;
m.Array[5]=up.y*scale.y;
m.Array[6]=up.z*scale.y;
m.Array[8]=forward.x*scale.z;
m.Array[9]=forward.y*scale.z;
m.Array[10]=forward.z*scale.z;
m.Array[12]=position.x;
m.Array[13]=position.y;
m.Array[14]=position.z;
return m;
}

Edited by BornToCode, 23 June 2013 - 10:51 AM.

### #8Banderi  Members

Posted 23 June 2013 - 12:46 PM

..sweet Jesus, my brain exploded. Hold on a sec *picks up brain*

@BornToCode

This kind of bill-boarding is not what I require, and I've also achieved it through a waaaay simpler way (same rotation matrix of the camera applied on the mesh).

In other words, if I apply the matrix to a rectangular mesh, it rotates it toward the camera with the original position fixedly projected on a plane. That's not supposed to happen in my case, as the rectangle has to be a connection between two 3D points ;)

@Waterlimon

Not to be rude.. but I just don't get a single word.. as I said, I HORRIBLY fail at math in this point..

Could you make it simpler? If possible, can you use DirectX APIs to do that?

Edited by Banderi, 23 June 2013 - 12:59 PM.

### #9belfegor  Members

Posted 23 June 2013 - 03:41 PM

I am trying to follow Waterlimon pseudo but can't get it to work right:

D3DXVECTOR3 start(0.0f, 0.0f, -5.0f);
D3DXVECTOR3 end(0.0f, .0f, 5.0f);
D3DXVECTOR3 mid = (end + start) * 0.5f;

D3DXMATRIX lWorld, lRot, lTra, lWVP;
D3DXMatrixTranslation(&lTra, mid.x, mid.y, mid.z);
lTra.m[0][0] = D3DXVec3Length(&(end - start));
D3DXMatrixIdentity(&lRot);
D3DXVECTOR3 xDir = end - start;
D3DXVec3Normalize(&xDir, &xDir);
D3DXVECTOR3 zDir = cam->Position - mid;
D3DXVec3Normalize(&zDir, &zDir);
D3DXVECTOR3 yDir;
D3DXVec3Cross(&yDir, &xDir, &zDir);
D3DXVec3Normalize(&yDir, &yDir);
lRot.m[0][0] = xDir.x; lRot.m[0][1] = xDir.y; lRot.m[0][2] = xDir.z;
lRot.m[1][0] = yDir.x; lRot.m[1][1] = yDir.y; lRot.m[1][2] = yDir.z;
lRot.m[2][0] = zDir.x; lRot.m[2][1] = zDir.y; lRot.m[2][2] = zDir.z;
lWorld = lRot * lTra;
...


Quad doesn't scale along start-end point and orientation is wrong.

Edited by belfegor, 23 June 2013 - 03:45 PM.

### #10Waterlimon  Members

Posted 23 June 2013 - 05:32 PM

I might have a mistake there somewhere since matrices are kind of blurry to me too.
-Is the majorness correct? (rows being the vectors)
-Is the matrix multiplication order correct? (i usually have problems with the translation part so that might be wrong)

o3o

### #11Waterlimon  Members

Posted 23 June 2013 - 05:37 PM

Oh i assumed the points to be same depth. Might want to do this in screen space.

Screen space you would just transform the points to screen space, then make quad of correct size (distance between points) and rotate it in 2D.

o3o

### #12Kryzon  Prime Members

Posted 23 June 2013 - 05:40 PM

Creating the mesh quad should be more straightforward, like you said.

The following was taken from user Floyd of the BlitzBasic forums. Say that the vector from Point 1 to Point 2 describes the Z axis of the quad, and say that the Y axis of this quad is aligned to the mesh normal (so the Y axis is perpendicular to the quad). You can figure global Pitch (X) and Yaw (Y) rotation values from these axes.

We need to find the Roll (Z) value.

A way to find this Roll (Z) rotation value for the quad to face the camera is to imagine that this value will make the quad place the camera in this quad's ZY plane.

There may be some negations needed based on what you consider as positive or negative sides, angles etc., but the logic is here.

To do the above, you need to get the camera's position in the quad's local space. Store it in a vector like "cameraLocalPos".

Then you can find the global Roll (Z) value for the quad with this: roll = ATan2( cameraLocalPos.X, cameraLocalPos.Y )

Note the inversion of the ATan2 parameters since we want the angle relative to the Y axis rather than the usual X axis.

Then rotate the quad in global coordinates by the following: Quad.Rotate( GlobalPItch, GlobalYaw, roll ).

Edited by Kryzon, 23 June 2013 - 05:53 PM.

### #13Banderi  Members

Posted 23 June 2013 - 05:52 PM

@Kryzon

Sounds exactly what I need, although the rectangle should not be aligned with any standard axis;

So, one million dollar question: how do I get it to rotate around a particular non-aligned axis (which should correspond to the distance between the points) a certain angle, such that said angle returns a bill-boarded normal (which if I got it right, should be the same formula you showed)?

Edited by Banderi, 23 June 2013 - 05:56 PM.

### #14Kryzon  Prime Members

Posted 23 June 2013 - 06:47 PM

What you're asking depends on how you created the mesh, and how it's positioned relative to its "pivot point" (or transformation matrix, in other words).

Steps:

1) Create a blank mesh object with an identity pivot point. This pivot point being identity means it's located at the world's origin and has zero orientation.

2) Measure the distance between the two 3D points and consider this as the length of the quad. The height of the quad is some predefined value.

Build the quad with absolute world coordinates and centered on the world's origin (so this mesh is actually centered on its own pivot point, which is also at the worlds origin).

Make sure the length of the quad goes along the world's Z axis.

Make sure the visible face of the quad points along the world's Y axis.

This mesh is aligned to the world, it's not facing the camera yet.

3) Build a rotation matrix from vectors A and B.

Vector A is the 'vector going from point 1 to point 2' (considered as Z).

Vector B is the resulting vector from 'crossProduct( A, World_Y_Axis )' (considered as X).

With these two vectors A and B you can find an orientation matrix O.

4) Find the location at 'the middle point between point 1 and point 2' (it's just the average of their coordinates). This finds a location vector L.

5) Transform the quad's matrix by orientation O and location L. This positions and orients the quad just like you want it to be, except it's not facing the camera yet.

6) Transform the quad's matrix Roll (Z) value by the way previously described in post #12. This way the rectangle faces the camera.

Edited by Kryzon, 23 June 2013 - 06:49 PM.

### #15Trienco  Members

Posted 23 June 2013 - 10:26 PM

Building the objects transformation matrix yourself sounds like the easiest approach, except that the two vectors to build the rotation part from are p2-p1 and (to make it face the camera) camera.position - p1 (or any other point on the line).

From there you just get the third vector with a cross product and after that use another cross product between the first and third vector to "fix" the second one (to be orthogonal). Normalize all vectors and you should be done. In your matrix, the first vector is the x-axis, the third one is the y-axis and the second is z. That choice is arbitrary and only depends on how your rectangle is modeled (I assume x being along the length, y being the width and z being the normal vector facing the camera).

There is no manual scaling going on unless what you mean is that is has to scale itself to cancel out the scaling you get from perspective projection.

When you create the geometry, do _not_ center it around the origin or you're just making it pointlessly complicated to move it into position. Just use (0,width,0), (0,-width,0), (length,width,0) and ((length,-width,0).

That approach has worked perfectly fine to have a ton of laser beams flying around. At least it's how I remember it, since the whole project seems to have disappeared.

f@dzhttp://festini.device-zero.de

### #16Kryzon  Prime Members

Posted 23 June 2013 - 11:08 PM

camera.position - p1 (or any other point on the line).

[...]

From there you just get the third vector with a cross product and after that use another cross product between the first and third vector to "fix" the second one (to be orthogonal).

That's an interesting way to get the orientation, it should be much faster.

Thanks for sharing.

### #17belfegor  Members

Posted 24 June 2013 - 02:45 AM

As Trienco proposed i removed scaling and created geometry with predetermined width and length (height in my case) and now it seems to work correctly.

Thank you.

### #18Banderi  Members

Posted 24 June 2013 - 08:23 AM

Mh, I'll try this one as soon as I get my hard disk fixed (hope soon... sigh)

Thanks!

### #19Trienco  Members

Posted 24 June 2013 - 10:31 PM

There is one special case that needs to be considered. If the camera and both points are on the same line, the cross product will be all 0 (or very close to it). On the bright side, in this special case you would be looking at the front or rear edge of the rectangle and couldn't see it anyway.

f@dzhttp://festini.device-zero.de

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.