|
CPP File:
For this tutorial, I am adding a new function called Lighting
and it looks like this:
void Lighting (void) {
GLfloat LightPosition[] = { 0.0, 0.0, 5.0, 1.0};
GLfloat DiffuseLight[] = {1.0, 0.0, 0.0};
GLfloat AmbientLight[] = {0.2, 0.2, 0.2};
GLfloat SpecularLight[] = {1.0, 1.0, 1.0};
glLightfv (GL_LIGHT0, GL_SPECULAR, SpecularLight);
glLightfv (GL_LIGHT0, GL_DIFFUSE, DiffuseLight);
glLightfv (GL_LIGHT0, GL_AMBIENT, AmbientLight);
glLightfv(GL_LIGHT0, GL_POSITION, LightPosition);
GLfloat mShininess[] = {8};
GLfloat DiffuseMaterial[] = {1.0, 0.0, 0.0};
GLfloat AmbientMaterial[] = {0.2, 0.2, 0.2};
GLfloat SpecularMaterial[] = {1.0, 1.0, 1.0};
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, DiffuseMaterial);
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, AmbientMaterial);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, SpecularMaterial);
glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mShininess);
}
If you have followed the lighting tutorials in the OpenGL
section, you would know what this all meant. So if you
have not yet checked it out, go to the OpenGL section
and scroll down to the Lighting Types and Material Lighting
tutorials.
This Lighting function, is being called from within our
display function, just after glLoadIdentity();
#include <GL/glew.h>
#include <GL/gl.h>
#include <GL/glut.h>
#include <windows.h>
#include <stdio.h>
#pragma comment(lib,"glew32.lib")
char *VertexShaderSource,*FragmentShaderSource;
int VertexShader,FragmentShader;
int ShaderProgram;
GLfloat angle = 0.0;
char *readShaderFile(char *FileName) {
FILE *fp;
char *DATA = NULL;
int flength = 0;
fp = fopen(FileName,"rt");
fseek(fp, 0, SEEK_END);
flength = ftell(fp);
rewind(fp);
DATA = (char *)malloc(sizeof(char) * (flength+1));
flength = fread(DATA, sizeof(char), flength, fp);
DATA[flength] = '\0';
fclose(fp);
return DATA;
}
void Lighting (void) {
GLfloat LightPosition[] = { 0.0, 0.0, 5.0, 1.0};
GLfloat DiffuseLight[] = {1.0, 0.0, 0.0};
GLfloat AmbientLight[] = {0.2, 0.2, 0.2};
GLfloat SpecularLight[] = {1.0, 1.0, 1.0};
glLightfv (GL_LIGHT0, GL_SPECULAR, SpecularLight);
glLightfv (GL_LIGHT0, GL_DIFFUSE, DiffuseLight);
glLightfv (GL_LIGHT0, GL_AMBIENT, AmbientLight);
glLightfv(GL_LIGHT0, GL_POSITION, LightPosition);
GLfloat mShininess[] = {8};
GLfloat DiffuseMaterial[] = {1.0, 0.0, 0.0};
GLfloat AmbientMaterial[] = {0.2, 0.2, 0.2};
GLfloat SpecularMaterial[] = {1.0, 1.0, 1.0};
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, DiffuseMaterial);
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, AmbientMaterial);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, SpecularMaterial);
glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mShininess);
}
void display (void) {
glClearColor (0.0,0.0,0.0,1.0);
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
Lighting();
glTranslatef(0,0,-5);
glRotatef(angle,1,1,1);
glRotatef(angle,0,1,1);
glutSolidTeapot(2);
glutSwapBuffers();
angle+=0.5;
}
void InitShader (void) {
GLEW_ARB_vertex_shader;
GLEW_ARB_fragment_shader;
VertexShader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
FragmentShader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
VertexShaderSource = readShaderFile("light-advanced.vert");
FragmentShaderSource = readShaderFile("light-advanced.frag");
const char * VS = VertexShaderSource;
const char * FS = FragmentShaderSource;
glShaderSourceARB(VertexShader, 1, &VS,NULL);
glShaderSourceARB(FragmentShader, 1, &FS,NULL);
free(VertexShaderSource);free(FragmentShaderSource);
glCompileShaderARB(VertexShader);
glCompileShaderARB(FragmentShader);
ShaderProgram = glCreateProgramObjectARB();
glAttachObjectARB(ShaderProgram,VertexShader);
glAttachObjectARB(ShaderProgram,FragmentShader);
glLinkProgramARB(ShaderProgram);
glUseProgramObjectARB(ShaderProgram);
}
void DeInitShader (void) {
glDetachObjectARB(ShaderProgram,VertexShader);
glDetachObjectARB(ShaderProgram,FragmentShader);
glDeleteObjectARB(ShaderProgram);
}
void Init (void) {
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glEnable (GL_LIGHTING);
glEnable (GL_LIGHT0);
}
void reshape (int w, int h) {
glViewport (0, 0, (GLsizei)w, (GLsizei)h);
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
gluPerspective (60, (GLfloat)w / (GLfloat)h, 0.1, 1000.0);
glMatrixMode (GL_MODELVIEW);
}
int main (int argc, char **argv) {
glutInit (&argc, argv);
glutInitDisplayMode (GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowSize (500, 500);
glutInitWindowPosition (100, 100);
glutCreateWindow ("A basic OpenGL Window");
glewInit();
InitShader();
Init();
glutDisplayFunc (display);
glutIdleFunc (display);
glutReshapeFunc (reshape);
glutMainLoop ();
DeInitShader();
return 0;
} |
|
Vertex Shader:
First off, I am going to explain a little thing
with shaders, that will help you to optimize them
in the future. Vertex shaders are called for every
vertex passed through, and Fragment shaders are
called for every pixel on the screen. Because of this
it is wise to do as many calculations in the vertex
shader as possible, as they are called less, and if
the calculation does not need to be called on a per
pixel basis, why do it?
Now as for the tutorial, you will see that I have
removed the variable Diffuse from our vertex shader.
This is because we are now going to calculate that in
the fragment shader so we can get per pixel lighting.
In our vertex shader, I have added the varying variables:
varying vec3 Normal;
varying vec3 Light;
varying vec3 HalfVector;
These hold the surface normal of our vertex, the direction
vector of the light source, and the half vector of the
light source (you may want to look into the maths behind
lighting, I may add something in the maths section at a
later date).
These variables are going to be calculated here, and passed
through to our fragment shader. This is because these variables
will not change, so there is no need calculating them on a
per pixel basis.
Now that we have these variables, we need to calculate them.
Our Normal and Light variables stay exactly the same
as in the previous tutorial. The only new one is the HalfVector
which is called similar to our Light variable, just replace
position with halfVector, and we get the light sources half
vector supplied by OpenGL.
varying vec3 Normal;
varying vec3 Light;
varying vec3 HalfVector;
void main(void)
{
Normal = normalize(gl_NormalMatrix * gl_Normal);
Light = normalize(gl_LightSource[0].position.xyz);
HalfVector = normalize(gl_LightSource[0].halfVector.xyz);
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
} |
|
Fragment Shader:
Make sure you remember to add the varying variables:
varying vec3 Normal;
varying vec3 Light;
varying vec3 HalfVector;
To the fragment shader so that we can read them in.
First off here, we have to make sure that our Normal
has been normalized so to make sure, we are just going
to normalize it with the line:
Normal = normalize(Normal);
Next off we want to calculate our diffuse portion of
the lighting. Now unlike the basic lighting tutorial,
I am going to take into account the materials we have
attached to the object, and the settings we have applied
to our light.
Now for our diffuse portion of our equation we get the line:
float Diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse * max(dot(Normal, Light),0.0);
Now the variable gl_FrontMaterial.diffuse is the diffuse
material we have assigned to the front face of our object,
and to get the diffuse portion of our light, we call the
variable gl_LightSource[0].diffuse. Then to take into account
how intense this should be, we multiply it by:
max(dot(Normal, Light),0.0)
Which from our previous tutorial, we used to calculate how
intense the light should be, using the angle of the surface
compared to the position of the light.
Seeing as though we now have the diffuse part of our lighting
we now need the Ambient term. In this term there are no real
calculations like the dot product, used in the diffuse term.
Here we are just taking values set inside our OpenGL program
and applying them to our object. The variables that we need
to use are:
gl_FrontMaterial.ambient
gl_LightSource[0].ambient
gl_LightModel.ambient
For our Ambient term to work, we need to multiply
gl_FrontMaterial.ambient and gl_LightSource[0].ambient
and then add the product of gl_LightModel.ambient and
gl_FrontMaterial.ambient
So our equation looks like:
float Ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient;
Ambient += gl_LightModel.ambient * gl_FrontMaterial.ambient;
In this tutorial, I am not going to cover the emissive term
that OpenGL supports (I will in a later tutorial), so I am
going to jump straight to the most complicated term in the
lighting equation. Which is the specular term. The reason
that this term is the most complicated, is that it needs
to find the reflection of the lighting from the surface of
the object, to the 'camera'. The first part of the equation
is similar to the start of the previous equations and takes
the materials specular term, and the lights specular term
and multiplies them together to look like:
gl_FrontMaterial.specular * gl_LightSource[0].specular
and then comes the tricky part, we need to multiply this by
pow(max(dot(Normal,HalfVector),0.0), gl_FrontMaterial.shininess);
This takes the dot product of our Normal and HalfVector, and as
we have seen in the diffuse term, sets a max of 0.0
With the number we have just calculated, we need to raise that
to the power of our shininess term set inside our OpenGL program.
Because we are using the halfVector at this stage, we don't need
to use the reflect() expression, but we will when we get into
normal mapping.
So in the end our specular expression looks like:
float Specular = gl_FrontMaterial.specular * gl_LightSource[0].specular * pow(max(dot(Normal,HalfVector),0.0), gl_FrontMaterial.shininess);
Now for the combining of all the expressions. We still need to
multiply the diffuse term by the objects color. And with that
we then need to add the Ambient term and the Specular term.
So our final gl_FragColor call looks like:
gl_FragColor = Ambient + (Diffuse * vec4(1,0,0,1)) + Specular;
varying vec3 Normal;
varying vec3 Light;
varying vec3 HalfVector;
void main(void)
{
Normal = normalize(Normal);
float Diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse * max(dot(Normal, Light),0.0);
float Ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient;
Ambient += gl_LightModel.ambient * gl_FrontMaterial.ambient;
float Specular = gl_FrontMaterial.specular * gl_LightSource[0].specular * pow(max(dot(Normal,HalfVector),0.0), gl_FrontMaterial.shininess);
gl_FragColor = Ambient + (Diffuse * vec4(1,0,0,1)) + Specular;
}
|