Texturing

Back to GLSL Tutorial Index

Discuss this Tutorial in the Forum

Jump To:

C++ Source
Vertex Shader Source
Fragment Shader Source
Download

CPP File:

In here, I am going to be taking the LoadTexture function
from my OpenGL texturing tutorial, and using that to
load in the exact same texture as from the texturing
tutorial. I am then using code similar to that
found in the Heightmap Multitexture tutorial in the
OpenGL section.

Now for the new code :)

Inside our texturing code:
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glEnable(GL_TEXTURE_2D);

I am going to be adding a, integer variable called
texture_location, and calling the function glUniform1iARB.
The texture_location variable will call the function
glGetUniformLocationARB, which is used to get the location
of a certain variable inside a certain shader. This is then
used to assign values to that variable inside the shader
from here in our OpenGL program.

int texture_location = glGetUniformLocationARB(ShaderProgram, "base_texture");
glUniform1iARB(texture_location, 0);

Because base_texture is the first variable we are setting
from outside the shader, we set glUniform1iARB to:
glUniform1iARB(texture_location, 0);
0, being the first variable, 1 being the second, 2 being the
fourth, etc.

And that is all for the new code in our OpenGL program,
check out the shaders for the rest.

#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;

GLuint texture;

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;
}

GLuint LoadTexture( const char * filename, int width, int height )
{
GLuint texture;
unsigned char * data;
FILE * file;

file = fopen( filename, "rb" );
if ( file == NULL ) return 0;
data = (unsigned char *)malloc( width * height * 3 );
fread( data, width * height * 3, 1, file );
fclose( file );

glGenTextures( 1, &texture );
glBindTexture( GL_TEXTURE_2D, texture );
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
free( data );
return texture;
}

void FreeTexture( GLuint texture )
{
glDeleteTextures( 1, &texture );
}

void Lighting (void) {
GLfloat LightPosition[] = { 0.0, 0.0, 5.0, 1.0};

GLfloat DiffuseLight[] = {1.0, 0.0, 0.0};
GLfloat AmbientLight[] = {1.0, 1.0, 1.0};
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.3, 0.3, 0.3};
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);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
int texture_location = glGetUniformLocationARB(ShaderProgram, "base_texture");
glUniform1iARB(texture_location, 0);
glEnable(GL_TEXTURE_2D);

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("texture.vert");
FragmentShaderSource = readShaderFile("texture.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);

texture = LoadTexture( "texture.raw", 256, 256 );
}

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:

In our vertex shader, we need to add a new varying variable.
I am going to call this texCoord, and it is a vec2:
varying vec2 texCoord;

I then need to assign some values to this variable. But first
I will explain what this variable will do. This variable will
hold the texture coordinates for the current vertex. The
texture coordinates for the teapot which is a predefined GLUT
shape are already assigned in OpenGL by GLUT. So to read them
in, we use the line:
texCoord = gl_MultiTexCoord0.xy
gl_MultiTexCoord is generally used to store texture coordinates
but some people like to store variables in this that will
then be used for other means inside the shader. I am going
to stick with using it for texture coordinates as it is
simpler and less confusing. Also note that I am adding .xy
to the end of gl_MultiTexCoord0, this is to call the
first two parts of the variable because texCoord is only
a two part variable and gl_MultiTexCoord can hold 3D textures
which could be called with .xyz.

varying vec2 texCoord;

varying vec3 Normal;
varying vec3 Light;
varying vec3 HalfVector;

void main(void)
{
texCoord = gl_MultiTexCoord0.xy

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:

In our fragment shader, is where all of our texturing is going
to take place. First off we need to set a uniform variable.
A uniform variable is a variable set from inside our OpenGL
program and then read in our shader program. These can
take any form, such as sampler2D (2D texture), sampler 3D
(3D texture), float, int, vec, etc.

In this case we are going to call in a 2D texture so we need
a sampler2D uniform variable. This variable I am going to
call base_texture and will hold our texture loaded and bound
inside our OpenGL program.

After that, we need to add the varying vec2 texCoord so that
we can read in our texture coordinates set from inside our
vertex shader.

Now that we have our texture ready to be read, and our texture
coordinates. It is now time to call the texture and use it.

I am going to store the texture in a variable called:
vec4 base_color;

Even though our variable is only really a vec3 (r,g,b)
gl_FragColor requires a vec4 color to output, so we
are just going to assing base_color as a vec4 straight out.

Now to read in a texture we need to call:
texture2D(texture, texture coordinates);
Put together, this will look like:
vec4 base_color = texture2D(base_texture, texCoord);

This has just loaded in our texture and is now ready
to use. So in our gl_FragColor line, I am going to replace
vec4(1,0,0,1) with base_color.

Then our final gl_FragColor line will look like:
gl_FragColor = Ambient + (Diffuse * base_color) + Specular;

And when you run the program, you should see a textured
teapot. Now don't worry about any redish orange you see in
the darker parts of the texture, this is just because of
the objects red material and the red light we set up.

uniform sampler2D base_texture;

varying vec2 texCoord;

varying vec3 Normal;
varying vec3 Light;
varying vec3 HalfVector;

void main(void)
{
vec4 base_color = texture2D(base_texture, texCoord);

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 * base_color) + Specular;
}


Download:

Download C++ .CPP Source Code for this Tutorial

Download Vertex Shader Source Code for this Tutorial

Download Fragment Shader Source Code for this Tutorial

Download Texture for this Tutorial

     

 

Copyright 2008, Donald Urquhart
Proudly supported by http://www.cdadc.com