Overview
This final project is to implement any type of billboard with night light using GLSL shader language.
Another goal is to write a tutorial about how to implement this project.
|
Create Billboard structure and activate Texture (in OpenGL application)
- The way is simailar to creating skybox. Only difference is the direction of normal vector for each side.
- The following example is the front side on billboard.
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texID[RED_ID]);
glBegin(GL_QUADS);
glNormal3f(0,0,1);
glTexCoord2f(1.0f, 0.0f); glVertex3f(x, y , z+length);
glTexCoord2f(0.0f, 0.0f); glVertex3f(x+width, y , z+length);
glTexCoord2f(0.0f, 1.0f); glVertex3f(x+width, y + height, z+length);
glTexCoord2f(1.0f, 1.0f); glVertex3f(x, y + height, z+length);
glEnd();
|
- The first two lines are for activating and binding as you know
- In this project, the front side and back side are used.
- Of course, we need to load shader program and load some textures before doing it.
- Related C++ source code, main.cpp
- Related C++ source code, shader.cpp
|
Create Night Light
- The point light per pixel shader from coming from this web site is used
- This shader is similar to a directional light shader . The direction of the light rays is constant for every vertex, whereas for a point light it is the vertex from the vertex
to the lights poisition. Hence, all that needs to change in the vertex shader is the computation of the lights direction.
- The attenuation must be computed for a point light. The formula is based on OpenGL's method.
- Now, We need to modify some lines for applying for texture's color affected by the point light.
- In my project, texture's color per pixel is added.
/* a fragment shader can't write a verying variable, hence we need
a new variable to store the normalized interpolated normal */
n = normalize(normal);
/* compute the dot product between normal and ldir */
NdotL = max(dot(normalize(lightDir),n),0.0);
color = vec4(texColor1 * NdotL + ambientGlobal.rgb,1.0 * ambientGlobal.a);
if (NdotL > 0.0) {
att = 1.0 / (gl_LightSource[1].constantAttenuation + gl_LightSource[1].linearAttenuation * dist
+ gl_LightSource[1].quadraticAttenuation * dist * dist);
color += att * (diffuse * NdotL + ambient);
halfV = normalize(halfVector);
NdotHV = max(dot(n,halfV),0.0);
color += att * gl_FrontMaterial.specular * gl_LightSource[1].specular * pow(NdotHV,gl_FrontMaterial.shininess);
}
|
|
- Related Vertex Shader
- Related Fragment Shader
|
Do animation - Billboard
- The OpenGL application would set up a uniform variable that control the texture's coordinate.
It counts the simple variable, "anim_count", and passes it into the fragment shader.
- Out animation's main idea is to control the texture coordinate, such as adding, subtracting, muliplying and dividing.
For instance, if you want to have procedual clouds that drift slowly across the sky, you can make a very simple change to the
cloud shader. The offset is updated by application each frame. If we want the clouds to drift slowly from left to right, we just
subtract a small amount from the X componet of the uniform variable each frame. If we want the clouds to move rapidly from
Bottom to Top, we just subtract a larger amount from the Y compoent.
- In this project, we have serveral offsets that are involved to texture coordinate.
- We need to compute time period between different type's offsets for selecting the offset applied for current Texture Coordinate.
- Another animation is "Wobble Effect" from Orange Book Ch 13. but it also use the offset's control.
- diagonal translation (From Top Right To Center)
- texture's X component is divided by offset (20.0 / anim_count)
- texture's Y component is divided by offset (20.0 / anim_count)
-
vec3(texture2D(textureName1,gl_TexCoord[0].st / vec2(20.0/anim_count, 20.0/anim_count)));
- vertical traslation (From Top to Bottom)
- texture's X component is set, 1.0
- texture's Y component is added, anim_count / 20.0
- texColor1 = vec3(texture2D(textureName1,gl_TexCoord[0].st + vec2(1.0, anim_count/20.0)));
- horizontal translation (From Left to Right)
- texture's X component is added, anim_count / 20.0
- texture's Y component is set, 1.0
- texColor1 = vec3(texture2D(textureName1,gl_TexCoord[0].st - vec2(anim_count/20.0,1.0)));
- Wobble Effect
- wobble function is taken form Orange Book CH13
- vertex shader : It will be responsible for a simple lighting computation based on
the surface normal and the light position provided by OpenGL application. It will
pass along the texture coordinate without modification.
- fragment shader :
- start Radian
- ==> the starting point for the perturbation computation in radinas, and it is
incremented by the application at each frame in order to animate the wobble effect.
The wobble effect can be made to go faster by using a larger increment value, and
it can be made to go slower by using a smaller increment amount.
- Freq
- ==> as the Richter scale for wobbles. A value of 0 will result in no wobbles
whatsoever. A value of 1.0 will result in gentle rocking, a value of 2.0 will cause
jiggling, a value of 4.0 will result in wobbling, and a value of 8.0 will result
in magnitude 8.0 earthquake-like effects.
- The first seven lines of the shader bring the value of rad into the range [-PI/2,
PI/2]. When this is accomplished, we can compute sin(rad) using the first two terms
of the Taylor series for sine, which is just x - x^3 /3!. The result of this computation
is multiplied by the x component of Amplitude. The value for the computed sine value
will be in the range [-1,1]. If we just add this value to the texture coordinate
as the perturbation factor, it will really perturb the texture coordinate. We want
a wobble, not an explosion! Multiplying the computed sine value by a value of 0.05
will result in reasonable size wobbles. Increasing this scale factor will make the
wobbles bigger, and decreasing it will make them smaller. You can think of this
as how far the texture coordinate is stretched from its original value. Using a
value of 0.05 means that the perturbation will alter the original texture coordinate
will alter the original texture coordinate by no more than (+,- 0.05). A value of
0.5 means that the perturbation will alter the orignal texture coordinate by no
more than (+,- 0.5)
- With the x perturbation factor computed, the whole process is repeated to compute the y perturbation factor.
- The following image's parameter value
- start Radian = 60
- Freq (x,y)= [0.0,4.0]
- Amplitude (x,y) = [0.0,0.15]
- Related Vertex Shader
- Related Fragment Shader
|
Create LED on the top of Billboard
- Our LEDs keep turning on during only Night Time. There are 5 LEDs on the top of billboard.
- We need the selection function in order to turing on only one LED during Night Timd.
- The way is simple.
- At first, The fragment shader get from "time_count" as UNIFORM from OpenGL application. The value is is divided by the maxmum number of LED. The remain value is discarded using built-in
floor function. Division Operation's means how long it keeps turning on.
- Now, this valus is operated by built-in function,"mod" so that we decide which LED turns on
-
uniform float sunrise_e, myindex;
uniform float time_count;
uniform float maxneon;
varying float LightIntensity;
void main(void)
{
float intensity;
float f = floor(time_count / maxneon);
if (time_count > 5.0 && time_count <= sunrise_e && mod(f,maxneon) == myindex) {
intensity = LightIntensity + 1.0;
}
else if (time_count > 5.0 && time_count <= sunrise_e) {
intensity = LightIntensity * 0.5 + 0.1;
}
else {
intensity = LightIntensity * 0.5 + 0.1;
}
gl_FragColor = vec4 (vec3(1.0,0.0,0.0) * intensity, 1.0);
}
|
- Related Vertex Shader
- Related Fragment Shader
|
Sun - one Rotation per virtual day
- The maximum time of our virtual timer is 200. The Sun is rotating 360 degree according to our virtual time [0.199].
- We need to compute the radian per one unit of our virtual timer. " 360 * time_count / 200"
- "180" is the offset degree for meeting with the coordinate of the OpenGL application.
-
float r = radians(360.0 * time_count / 200.0 + 180.0);
lightPos.x = 0.0;
lightPos.y = 200.0 * sin(r);
lightPos.z = 200.0 * cos(r);
|
|
SkyColor changing according to virtual time
- Sky's color is changed according to "time_count"
- Main idea is we set two colors, horizon and zenith, and mix that value with LightIntensity (Sun Light).
- Night : Horizon and Zenith has almost black Color
-
if (time_count <= night_e) {
horizon.x = 0.1;
horizon.y = 0.1;
horizon.z = 0.1;
zenith.x = 0.0;
zenith.y = 0.0;
zenith.z = 0.0;
}
|
 |
- Sunrise = From black color To (orange color + blue color)
-
else if (time_count <= sunrise_e) {
float f1 = time_count - night_e;
float f2 = sunrise_e - night_e;
horizon.x = 0.1 + f1 / f2 * 0.9;
horizon.y = 0.1 + f1 / f2 * 0.6;
horizon.z = 0.1 + f1 / f2 * 0.1;
zenith.x = 0.0 + f1 / f2 * 0.1;
zenith.y = 0.0 + f1 / f2 * 0.1;
zenith.z = 0.0 + f1 / f2 * 0.4;
}
else if (time_count <= (day_s + sunrise_e - night_e)) {
float f1 = time_count - sunrise_e;
float f2 = sunrise_e - night_e;
horizon.x = 1.0 + f1 / f2 * 0.0;
horizon.y = 0.7 + f1 / f2 * 0.3;
horizon.z = 0.2 + f1 / f2 * 0.8;
zenith.x = 0.1 + f1 / f2 * 0.3;
zenith.y = 0.1 + f1 / f2 * 0.3;
enith.z = 0.4 + f1 / f2 * 0.4;
}
|
 |
- Day : Horizon has white color, and Zenith has almost blue color
-
else if (time_count <= day_e) {
horizon.x = 1.0;
horizon.y = 1.0;
horizon.z = 1.0;
zenith.x = 0.4;
zenith.y = 0.4;
zenith.z = 0.8;
}
|
 |
- Sunset : From Blue to Violet color
else {
float f1 = time_count - day_e;
float f2 = sunset_e - day_e;
horizon.x = 1.0 - f1 / f2 * 0.2;
horizon.y = 1.0 - f1 / f2 * 0.5;
horizon.z = 1.0 - f1 / f2 * 0.5;
zenith.x = 0.4 - f1 / f2 * 0.3;
zenith.y = 0.4 - f1 / f2 * 0.3;
zenith.z = 0.8 - f1 / f2 * 0.4;
} |
 |
- Final sky's color has sun's effect by doing dot product of the vertex position and the light position.
-
vec3 P = normalize(position);
vec3 V = normalize(vertex);
vec3 L = normalize(light);
float pl = max(dot(P, L), 0.0);
float kh = pow(V.y, 0.25);
float kl = pow(pl, 8192.0);
float kg = pow(pl, 8.0) * 0.5;
gl_FragColor = vec4(vec3(kl + kg) + mix(horizon, zenith, kh), 1.0); |
- Related Vertex Shader
- Related Fragment Shader
|
Cloud
- Cloud effect is using noise function and 3D texture mapping.
- The noise function and 3D texture mapping is explained in Orange Book CH 12
- Translating cloud
- This tranlation is similar as the animation effect explained previous.
- The uniform variable "wind" defined in OpenGL application indicates the direction of cloud. The Wind direction is multiplied by the passing
virtual time, and the result value is added into the original texture's coordinate (X Y Z).
- Finally, it is used for performing a 3D texture lookup on our 3D noise function to produce a four-component result.
-
vec3 t = gl_TexCoord[0].xyz;
t.x += time * wind.x / 200.0;
t.y += time * wind.z / 200.0;
t.z += time / 500.0;
|
- Related Vertex Shader
- Related Fragment Shader
|
|