Billboarding Instanced Object

I need an instanced mesh to have all instances face the camera.

The solution for this question: is on the right track, but not accurate for my use case.

I have been trying to follow this tutorial:, specifically solution #2, however, I haven’t had any luck.

The answer here also looks promising, but I can’t get anything working.

Does the center position need to be in “world space”? My understanding is that means the center position needs to be sent after any translations to the geometry or mesh.

Finally, after the worldspace position has been calculated for the vertex, what other calculations do I need? I assume the projection matrix multiplication at least. Do I also need model and view matrix multiplied as well?


Also I should mention this is the code I am using for the vertex shader: Using the calculated worldPos vector doesn’t render the mesh. I have a draw call for the mesh, but nothing is visible.

Let me know if I can provide additional information.


Probably a duplicate of:

I just solved it!

The difference is I don’t want all the billboards to be parallel to the camera forward vector. It looks fine in most cases, but in the case where the billboard is not close to the camera forward vector, it’s looks wrong since the billboard is facing a point elsewhere besides the camera position.
Vertex shader:

uniform vec2 size;
uniform vec3 center;

vec3 look = normalize(cameraPosition - center);
vec3 cameraUp = vec3(view[0][1], view[1][1], view[2][1]);
vec3 billboardRight = cross(cameraUp, look);
vec3 billboardUp = cross(look, billboardRight);
vec3 pos = center + billboardRight * v.x * size.x + billboardUp * v.y * size.y;
return pos;


        const fullWidth = 0.75;
        const fullHeight = 0.25;

        containerShape.moveTo(0, 0);
        containerShape.lineTo(0, fullHeight);
        containerShape.lineTo(fullWidth, fullHeight);
        containerShape.lineTo(fullWidth, 0);
        containerShape.lineTo(0, 0);

        const containerGeo = new ShapeBufferGeometry(containerShape);
        containerGeo.translate(-fullWidth / 2, -fullHeight / 2, 0);

        const containerMaterial = new ShaderMaterial({
            uniforms: {
                size: {
                    value: new Vector2(fullWidth, fullHeight),
                center: {
                    value: new Vector3(1, 0.5, 1),

This should be everything. Despite missing images and broken links, this: got me past the finish line.

EDIT 4/12/19
I found a pretty serious artifact with the above solution: If you are willing to accept having the billboard fixed along one axis, the code below solves the problem. Without setting the billboard up vector to a fixed value, I couldn’t fix the glitch.

If the camera is above the billboard (, then you can more easily tell the billboard isn’t really “looking” at the camera. If anyone has a solution to this, please post it!

            vec3 billboard(vec3 v, mat4 view) {
                vec3 look = cameraPosition - instanceOffset;
                look.y = 0.0;
                look = normalize(look);
                // vec3 cameraUp = vec3(view[0][1], view[1][1], view[2][1]);
                vec3 billboardUp = vec3(0, 1, 0);
                vec3 billboardRight = cross(billboardUp, look);
                vec3 pos = instanceOffset + billboardRight * v.x * size.x + billboardUp * v.y * size.y;
                return pos;