Fixed Size Billboarding - why are my billboards scaling?


I’m trying to do some basic billboarding with the billboards coming out at a fixed size. But for some reason they are scaling with perspective.

Here’s my vertex shader:

attribute vec4 aCentrePosition; 
attribute vec2 aCorner;

uniform float uLeafSize;
uniform mat4 uModelWorldMatrix;    
uniform mat4 uViewProjectionMatrix;

varying vec2 vLoc;
varying float distance;

void main(){
    vec4 TransformedCentre=uViewProjectionMatrix*uModelWorldMatrix *aCentrePosition;
    vec2 offset = aCorner*vec2(uLeafSize);

    vec4 newPos = TransformedCentre+vec4(offset,0,0);
    gl_Position = newPos;

As I understand it - TransformedCentre should be in clip space, so I would expect adding offset to consistently add the same amount of screen no matter the z position of the billboard, but the circles I’m getting recede into the distance as you can see here:


What am I missing?

For completeness here’s my fragment shader:

precision mediump float;

varying vec2 vLoc;
varying float distance;

float draw_circle(vec2 coord, float radius) {
    return step(length(coord), radius);

void main() {
    float green = 0.6*distance/100.0;
    float red = 0.2*distance/100.0;
    float blue= 0.2*distance/100.0;
    red=clamp(red, 0.0, 0.3);
    blue=clamp(blue, 0.0, 0.3);
    green=clamp(green, 0.6, 1.0);

    float circle=draw_circle(vLoc, 1.0);
    if (circle == 0.0)
    vec4 color=vec4(red,green,blue,circle);

    gl_FragColor = vec4(color);


hi @iamalexf
Cann’t say that I understand your code, so this will sort of be from the hip: Keep the projection out of the equation and stick to a clean view-matrix. Add the offset and use the projection on the output.
If you construct the view-matrix from a camera-consept, I’ve found that
viewMatrix = scale(-1,1,-1) x camera-rotate-matrix x inverse(camera_pos_matrix)
correlates well with the glm::projection() … it produces the same matrix as glm::lookAt()

All of the modified positions will have the same W coordinate as the centre. You need e.g.:

vec4 newPos = TransformedCentre+vec4(offset*TransformedCentre.w,0,0);
vec4 newPos = TransformedCentre/TransformedCentre.w+vec4(offset,0,0);

Thanks @CarstenT - thinking about it - I could definitely make it more readable :slight_smile:

Thanks so much @GClements - this is it!

What I was missing was a proper understanding of what the w component does here - which I have now bothered to find out. Thanks for setting me on the path…

it’s not your or your code. I don’t know what a billboard are.

A sprite which is used in place of a 3D object. Typically they’re used to improve performance, as a billboard is just a single quad (two triangles) regardless of the complexity of the object rendered on it. But normally you want billboards to scale with distance, so that probably isn’t the correct term here.

The usual situation where you want the position to be in 3D but the object size to be fixed is for “markers”, icons which are placed at a specific 3D location.

1 Like