First, let us discuss the dot product of two vectors.
The dot product of two vectors u and v is defined as: x = u1*v1 + u2*v2 + ... + un*vn
Example: Vector u = (3, 5, 7) and Vector v = (2, 4, 6)
Then
x = 3*2 + 5*4 + 7*6
x = 6 + 20 + 42
x = 68
x (that is, 68) is then the dot product of vectors u and v.
Note, the dot product is represented as u.v
The dot product is part of a formula for finding the angle between two vectors.
That formula is:
Cos( Theta ) = u.v / u v
That is, the dot product of u and v over the magnitude of u times the magnitude of v.
This can be very useful. Imagine using this formula for particles in a first person shooter.
The particle position can pose as the origin. The default direction that the particles face
can be used for one vector. The direction towards the camera from the particle can be used
as the second vector. Thus far, this gives us the following:
Vector u(0, 0, 1) //facing down the z axis by default
Vector v(cam.x  particle.x, 0, cam.z  particle.z) //cam's position relative to particle
Normalize(v) //normalization of a vector involves dividing each component of the vector by
its magnitude. Length of a vector is defined as the square root of the sum of the squares of
each of the vector's components. That is: sqrt(v.x*v.x + v.y*v.y + v.z*v.z) Therefore,
the normal of a vector is defined as:
Magnitude = sqrt(v.x*v.x + v.y*v.y + v.z*v.z)
Normal = (v.x/Magnitude, v.y/Magnitude, v.z/Magnitude)
Now then, with vectors u and v from above being our vectors representing the directions from
the origin (that is, the particle's default position), we can use the formula from above to
determine how much we must rotate a particle to make it face the camera.
This leads us to the final steps of this sample.
Vector u(0, 0, 1)
Vector v(cam.x  particle.x, 0, cam.z  particle.z)
UMagnitude = sqrt(u.x*u.x + u.y*u.y + u.z*u.z)
VMagnitude = sqrt(v.x*v.x + v.y*v.y + v.z*v.z)
Vector VNormal(v.x/VMagnitude, v.y/VMagnitude, v.z/VMagnitude)
DotProduct = u.x*v.x + u.y*v.y + u.z*v.z
Theta = DotProduct / (UMagnitude * VMagnitude)
All that remains is to rotate the particle by the arccosine of Theta.
Because cosine oscillates between 1 and 1 (and because the result of the formula is
Cosine Theta), this method can get angles up to 180 degrees. This gives us two scenarios for
billboarding: angle = 0 to 179 degrees, and angle = 180 to 359 degrees.
Because this is strictly math, this can be applied to all graphics programming regardless of
the library or API being used. However, because I am a fan of DirectX, the code supplied in
the sample uses DirectX.
D3DXMATRIX GetBillboardMatrix(D3DXVECTOR3 position, D3DXVECTOR3 cam){
//local variables
D3DXMATRIX World; //returned after built
D3DXMATRIX Scalar; //scalar matrix
D3DXMATRIX RotY; //billboard particles
D3DXMATRIX RotZ; //roll particles
D3DXMATRIX Translate; //move particles
D3DXVECTOR3 u(0.0f, 0.0f, 1.0f); //unrotated direction of particles
D3DXVECTOR3 v = cam;
//v = direction from origin to cam
v.x = particles.position.x; //subtract particle position to get
v.y = 0.0f; //cam position from origin
v.z = particles.position.z;
D3DXVec3Normalize(&v, &v);
//get dot product and magnitude of u and v
float theta = u.x*v.x + u.y*v.y + u.z*v.z; //get dot product of u and v (Horizontal angle)
float umag = sqrt( (u.x*u.x) + (u.y*u.y) + (u.z*u.z) ); //magnitude of u
float vmag = sqrt( (v.x*v.x) + (v.y*v.y) + (v.z*v.z) ); //magnitude of v
//get angle between vectors u and v
theta = acos(theta / (umag*vmag)); //cos theta = (u . v) / u v
//set all matrices to identity matrix
D3DXMatrixIdentity(&World);
D3DXMatrixIdentity(&Scalar);
D3DXMatrixIdentity(&RotY);
D3DXMatrixIdentity(&RotZ);
D3DXMatrixIdentity(&Translate);
//scale particles to desired size
D3DXMatrixScaling(&Scalar, particles.scale.x, particles.scale.y, particles.scale.z);
//rotate particles around y axis to make them face camera horizontally
if(cam.x >= particles.position.x)
D3DXMatrixRotationY(&RotY, theta); //180 to 359 degrees
else
D3DXMatrixRotationY(&RotY, theta); //0 to 179 degrees
//rotate around z axis to make particles roll
D3DXMatrixRotationZ(&RotZ, roll);
//move particles to their positions
D3DXMatrixTranslation(&Translate, particles.position.x, particles.position.y, particles.position.z);
//multiply all matrices into final world matrix
D3DXMatrixMultiply(&World, &World, &Scalar); //size
D3DXMatrixMultiply(&World, &World, &RotZ); //z first to let particles roll
D3DXMatrixMultiply(&World, &World, &RotY); //y to make particles face camera
D3DXMatrixMultiply(&World, &World, &Translate); //move particles to their positions
//return final world matrix
return World;
}
