Shaders

Yes shading, the holy grail of real time 3d computer graphics, except they are not actually called shaders, programs are the correct term as in vertex programs instead of vertex shaders and fragment programs instead of pixel shaders, and FYI i am not gonna care about the correct terminology in this tut.
Now we are going to take a first step in learning how to use glsl shaders, we will base this tut on tut1 with all the FBO stuff removed, then we are gonna add five functions to help us, oops i lied, i mean they are going to do all the work for us.
The first function is a function that replaces the old draw_cube() function, and although we don’t really need it, since in this tut we could still just use the cube, but we are going to need it in the various other shader tut’s.
What does it do you ask well it’s name is cv90_render(); and it resides in a file called cv90.cpp, and basically contains a pretty decent mesh of a Swedish built CV9040c, that’s right, it’s a tank (well technically a cross between an APC and a light tank), this particular 3d model was made for FHS in a project called Foreign ground which is set in Liberia, hence in the later tutorials where we will be adding texturing you will see it’s has the traditional UN white colors instead of the slightly cooler M90 pattern.
What are the contents then, well first you have six huge arrays containing all the data that i exported using a program called crossroads3d , old but still useful.
The other part is this function to render it all, it’s pretty straightforward.


void cv90_render(void)
{
int faces=sizeof(cv90_face)/sizeof(long);
int i=0,p=1;

while(i<faces)
{
glBegin(GL_POLYGON);
while(p)
{
if(cv90_face[i]==-1) p=0;
else
{
glTexCoord2f(cv90_uv[cv90_uvface[i]].x,cv90_uv[cv90_uvface[i]].y);
glNormal3f(cv90_normal[cv90_nface[i]].x,cv90_normal[cv90_nface[i]].y,
cv90_normal[cv90_nface[i]].z);
glVertex3f(cv90_vertex[cv90_face[i]].x,cv90_vertex[cv90_face[i]].y,
cv90_vertex[cv90_face[i]].z);
}
i++;
}
p=1;
glEnd();
}
}

Then we need a few variables to store our shaders in


GLhandleARB ProgramObject;
GLhandleARB VertexShaderObject;
GLhandleARB FragmentShaderObject;
char* VertexShaderSource;
char* FragmentShaderSource;
unsigned int useshader;

the second one is just a general purpose function for getting the files length, it’s used in the folowing two functions.

unsigned long getFileLength(ifstream& file)
{
if(!file.good()) return 0;

unsigned long pos=file.tellg();
file.seekg(0,ios::end);
unsigned long len = file.tellg();
file.seekg(ios::beg);

return len;
}

The third and fourth function loads the vertex and fragment program source code from a file, they are both identical save from the different variables used so i am only gonna show loadVShade, you have to do loadFShade yourself.

void loadVShade(char filename[160])
{
ifstream file;
file.open(filename, ios::in);
if(!file) {useshader=0; return;}

unsigned long len = getFileLength(file);
if (len==0) {useshader=0; return;}
VertexShaderSource = new char[len+1];

if (VertexShaderSource == 0) {useshader=0; return;}
VertexShaderSource[len] = 0;

unsigned int i=0;
while (file.good())
{
VertexShaderSource[i++] = file.get();
if (i>len) i=len;
}
i--;
VertexShaderSource[i] = 0;
file.close();
return;
}

and now to the most important one, so important that i have to explain it in segments.

First create all the shader objects we need + other variables

void compileShaders(void)
{
int compiled = 0;
int linked = 0;
char str[4096];

useshader=1;

ProgramObject = glCreateProgramObjectARB();
VertexShaderObject = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
FragmentShaderObject = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);

Next, transfer the shader source to the shader objects, after this is done we don’t need it anymore so lets delete the memory containing the source.

glShaderSourceARB(VertexShaderObject, 1, (const char **)&VertexShaderSource, NULL);
glShaderSourceARB(FragmentShaderObject, 1, (const char **)&FragmentShaderSource, NULL);

delete[] VertexShaderSource;
delete[] FragmentShaderSource;

This step compiles each shader source independently and then checks for compilation errors

glCompileShaderARB(VertexShaderObject);
glGetObjectParameterivARB(VertexShaderObject,
GL_OBJECT_COMPILE_STATUS_ARB, &compiled);

if (!compiled)
{
glGetInfoLogARB( VertexShaderObject, sizeof(str), NULL, str );
MessageBox( NULL, str, "vertex Shader Compile Error", MB_OK|MB_ICONEXCLAMATION );
useshader=0;
return;
}

glCompileShaderARB(FragmentShaderObject);
glGetObjectParameterivARB(FragmentShaderObject,
GL_OBJECT_COMPILE_STATUS_ARB, &compiled);

if (!compiled)
{
glGetInfoLogARB( FragmentShaderObject, sizeof(str), NULL, str );
MessageBox( NULL, str, "Fragment Shader Compile Error",
MB_OK|MB_ICONEXCLAMATION );
useshader=0;
return;
}

when it’s all compiled and well we attach the shader objects to the program objects, now the program object is what we actually call when we want to “bind” a shader.
In this step we can also delete the shader objects, we wouldn’t want to hog all that memory anyway, not after that half meg array the cv90_render() function got.

glAttachObjectARB(ProgramObject,VertexShaderObject);
glAttachObjectARB(ProgramObject,FragmentShaderObject);

glDeleteObjectARB(VertexShaderObject);
glDeleteObjectARB(FragmentShaderObject);

Finally we link the program object and check if something went wrong, if not then we got a shader ready for use.

glLinkProgramARB(ProgramObject);
glGetObjectParameterivARB(ProgramObject, GL_OBJECT_LINK_STATUS_ARB, &linked);
if (!linked)
{
MessageBox (HWND_DESKTOP, "can't link shaders", "Error",
MB_OK | MB_ICONEXCLAMATION);
useshader=0;
return;
}

return;
}

So after all that the end is rather anticlimactic, well i said the functions where gonna take care of it for us, so in the Initialize function just add these line to load and compile the shaders.

loadVShade("glsl.vert");
loadFShade("glsl.frag");
compileShaders();

and to render we use this

[code]if (useshader) glUseProgramObjectARB(ProgramObject);
cv90_render();
if (useshader) glUseProgramObjectARB(0);

There is nothing more to it, oops i lied again, we need the glsl.vert and glsl.frag files


// glsl.vert
varying vec4 pos;

void main( void )
{
pos=gl_Vertex;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
gl_TexCoord[0] = gl_MultiTexCoord0;
}

// glsl.vert
varying vec4 pos;

void main( void )
{
gl_FragColor = vec4(sin(pos.x*10.0),cos(pos.y*10.0),sin(pos.z*10.0),
0)+vec4(gl_TexCoord[0].xy,0,0);
//gl_FragColor = vec4(sin(gl_TexCoord[0].x*2.0),cos(gl_TexCoord[0].y*2.0),0,0);
}

I have included two patterns the default striped pattern and one based on the UV coordinates, just comment and uncomment the two gl_FragColor lines in glsl.frag to switch between them.
Now these shaders are not that advanced, that’s for another tut to teach so for now and as always check the source code for more info, comments and just plainly monkeying around with it and see if you can make something cool.
Download this tutorial for MSVCPP 6.0


No Comments

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a comment


WordPress Themes