Search code examples
mathmatrixgraphicsrenderingraycasting

What is the math behind billboard sprite drawing? (inverse matrix)


I am following "Raycasting Tutorial" in this site Rayacasting Tutorial, in order to create a 3D perspective in a 2D map similar to an old game known as Wolfenstein_3D.

Here is the result so far as shown in this image:

enter image description here

What I am confused about is the math behind rendering a billboard 2d sprite which is always facing the camera direction.

Here is the billboard 2d sprite how it looks like:

enter image description here

I followed the tutorial about sprite rendering, that you can find it here Draw_Sprite, and I managed to display the billboard sprite in my scene as you can see in the first image, and in the tutorial, they used the inverse matrix, they multiplied the relative position of the sprite with the inverse of the camera matrix.

the relative position of the sprite is combined with 2 coordinates since we are working on a 2d map and it's as follows:

(Disable the dark theme if you want to see a clear image)

enter image description here

and the camera matrix is as follows:

enter image description here

in the tutorial as I mentioned earlier they multiplied the relative position of the sprite with the inverse of the camera matrix as shown below:

enter image description here

But I don't understand how it works, why we need to multiply the inverse of the camera matrix with the matrix of our sprite, I want to understand the logic behind it, How this formula makes the sprite rotate to be always facing the camera direction ?! I am still new in game development.


Solution

  • First some math background for this:

    2x2 matrix for 2D holds just rotation matrix. That means:

    mat2 m;    // rotation matrix
    vec2 a,b;  // 2D points
    
    b = m*a;   // rotates a by m and stores into b
    

    If you multiply by inverse:

    mat2 n;
    n = inverse(m);
    b = n*b;
    

    You obtained original position a because multiplying inverse and direct matrix is unit matrix:

               b =            m*a 
    inverse(m)*b = inverse(m)*m*a 
    inverse(m)*b =              a 
    

    However using matrices for 2D ray caster is highly unusual as its complicates things. See:

    Also using just rotational matrix means you have to offset/translate on your own either before or after rotation. So my bet you are doing in code something like this:

    a = rotation_matrix*a;
    a += translation;
    a = Inverse(rotation_matrix)*a;
    

    As I mentioned in the comments for purely rotational matrices the Transpose function is the same as Its inverse so for 2D:

    m = a0 a1    inverse(m) = transpose(m) = a0 a2
        a2 a3                                a1 a3
    

    For more info about matrices take a look at:

    There are more possible notations of doing this math (using direct or inverse matrices, using row/column major order, multiplying order etc ... which might slightly change the equations).

    However your matrix description does not seem right. It should be:

    | camerax.x cameray.x |
    | camerax.y cameray.y |
    

    So basically the 2 direction vectors (one for x axis and one for y axis of camera/player) in world coordinates (but the camera plane normal is parallel to the other direction so its the same ... just confusing a lot)

    Now this is how I see it:

    sprite

    The player is your camera so what you need os to convert the sprite world postion sw into player coordinates sp. And then just render the sprite parallel to player xz plane (or any other if your axises are configured differently).

    so let the mp be player rotation matrix and pp players world position then:

    sp = mp*(sw-pp)
    

    is the sprite converted into player coordinates. However depending on your engine you might need a fake wall parallel to players xz in world coordinates instead. So the wall would be at:

    sprite_wall_vertexes = sw (+/-) (inverse(mp)*sprite_half_size)