Particle System Billboard & Rotation

Started by
21 comments, last by SiliconMunky 18 years, 7 months ago
A simpler solution to your problem might be using the modelview matrix to your advantage. You can transform the 'up' and 'right' vectors of the camera to particle coordinate system (transformed, rotated and scaled as much as you like) and use these vectors to construct the quad. It is now not required to use glRotate to align the quads with the camera.
Since I had nothing better to do, I wrote the main part down for you:
void get_align_vectors(CVector3 &quad_up, CVector3 &quad_right){    float model_view[16];    CVector3 cam_up_vec = CVector3(0.0f, 1.0f, 0.0f),             cam_right_vec = CVector3(1.0f, 0.0f, 0.0f);    glGetFloatv(GL_MODELVIEW_MATRIX, model_view);    quad_up    = apply_matrix_to_vec(transposed(model_view), cam_up_vec);    quad_right = apply_matrix_to_vec(transposed(model_view), cam_right_vec);    quad_up.normalize();    quad_right.normalize();}void draw_particle(CVector3 quad_up, CVector3 quad_right, CVector3 quad_pos){    CVector3 corner_tr,             corner_tl,             corner_br,             corner_bl;    corner_tr =  quad_up + quad_right + quad_pos;    corner_tl =  quad_up - quad_right + quad_pos;    corner_br = -quad_up + quad_right + quad_pos;    corner_bl = -quad_up - quad_right + quad_pos;    glBegin(GL_TRIANGLE_STRIP);        glVertex3f(corner_tr.x, corner_tr.y, corner_tr.z);        glVertex3f(corner_br.x, etc...);        glVertex3f(corner_bl.x, etc...);        glVertex3f(corner_tl.x, etc...);       glEnd();}void draw_particles(){    CVector3 up, right;    get_align_vectors(up, right);    for (i = 0; i < nr_quads; i++)        draw_particle(up, right, particle.position);}


Note that this is untested and uncompiled, but I believe the theory is sound. I left out the standard matrix/vector functions.

Tom
Advertisement
Thanks for the helpful replies. The code I am using to align the quads to the screen is as follows:

void CGLView::BillboardSpherical(CVector3 vCamPos, CVector3 vObjPos){	CVector3 vLookAt(0.0f, 0.0f, 1.0f);        CVector3 vObjToCamProj(0.0f, 0.0f, 0.0f);        CVector3 vUpAux(0.0f, 0.0f, 0.0f);	float angleCosine = 0.0f;	glPushMatrix();		// objToCamProj is the vector in world coordinates from the 	// local origin to the camera projected in the XZ plane	vObjToCamProj.x = vCamPos.x - vObjPos.x;	vObjToCamProj.y = 0.0f;	vObjToCamProj.z = vCamPos.z - vObjPos.z;	//normalize both vectors to get the cosine directly afterwards	vObjToCamProj.Normalize();	//compute the angle	angleCosine = vLookAt.Dot(vObjToCamProj);	//easy fix to determine wether the angle is negative or positive	//for positive angles upAux will be a vector pointing in the 	//positive y direction, otherwise upAux will point downwards	//effectively reversing the rotation.	vUpAux = vLookAt.Cross(vObjToCamProj);	vUpAux.Normalize();	//perform the rotation. The if statement is used for stability reasons	//if the lookAt and objToCamProj vectors are too close together then 	//|angleCosine| could be bigger than 1 due to lack of precision   if ((angleCosine < 0.99990) && (angleCosine > -0.9999))      glRotatef(acosf(angleCosine) * 180.0f / 3.1415927f, vUpAux.x, vUpAux.y, vUpAux.z);	      	//so far it is just like the cylindrical billboard. The code for the 	//second rotation comes now	//The second part tilts the object so that it faces the camera        CVector3 vObjToCam;        //objToCam is the vector in world coordinates from the local         //origin to the camera	vObjToCam.x = vCamPos.x - vObjPos.x;	vObjToCam.y = vCamPos.y - vObjPos.y;	vObjToCam.z = vCamPos.z - vObjPos.z;	//Normalize to get the cosine afterwards	vObjToCam.Normalize();	//Compute the angle between objToCamProj and objToCam, 	//i.e. compute the required angle for the lookup vector	angleCosine = vObjToCamProj.Dot(vObjToCam);	//Tilt the object. The test is done to prevent instability 	//when objToCam and objToCamProj have a very small	//angle between them	if ((angleCosine < 0.99990) && (angleCosine > -0.9999))	{		if (vObjToCam.y < 0.0f)			glRotatef(acosf(angleCosine) * 180/3.14f,  1, 0, 0);			else			glRotatef(acosf(angleCosine) * 180/3.14f, -1, 0, 0);	      	}}


So I basically call
  BillboardSpherical(m_Camera.vPosition, vParticle);  glBegin(GL_TRIANGLE_STRIP);  ....  glEnd();  BillboardEnd();


Where BillboardEnd just contains a glPopMatrix().

This function needs to be changed to accomodate rotation about an arbitrary location. How would I go about this? Change the final glRotatef to be about a vector other than (1, 0, 0)? Would I need to calculate a different vLookAt also?

I'm not too familiar with OpenGl, what does glRotatef do? I don't understand how those matricies get applied when rendering the quad.
Well, glRotatef(angle, x, y, z) basically multiplies the current modelview matrix by a matrix that rotates an object (or your coordinate system) counterclockwise by "angle" degrees, about the vector that goes from the origin through the point (x,y,z).

So the first glRotatef in that function rotates a quad about an "up" vector to point it in the direction of the camera (cylindrical billboard), and the second glRotatef rotates the quad about the x axis so it actually faces the camera (spherical billboard).
Why use a struct instead of a class? I've always wondered...
@ tritone: I would take a different approach overall if I were you - trying to do it all with glRotate() and so on seems like taking the long way around to me. I also think it may introduce some unnecessary complications.

The first thing to consider is whether you want your particles oriented toward the camera, or the viewplane. I've never actually written a particle engine per se, but it seems to me you'd want the latter.

One advantage of viewplane alignment is that all the particles share the same orientation matrix. Generally, creating a billboard matrix is not an expensive operation, but get a few thousand particles going and it may become an issue.

So to get to the point: I would construct the billboard matrices yourself rather than using gl functions, and then upload them via glMultMatrix(). To construct it yourself you'll need a basic understanding of affine transformation matrices in general, and OpenGL matrices in particular. You can just make one matrix, an array of 16 floats. The basis vectors for this matrix can be constructed from the camera basis vectors as explained earlier in the thread. This same matrix can be used for each particle, with only the translation element changed to reflect the particle's position. This means you won't be able to use glRotate() to rotate the whole system; however, this is something you can easily do yourself.

If someone who has implemented a particle system reads this and disagrees with these suggestions, perhaps they can suggest an alternative method. However, I think the above would solve your problems (although it may take a little work to implement).
I think the easiest way to align the quad to your camera is to create a look at matrix.

This tutorial should help you out, it was helpful for me when I setup my DX particle system.

http://nehe.gamedev.net/data/articles/article.asp?article=19#4
What I do, is just perform all the transformations I want, then just before drawing the billboard I get the MV matrix, set the 3x3 submatrix to Identity, and load it again:

glPushMatrix();glTranslatef(portals.pos.x,portals.pos.y,portals.pos.z);glGetDoublev(GL_MODELVIEW_MATRIX,&matr[0]);matr[0]=1;matr[1]=0;matr[2]=0;matr[4]=0;matr[5]=1;matr[6]=0;matr[8]=0;matr[9]=0;matr[10]=1;glLoadMatrixd(&matr[0]);glBegin(GL_QUADS);glTexCoord2f(0,0);glVertex3f(-1000,1000,0);glTexCoord2f(1,0);glVertex3f(1000,1000,0);glTexCoord2f(1,1);glVertex3f(1000,-1000,0);glTexCoord2f(0,1);glVertex3f(-1000,-1000,0);glEnd();glPopMatrix();


What it does, is keep all the transformation info(which is at the 4th row and column) and cancels out any rotation. It has worked fine for me until now. If you use vertex shaders, you can even skip the glGet() and glLoadMatrix() and just alter the MV matrix inside the shader.
mikeman, how does that rotate the billboard to face the camera then? Wouldn't all the billboards just look down the Z axis?
Quote:Original post by SiliconMunky
mikeman, how does that rotate the billboard to face the camera then? Wouldn't all the billboards just look down the Z axis?


Well yeah, the billboards will be viewplane-aligned instead of viewpoint-aligned which is performance costly. There will be a small error, but from my experience the results are more than acceptable.

This topic is closed to new replies.

Advertisement