Billboard Tutorial (CS594 - Final)
Byungil Jeong
- Programming Environment
- OS - linux
- Using OPENGL-2.0, GLSL, GLEW, GLUT, ImageMagick
- Sources
- Application Codes : billboard.c (main source), draw.c (draw
a cube for billboard), shader.c (load shader sources) - these codes are
based on the application codes of the brick shader tutorial of
3Dlabs.
- Shader Codes : billboard.vert (vertex shader),
billboard.frag (fragment shader)
- Step 1 : Constructing the billboard with daylight
imagery
-
Drawing Background
Draw a rectangle with background texture setting the camera to look at
(0, 0, -1) from the origin. This makes the background looks as if it
was drawn by parallel projection. The camera setting will be changed
for the billboard. Fixed OpenGL functionality is used for this. Refer
to display() in "billboard.c".

- Drawing the billboard and post
Draw the billboard supported by a post using two cubes. The cube for
the billboard has proper texture coordinates and normals (referring to
"draw.c"). This time we use billboard shaders to draw them.
<Vertex Shader>
// need absolute value of x, y and z component to
classify surfaces
vec3 absNormal = abs(gl_Normal.xyz);
float xzLen = length(absNormal.xz);
// classifying surfaces
if (xzLen > absNormal.y)
showContents = true;
else
showContents = false;
The vertex shader informs the fragment shader of side surfaces on which
billboard image will be drawn. It decides the side surfaces by
comparing the length of x and z component of normal with y component.
// pass texture coordinate to fragment shader
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_Position = ftransform();
Texture coordinates should be passed to the fragment shader through
built-in attribute variable gl_TexCoord to be interpolated properly.
<Fragment Shader>
vec2 texCoord = gl_TexCoord[0].xy;
vec4 myColor = texture2D(colorMap, texCoord);
if (drawPost || !showContents)
myColor = vec4(mortarColor*0.3, 1.0);
gl_FragColor = myColor;
The fragment shader maps textures on the side surfaces of the billboard
cube and uses the mortar color for the top and bottom. "drawPost" is
the unifrom variable which makes the post also drawn by the mortar
color. The screenshot of step 1 follows.
Links to Source codes
step1.tar.gz
- Step 2 : Lighting for
nighttime
- Load another background texture for nighttime and
add toggling background
In the main() of "billboard.c"
bg[0] = loadTexture("nightStar.jpg");
bg[1] = loadTexture("sky1.jpg");
In the display() of the same file
glBindTexture(GL_TEXTURE_2D, bg[textureID]);
Toggling textureID when users press key 't'.
- The vertex shader passes the normal and postion of vertices
and the light position in eye coordinate to the fragment shader.
normal = normalize(gl_NormalMatrix * gl_Normal);
vec4 ecVertex = gl_ModelViewMatrix * gl_Vertex;
vec4 ecLight = gl_ModelViewMatrix * vec4(lightPos, 1.0);
ecLightPos = ecLight.xyz;
ecVertexPos = ecVertex.xyz;
- The fragment shader calculates the light direction per
fragment to compute diffuse light. A point light source is placed at
the center (0, 0, 0) of the billboard cube so each surface is lighted
from back. Because of this, we invert surface normals to compute the
light intensity.
vec3 lightVec = ecLightPos - ecVertexPos;
float dist = length(lightVec); // calculate
distance from the light source to compute attenuation
vec3 lightDir = normalize(lightVec);
vec3 fragNormal = normalize(normal);
float intensity = 2.5*max(dot(lightDir,
-fragNormal), 0.0);
// no lighting for the post and top/bottom
if (drawPost || !showContents)
intensity = 0.0;
if (intensity > 0.0) {
float attenuation = 1.0/(1.0 +
0.2*dist*dist); // quadratic attenuation
if (!dayTime)
myColor *=
intensity * attenuation;
}
else
myColor = vec4(mortarColor*0.2,
1.0);
- The result of step 2.

Links to Source codes
step2.tar.gz
- Step 3 : Animate billboard and light source
- In the fragment shader, give offsets to the
texture coordinate of billboard image and application code keep
increases the offset.
float texOffset = fract(offset);
vec2 texCoord = vec2(gl_TexCoord[0].x + texOffset,
gl_TexCoord[0].y);
- Adapt lighting constants for the case the light
source approaches surfaces very closely
float intensity = 1.5*max(dot(lightDir, -fragNormal), 0.0);
float attenuation = 1.0/(1.0 + 0.001*dist*dist);
- The result of Step 3.

Links to Source codes
step3.tar.gz
- Step 4 : Add transparency - removing red background
of the billboard image
- Draw background to a FBO and add transparency
using the texture attached to the FBO
- Get the window coordinates of each fragment from
gl_FragCoord and compute the background coordinate for each fragment.
vec2 bgCoord = gl_FragCoord.xy/winSize;
vec4 bgColor = texture2D(bgMap, bgCoord);
- By multiplying red component and dividing by blue
component, make the red area of the billboard transparent
float mixRatio =
clamp(transparency*myColor.r/myColor.b, 0.01, 0.99);
- The final result.

Links to Source codes
step4.tar.gz
last revision 5/2/06