PDA

View Full Version : Billboards...



Fiante Endurcie
11-23-2000, 06:43 PM
Sorry to nag you guys with this question as you may find it trivial but I have failed over and over again while trying to get my billboards going and I really really need them to do nefty special effects as it seems that they are used so often in great games for different tasks (particule systems, trees, lights etc)

What I have:
The eye position (x,y,z), the up vector for the eye.
I know how to retrieve the modelview and the projection matrix.

What I want:
After calling glLookAt and glPerspective I want to be able to draw spherical and cylindrical billboards. I want these to drawn at a given spot. By that I mean after some calls to gltranslate and glrotate I want to be able to do math equations giving me the theta needed to turn my 2 triangles so that it is flat to the camera.

I have looked in the message board alot of time and over the internet and I have not found what I was looking for. There was some code but it seemed that it didn't fit my needs.

I have tried to implement Sisgraph97 section 5.7 only to fail miserably. The code is the following on that page:

Veye = [M] [0 0 -1 0]

where M is the modelview at a given time and where [M] -1 is the inverse to that matrix (I

think). Then:

cos Theta = Veye DOT Vfront = val1
sin Theta = Veye DOT Vright = val2
where
vfront = [0 0 1]
vright = [1 0 0]

So the rotation along Y is if i am not mistaken:
sin theta / cos theta = val2 / val1
= > tetha = atan2 (val2 / val1)

It doesn't work.
I know the basic algebra:
a DOT b = ax * bx + ay * by+ az * bz = angle between 2 pts

a CROSS b = [aybz-azby bxaz-axbz axby-aybx] = An ortogonal vector between a and b (thumbs rule)

I have tried other things such as multiplying [0 0 1] and [1 0 0] by the modelview then using the same atan2 thing with the view direction only to fail again and again.
I am seeking the solution I admit defeat (and alot of wasted time) please help me!

[This message has been edited by Fiante Endurcie (edited 11-23-2000).]

zed
11-24-2000, 12:53 AM
a nice billboarding method is one nate miller stuck on his site recently http://nate.scuzzy.net/

DFrey
11-24-2000, 06:58 AM
I do all my billboards in eye-space so no turning is necessary. (Well, at least the billboards don't need turning, everything else does to line up with my eye-space.)

Fiante Endurcie
11-24-2000, 08:26 PM
The problem with Nate's Miller example is that his camera is fix. If you call gluLookAt(something) you will see that his billboard donesn't face the camera but only a fixed point in the modelview. While this is fun to do a simple effect it doesn't work in a game where I want a billboard to be scaled appropriatly depending to the distance to the eye and to always face the moving camera.

Do I have to take the perspective into acount anyway and
does my object need to point at the coordinates of the camera in modelview or does the paremeter of the projection matrix have to be taken into account. I don't know and am mistified quite frankly!

Moz
11-25-2000, 03:11 AM
I've got an idea, but that's just an idea, I've never tried it.
Try to transform the world coordinates of the point(s) where you want your billboard(s) to be to window coordinates with gluProject.
Then change the projection matrix to an orthogonal projection and display your billboard(s) in 2D, facing the camera. You'll have to scale the billboard(s) manually depending on the Z window coordinate of its position.
Afterwards, you can return to your perspective projection.

Moz

Michael Steinberg
11-25-2000, 04:08 AM
If you look somewhere, you'll have an right-axis-vector and an up-axis-vector. The billboard simply has to lay on a plane that is parallel to the plane of these two axises. So you can get the coordinates of the vertices of a billboard quad through linear combination of these vectors and the place-vector where the billboard should be positioned.

Michael Steinberg
11-25-2000, 04:12 AM
Oh, wait, that sounds somehow strange...

Uhmmm, another try, get the vector that moves the billboard to the viewpoint. That vector is orthogonal (is the normal) to the plane where the billboard should lay on.

zed
11-25-2000, 11:20 AM
Originally posted by Fiante Endurcie:
[B]The problem with Nate's Miller example is that his camera is fix. If you call gluLookAt(something) you will see that his billboard donesn't face the camera but only a fixed point in the modelview. While this is fun to do a simple effect it doesn't work in a game where I want a billboard to be scaled appropriatly depending to the distance to the eye and to always face the moving camera.
B]

yes it does you have to first position the 'camera' and then use the code to work out the billboard

jammer
11-25-2000, 08:17 PM
Hello,
well, I'm sure there are many methods, but here's the one I use, which works very well:
First, reset the camera to be at position 0,0,0, facing in the -z axis direction ( This is usually you're starting mode, so a PopMatrix and a PushMatrix should do the trick). Second, rotate according to the direction of the camera in the following order: z axis, x axis, y axis. Now, translate using the position of the particle minus the position of the camera. Finally, rotate again using the direction of the camera, but this time with the values and the order reversed: -y axis, -x axis, -z axis. Now, you draw the particle along the x axis, facing positive z, centered at 0,0,0.
Here's a sample piece of code:

// set camera to 0,0,0 facing -z. Usually: PopMatrix(); PushMatrix();
glRotatef(camera.zdir, 0, 0, 1);
glRotatef(camera.xdir, 1, 0, 0);
glRotatef(camera.ydir, 0, 1, 0);
glTranslatef(particle.x - cam.x, particle.y - cam.y, particle.z - cam.z);
glRotatef(-cam.ydir, 0, 1, 0);
glRotatef(-cam.xdir, 1, 0, 0);
glRotatef(-cam.zdir, 0, 0, 1);
glBegin(GL_TRIANGLE_STRIP);
glVertex3f( -1, 1, 0);
glVertex3f( -1, -1, 0);
glVertex3f( 1, 1, 0);
glVertex3f( 1, -1, 0);
glEnd();

See if that works.

jammer
11-25-2000, 08:38 PM
oops, on the glTranslatef and the second set of rotations, it's supposed to be camera, not cam. I changed it from my original code to be easier to read. Doh! Well, I'm sure you guys figured that out anyway.

Fiante Endurcie
11-26-2000, 07:21 AM
Here is the code I got working for the cylindrical billboards. Any comments (other then my functions and data names are a bit whacky) ?

void billboardy (Matrix4x4 * transform) {
if (!transform)
{
Matrix4x4 modelview;
glGetFloatv(GL_MODELVIEW_MATRIX , modelview);
transform= &modelview;
}

Vector3d vfront (0,0,-1), vright (-1,0,0);
vfront.multAlpha0 (*transform); //multAlpha0 => don't use the translation components
vright.multAlpha0 (*transform);

float val = view.Dot (vfront);
float val2 = view.Dot (vright);

float angle = (float) atan2 (val2, val);

glRotatef ((angle*180.0f / pi), 0, 1, 0);

//draw here
}

I am now trying to get the spherical billboards working. Any pointers anyone?

Eric
11-27-2000, 12:01 AM
That's my code for both billboards.

------------------------------------------------------

void HGL_BillBoard_Cylindrical_Mode(HGL_Vector3Df vNormal,HGL_Vector3Df vUp)
{

GLfloat fModelViewMatrix[16];

glGetFloatv(GL_MODELVIEW_MATRIX, fModelViewMatrix);
HGL_Matrix_4x4f ModelViewMatrix(fModelViewMatrix);
HGL_Matrix_4x4f InvModelViewMatrix=ModelViewMatrix.Invert();
HGL_Vector3Df vOriginalView(0,0,1);
HGL_Vector3Df vOriginalViewSide(1,0,0);
HGL_Vector3Df vView=InvModelViewMatrix*vOriginalView;
vView.Normalize();
HGL_Vector3Df vOrtho=vView.CrossProduct(vUp);
vOrtho.Normalize();
vView=vUp.CrossProduct(vOrtho);

// Dot Product to see if flipping needed //
float fDot=vOriginalViewSide.DotProduct(vView);

// Rotation //
HGL_Vector3Df vAxis=vNormal.CrossProduct(vView);
float cAngle1=vNormal.DotProduct(vView);
float sAngle1=vAxis.Norm();
if (fDot>0)
sAngle1=-sAngle1;
vAxis.Normalize();
HGL_Matrix_4x4f R;
R.LoadRotate(cAngle1,sAngle1,vUp.U,vUp.V,vUp.W);
glMultMatrixf(R.Matrix);

}

void HGL_BillBoard_Spherical_Mode(HGL_Vector3Df vNormal,HGL_Vector3Df vUp)
{

GLfloat fModelViewMatrix[16];

glGetFloatv(GL_MODELVIEW_MATRIX, fModelViewMatrix);
HGL_Matrix_4x4f ModelViewMatrix(fModelViewMatrix);
HGL_Matrix_4x4f InvModelViewMatrix=ModelViewMatrix.Invert();
HGL_Vector3Df vOriginalView(0,0,1);
HGL_Vector3Df vView=InvModelViewMatrix*vOriginalView;
HGL_Vector3Df vOriginalViewSide(1,0,0);
HGL_Vector3Df vOriginalViewUp(0,1,0);
HGL_Vector3Df vViewUp=InvModelViewMatrix*vOriginalViewUp;
vView.Normalize();
vViewUp.Normalize();
// First Rotation //
HGL_Vector3Df vAxis=vNormal.CrossProduct(vView);
float cAngle1=vNormal.DotProduct(vView);
float sAngle1=vAxis.Norm();
vAxis.Normalize();
HGL_Matrix_4x4f R,R1,R2;
R1.LoadRotate(cAngle1,sAngle1,vAxis.U,vAxis.V,vAxi s.W);
// Second Rotation //
HGL_Vector3Df vTransformedUp=R1*vUp;
HGL_Vector3Df vUpAxis=vTransformedUp.CrossProduct(vViewUp);
float cAngle2=vTransformedUp.DotProduct(vViewUp);
float sAngle2=vUpAxis.Norm();
R2.LoadRotate(cAngle2,sAngle2,vUpAxis.U,vUpAxis.V, vUpAxis.W);
// Whole Rotation //
R=R2*R1;
glMultMatrixf(R.Matrix);

}

template <class T> void HGL_Matrix_4x4<T>::LoadRotate(T cAngle,T sAngle,T X,T Y,T Z)
{

HGL_Vector3D<T> V(X,Y,Z);
V.Normalize();
Matrix[0]=V.U*V.U+cAngle*(1-V.U*V.U)+sAngle*0;
Matrix[1]=V.V*V.U+cAngle*(0-V.V*V.U)+sAngle*V.W;
Matrix[2]=V.W*V.U+cAngle*(0-V.W*V.U)+sAngle*-V.V;
Matrix[3]=0;
Matrix[4]=V.U*V.V+cAngle*(0-V.U*V.V)+sAngle*-V.W;
Matrix[5]=V.V*V.V+cAngle*(1-V.V*V.V)+sAngle*0;
Matrix[6]=V.W*V.V+cAngle*(0-V.W*V.V)+sAngle*V.U;
Matrix[7]=0;
Matrix[8]=V.U*V.W+cAngle*(0-V.U*V.W)+sAngle*V.V;
Matrix[9]=V.V*V.W+cAngle*(0-V.V*V.W)+sAngle*-V.U;
Matrix[10]=V.W*V.W+cAngle*(1-V.W*V.W)+sAngle*0;
Matrix[11]=0;
Matrix[12]=0;
Matrix[13]=0;
Matrix[14]=0;
Matrix[15]=1;

}

template <class T> HGL_Point3D<T> HGL_Matrix_4x4<T>::operator*(HGL_Point3D<T>& Point)
{

T rX,rY,rZ;

rX=Matrix[0]*Point.X+Matrix[4]*Point.Y+Matrix[8]*Point.Z+Matrix[12];
rY=Matrix[1]*Point.X+Matrix[5]*Point.Y+Matrix[9]*Point.Z+Matrix[13];
rZ=Matrix[2]*Point.X+Matrix[6]*Point.Y+Matrix[10]*Point.Z+Matrix[14];
return(HGL_Point3D<T>(rX,rY,rZ));

}

template <class T> HGL_Vector3D<T> HGL_Matrix_4x4<T>::operator*(HGL_Vector3D<T>& Vector)
{

T rU,rV,rW;

rU=Matrix[0]*Vector.U+Matrix[4]*Vector.V+Matrix[8]*Vector.W;
rV=Matrix[1]*Vector.U+Matrix[5]*Vector.V+Matrix[9]*Vector.W;
rW=Matrix[2]*Vector.U+Matrix[6]*Vector.V+Matrix[10]*Vector.W;
return(HGL_Vector3D<T>(rU,rV,rW));

}

---------------------------------------------------------------

I haven't cleaned it but it works well.

Regards.

Eric

Michael Steinberg
11-27-2000, 06:20 AM
Oh man, C++ is so complicated!!! It also looks ugly... Sorry guys...

Kilam Malik
11-27-2000, 02:08 PM
To do the same in pure C, it would look really ugly. When you get used to C++, you love it (even templates >;-)).

Deiussum
11-27-2000, 02:44 PM
Originally posted by Michael Steinberg:
Oh man, C++ is so complicated!!! It also looks ugly... Sorry guys...

I don't think I can talk to you anymore. http://www.opengl.org/discussion_boards/ubb/smile.gif

Fiante Endurcie
11-28-2000, 03:09 PM
Thanks for your help you guys. I will go with jammer's code.
By the way jammer do you have a cylindrical version of your code (the code you posted is for spherical billboards) ?

Michael Steinberg
11-29-2000, 08:52 AM
If I look at that overhead (maybe it is no pverhead) it should was performance, no? I'm probably wrong with that.

jammer
11-30-2000, 01:21 PM
The billboard will always face the camera, regardless of your direction. I assume that's what you mean by spherical, so yes, it's spherical. By cylindrical, do you mean that it will face you on certain angles, but will act like a regular polygon on others ( what many games do to produce grass, vines, wires, etc )? If so, then I'm not completely sure how to do it. I haven't experimented with that effect yet, but I might in the future.

By the way, if you try my method and it acts funky, try putting the -axis rotations first, and the regular rotations last. This isn't somehting that should come up or anything, just something to keep in mind.