Billboards with geometry shader

Billboards in video games are the 2D planes in 3D scene that are always pointed perpendicularly at the camera. They can be used to draw distance objects that doesn’t require much details, to draw particles or they can be used as a part of the editor interface. Normally the plane is rotated by transforming metrices so it face the camera, but there is another, easier way – by using the goods of geometry shader.


In fact, to draw a square billboard we only need the position of it’s center and normalized size of the edge.

We pass the position of the center of the billboard from Vertext Shader straight to the Geometry Shader.
in vec3 inPosition;
out vec4 inoutPosition;
 
void main()
{
    // Just pass the position to the geometry shader.
    inoutPosition = vec4(inPosition,1);
}
After that we calculate the screen position of the center of the billboard by using the world position of the center of the billboard and the view-projection matrix.

This position can be used to generate a square primitive from 4 verticies. On the occasiion, the geometry shader can be used to set the proper UV coordinates of the texture to those verticies.

Because we set only the x and y coordinates of the verticies the created primitive will face the camera and will be perpendicularly to it.
layout( points ) in;
layout( triangle_strip, max_vertices = 4 ) out;
 
uniform mat4 viewProjectionMatrix;
uniform vec2 size;
 
in vec4 inoutPosition[1];
out vec2 outTexCoords;
 
void main()
{
    vec4 center = viewProjectionMatrix*inoutPosition[0];
    vec2 dir = size * 0.5;
 
    // [ ][x]
    // [ ][ ]
    gl_Position = vec4( center.x+dir.x, center.y+dir.y, center.z, center.w );
    outTexCoords = vec2(1,0);
    EmitVertex();
 
    // [x][ ]
    // [ ][ ]
    gl_Position = vec4( center.x-dir.x, center.y+dir.y, center.z, center.w );
    outTexCoords = vec2(0,0);
    EmitVertex();
 
    // [ ][ ]
    // [ ][x]
    gl_Position = vec4( center.x+dir.x, center.y-dir.y, center.z, center.w );
    outTexCoords = vec2(1,1);
    EmitVertex();
 
    // [ ][ ]
    // [x][ ]
    gl_Position = vec4( center.x-dir.x, center.y-dir.y, center.z, center.w );
    outTexCoords = vec2(0,1);
    EmitVertex();
 
    EndPrimitive();
}
The only thing that the fragment shader is doing is setting the color of the pixels based on the passed texture.
out vec4 outColor;

in vec2 outTexCoords;
uniform sampler2D tex;

void main(void)
{
    // Pass the color from the texture
    outColor = texture(tex, outTexCoords);
}
You can download a source code for this project from GitHub, or just get a working .exe application from here.