Texturing
Introduction
Textures are basically a chuck of memory, often using R,G,B(,A) values with 8 bits per channel. Usually textures contain image data, but it is data, you can do with it whatever you want. In GLSL a texture is specified as a uniform variable. Textures have their own type, which is one of the following:
sampler1D |
1D texture |
sampler2D |
2D texture
|
sampler3D |
3D texture |
samplerCube |
Cubemap texture |
sampler1DShadow |
1D texture for shadow map |
sampler2DShadow |
2D texture for shadow map |
sampler2DRect |
for rectanguar textures (width, height non power of 2) (GL_ARB_texture_rectangle) |
sampler2DRectShadow |
for shadow map, rectanguar textures (width, height non power of 2) (GL_ARB_texture_rectangle) |
Table: Texture Data Types in GLSL
There are texture lookup functions to access the image data. Texture lookup functions can be called in the vertex and fragment shader. When looking up a texture in the vertex shader, level of detail is not yet computed, however there are some special lookup functions for that (function names end with "Lod").
The parameter "bias" is only available in the fragment shader It is an optional parameter you can use to add to the current level of detail.
Function names ending with "Proj" are the projective versions, the texture coordinate is divided by the last component of the texture coordinate vector.
vec4 texture1D(sampler1D s, float coord [, float bias]) |
|
vec4 texture1DProj(sampler1D s, vec2 coord [,float bias]) |
|
vec4 texture1DLod(sampler1D s, vec4 coord [,float bias]) |
|
vec4 texture1DProjLod(sampler1D s, float coord, float lod) |
|
vec4 texture1DProjLod(sampler1D s, vec4 coord, float lod) |
|
|
|
vec4 texture2D(sampler2D s, vec2 coord [, float bias]) |
|
vec4 texture2DProj(sampler2D s, vec3 coord [,float bias]) |
|
vec4 texture2DProj(sampler2D s, vec4 coord [,float bias]) |
|
vec4 texture2DProjLod(sampler2D s, vec3 coord, float lod) |
|
vec4 texture2DProjLod(sampler2D s, vec4 coord, float lod) |
|
|
|
vec4 texture3D(sampler3D s, vec3 coord [, float bias]) |
|
vec4 texture3DProj(sampler3D s, vec4 coord [, float bias]) |
|
vec4 texture3DLod(sampler3D s, vec3 coord [, float bias]) |
|
vec4 texture3DProjLod(sampler3D s, vec4 coord, float lod) |
|
|
|
vec4 textureCube(samplerCube s, vec3 coord [, float bias]) |
|
vec4textureCubeLod(samplerCube s, vec3 coord , float lod) |
|
|
|
vec4 shadow1D(sampler1DShadow s, vec3 coord [, float bias]) |
|
vec4 shadow1DProj(sampler1DShadow s, vec4 coord [, float bias]) |
|
vec4 shadow1DLod(sampler1DShadow s, vec3 coord [, float bias]) |
|
vec4 shadow1DProjLod(sampler1DShadow s, vec4 coord, float lod) |
|
|
|
vec4 shadow2D(sampler2DShadow s, vec3 coord [, float bias]) |
|
vec4 shadow2DProj(sampler2DShadow s, vec4 coord [, float bias]) |
|
vec4 shadow2DLod(sampler2DShadow s, vec3 coord, float lod) |
|
vec4 shadow2DProjLod(sampler2DShadow s, vec4 coord, float lod) |
|
Table: Texture Lookup Functions in GLSL
Example: Loading a Texture
This part doesn't have anything to do with GLSL, but to have some code to load textures is important. There are many OpenSource libraries specialized in loading image formats. One of them is DevIL, another one is FreeImage. I am going to use FreeImage to load images for this tutorial.
I wrote a little wrapper around FreeImage to load and create textures with minimal code overhead. I also included a simple implementation of a smart pointer, which makes it much easier if you have several objects using the same texture, but you don't have to use it to load textures.
#include "texture.h"
#include "smartptr.h"
cwc::SmartPtr<cwc::TextureBase> pTexture;
void loadtexture()
{
pTexture = cwc::TextureFactory::CreateTextureFromFile("texture.jpg");
}
void draw()
{
if (pTexture) pTexture->bind(0); // bind texture to texture unit 0
}
|
C++ Source Code: Loading Texture |
Example: Swapping Color Channels
An simple GLSL example is to swap the red and blue channel of the displayed texture..
varying vec2 vTexCoord;
void main(void)
{
vTexCoord = gl_MultiTexCoord0;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
|
Vertex Shader Source Code |
the texture coordinate is passed down to the fragment shader as varying variable.
uniform sampler2D myTexture;
varying vec2 vTexCoord;
void main(void)
{
gl_FragColor = texture2D(myTexture, vTexCoord).bgra;
}
|
Fragment Shader Source Code |
You probably noticed the ".xy" and ".bgra". The order of components in GLSL can be changed. This can be done by appending the component names in the order you want. You can even repeat components. In this example ".bgra" is used. This technique can also be used to convert vectors, for example vec4 to vec2.
vec4 TestVec = vec4(1.0, 2.0, 3.0, 4.0);
vec4 a = TestVec.xyzw; // (1.0, 2.0, 3.0, 4.0)
vec4 b = TestVec.wxyz; // (4.0, 1.0, 2.0, 3.0)
vec4 c = TestVec.xxyy; // (1.0, 1.0, 2.0, 2.0)
vec2 d = TextVec.zx; // (3.0, 1.0)
|
Vertex of Fragment Shader Source Code: Swizzle Examples |
You may also wonder why "rgba" is used and not "xyzw". GLSL allows using the following names for vector component lookup:
x,y,z,w |
Useful for points, vectors, normals |
r,g,b,a |
Useful for colors |
s,t,p,q |
Useful for texture coordinates |
Table: Vector Component Names
Multitexturing
Mutlitexturing is very easy: In the GLSL program you simply specify several samplers and in the C++ program you bind textures to the appropriate texture units. The uniform sampler variables must be set to the appropriate texture unit number.
Example Project
This source code only contains the simple example of swapping red and blue channel and is meant as base code for your texturing experiments. It contains everything required to compile it under Visual Studio 8 (GLEW, Freeglut, FreeImage).
Download: GLSL_Texture.zip (Visual Studio 8 Project)
(If you create a project/makefile for a different platform/compiler, please send it to: christen(at)clockworkcoders.com and I will put it here.)