Billboard Effect

Demonstration of a billboarding effect
using the dot product of two vectors.

Because I do not wish to make the source
to my personal engine available to everyone,
the downloadable for graphics techniques are
only the executables.

Download it here

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;
}