Hi I need to implement following effect using vertex shaders. Basically its a shader for particle laser beam that rotates to particle along its own y axis till its "best" visible (Roughly).

My idea was:

  1. Take "y" axis of particle model (its a rectangle) and transform it to view space ("vy").
  2. Calculate the vector orthogonal to "vy" and eye vector ("w"), to get the direction on the screen which "x" of partilce should be oriented.
  3. Change particle vertices model coordinates using "w" instead of x.

2 Answers 2


Axis Aligned billboards can be done through a simple technique.

steps 1 to 4 can be done in CPU as it will be used by all boards.

  1. Firstly, create your billboard in a plane XY. The normal will be assumed to be Z (or in this case vector 0, 0, 1.
  2. Secondly, calculate the camera direction and negate this (normalise also), this will be the target direction for your billboard.
  3. Calculate the angle between the starting normal, and the camera direction normal. Using a Dot product and a cross (to get the axis).
  4. Using the angle and the axis, you can create a rotation matrix.
  5. Apply the billboard matrix to align to the direction of the camera, then apply your WVP matrix to bring it into the perspecitve and world locations.

I've attached my C# SHarpdx code for your inspection. This creates the matrix.


    void CalculateBillboardMatrix(Vector3 a_targetDirection, ref Matrix a_billboardMatrix)
        Vector3 veca = a_targetDirection;
        Vector3 vecb = new Vector3(1, 0, 0); // this is always the direction the particle is initially created in.
                                             //      veca.Normalize();
        VectorExt.Normalise(ref veca);

        if (Vector3.Dot(veca, vecb) != 1.0f)
            Vector3.Cross(ref veca, ref vecb, out Vector3 axis);
            VectorExt.Normalise(ref axis);

            float angle = VectorExt.AngleBetweenVectors(ref vecb, ref veca, ref axis);

            a_billboardMatrix = Matrix.RotationAxis(axis, angle);

            Vector3 offcentre = new Vector3(0.01f, 1, 0.01f);

            a_billboardMatrix = Matrix.RotationAxis(offcentre, 0.0f);


There are more advanced versions of this which I can give you to randomise the orientation.

You should do these in the Geometry shader though. Excerpt from my shader. This sample allows you to pass a random up vector in, I also pass in the size of the geometry I want also. You need to create up and right vector to create the geometry, I mult by the billboard matrix, as it ends up saving 2 matrix mults.

    randomUp = (Random updirection in the XY plane).

    float4 startup = float4(randomUp , 1);
    float4 startright = float4(cross(startup.xyz, float3(1, 0, 0)), 1);

    look = mul(startup, AirBillboardMatrix).xyz;
    right = mul(startright, AirBillboardMatrix).xyz;
    // Compute triangle strip vertices (quad) in world space.

    float4 geometry = inputVertex.geometry;
    float4 v[4];
    v[0] = float4(inputVertex.pos.xyz + geometry.x * right - geometry.y * look, 1.0f);
    v[2] = float4(inputVertex.pos.xyz + geometry.x * right + geometry.y * look, 1.0f);
    v[1] = float4(inputVertex.pos.xyz - geometry.x * right - geometry.y * look, 1.0f);
    v[3] = float4(inputVertex.pos.xyz - geometry.x * right + geometry.y * look, 1.0f);

Ask away. I will also direct you to the answer I have done in another thread -> Orientating billboards


It seems I've did the shader I was hoping too, its complicated though for a such simple thing to do and maybe i've missed something.

#version 120
// simple vertex shader
uniform mat4 viewMatrix;
uniform mat4 gl_ModelViewMatrix;
uniform mat4 gl_ProjectionMatrix;

const vec4 ex = vec4(1.0,0.0,0.0,0.0);
const vec4 ey = vec4(0.0,1.0,0.0,0.0);
const vec4 ez = vec4(0.0,0.0,1.0,0.0);

void main()

  // for rotation matrices this is inverse
  mat4 tvm = transpose(gl_ModelViewMatrix);
  vec3 up = gl_ModelViewMatrix[1].xyz; // ey na to przechodzi

  // question is what the direction in model space is transformed 
  // onto x-y view plane and is orthogonal to beam direction
  vec4 w = vec4(normalize(cross(ez.xyz, up.xyz)), 0.0);
  // cool now invert it to get direction in model space
  vec3 invW = (tvm*w).xyz;

  vec3 pos = vec3(gl_Vertex.x * invW, gl_Vertex.y, gl_Vertex.z); 

  gl_Position    = gl_ModelViewProjectionMatrix * vec4(pos, 1.0);
  gl_FrontColor  = gl_Color;
  gl_TexCoord[0] = gl_MultiTexCoord0;
  • \$\begingroup\$ Since ez is 0, 0, 1..., that cross product (ez.xyz, up.xyz) is just vec3(-up.y, up.x, 0.0). Also, I think the vec3 posts = line would be simpler as vec3(gl_Vertex.x * invW, gl_Vertex.y, gl_Vertex.z); \$\endgroup\$
    – Jibb Smart
    May 21, 2016 at 19:22

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .