Texture Mapping Textures The realism of an image is greatly enhanced by adding surface textures to the various faces of a mesh object. In part a) images have been pasted onto each face of a box. Part b) shows an image which has been wrapped around a cylinder. The wall also appears to be made of bricks however it is just a flat plane with a repeated texture applied to it. Basic Texture Techniques The basic technique begins with some texture function, texture(s,t) in texture space which is traditionally marked off by parameters named s and t. The function texture(s,t) produces a colour or intensity value for each value of s and t between 0 and 1. The Figure shows two examples of texture functions, where the value of texture(s,t) varies between 0 (dark) and 1(light). Part a shows a bitmap texture and part b shows a procedurally produced texture. Bitmap Textures Textures are often formed from bitmap representations of images. Such a representation consists of an array of colour values such as texture[c][r] often called texels If the array has C columns and R rows, the indices c and r vary from 0 to C-1 and R-1 respectively In the simplest case the function texture(s,t) accesses samples in the array as in the code 1 Colour texture(float s, float t) 2 { 3 return texture[int (s*c)][(int) t *R]; 4 }
Bitmap Textures In this case Colour holds an RGB triple. For example if R=400 and C=600, then the texture(0.261,0.783) evaluates to texture[156][313] Note the variation in s from 0 to 1 encompasses 600 pixels whereas the same variation in t encompasses 400 pixels. To avoid distortion during rendering, this texture must be mapped onto a rectangle with aspect ration 6/4. Pasting Textures onto a Flat Surface Since texture space is flat, it is simplest to paste texture onto a flat surface. The figure above shows a texture image mapped to a portion of a planar polygon F We must specify how to associate points on the texture with points on F In OpenGL we use the function gltexcoord2f() to associate a point in texture space, Pi=(si,ti) with each vertex Vi of the face. The function gltexcoord2f(s,t) sets the current texture coordinates to (s,t) and they are attached to subsequently defined vertices. Procedural Textures An alternative way to define a texture is to use a mathematical function or Procedure. For instance the Spherical Shape that appear in the last image could be generated by the following code. 1 float fakesphere(float s, float t) 2 { 3 flat r=sqrt((s-0.5)*(s-0.5)+(t-0.5)*(t-0.5)); 4 if(r<0.3) return 1-r/0.3; 5 else return 0.2; 6 } This function varies from 1 (white) at the center to 0 (black) at the edges of the apparent sphere. Anything that can be computed can provide a texture : smooth blend and swirls of colour, fractals, solid objects etc. This is the way most modern rendering tools provide their shaders Pasting Textures onto a Flat Surface II Normally each call to glvertex3f is preceded by a call to gltexcoord2f so each vertex gets a new pair of texture coordinates. For example to define a quadrilateral face and to position a texture on it we send OpenGL four texture coordinates and four 3D points as follows 1 glbegin(gl_quads); 2 gltexcoord2f(0.0,0.0); glvertex(1.0,2.5,1.5); 3 gltexcoord2f(0.0,0.6); glvertex(1.0,3.7,1.5); 4 gltexcoord2f(0.8,0.6); glvertex(2.0,3.7,1.5); 5 gltexcoord2f(0.8,0.0); glvertex(2.0,2.5,1.5); 6 glend(); Attaching a Pi to each Vi is equivalent to prescribing a polygon P in texture space that has the same number of vertices as F. Usually P has the same shape as F so the mapping is linear and adds little distortion
Mapping a Square to a Rectangle Repeating Textures The figure shows the common case in which the four corners of the texture square are associated with the four corners of a rectangle. In this example the texture is a 640 by 480 pixel bitmap, and it is pasted onto a rectangle with aspect ratio 640/480 so it appears without distortion. Note that the texture coordinates range from 0 to 1 still even though the size is 640-480. The above figure show the use of texture coordinates that tile the texture, making it repeat. To do this, some texture coordinates that lie outside of the interval [0,1] are used. When the rendering routine encounters a value of s and t outside of the unit square, such as s=2.67 it ignores the integral part and uses only the fractional part 0.67. Repeating Textures II Thus the point on a face that requires (s,t)=(2.6,3.77) is textured with texture(0.66,0.77). By default OpenGL tiles textures this way; if desired, it may be set to clamp texture values instead. Thus, a coordinate pair (s,t) is sent down the pipeline along with each vertex of the face. The notion is that points inside F will be filled with texture values lying inside P by finding the internal coordinate values (s,t) through the use of interpolation. OpenGL Texture Mapping Steps To use texture mapping, you perform the following steps 1. Create a texture object and specify a texture for that object 2. Indicate how the texture is to be applied to each pixel. 3. Enable texture mapping 4. Draw the scene, supplying both texture and geometric coordinates. Texture mapping only works in RGBA mode and not in Colour index mode.
Creating a Texture Object A texture is usually thought of as being a 2D image but can also be either a 1D modulation value or a 3D volume data set The data describing the texture may consist of one, two, three or four elements per texel. Typically image data is loaded from an image file to represent either R,G,B or R,G,B,A data. However procedural texture functions may also be used as shown below 1 float fakesphere(float s, float t) 2 { 3 float r=sqrt((s-0.5)*(s-0.5)+(t-0.5)*(t-0.5)); 4 if(r<0.3) return 1-r/0.3; 5 else return 0.2; 6 } Indicate how the Texture is to be applied to Each Pixel You can choose any of four possible functions for computing the final RGBA value from the fragment colour and the texture image data. One possibility is simply to use the texture colour as the final colour. (replace mode) Another method is to use the texture to modulate or scale the fragment's colour. Enable Texture Mapping Texture mapping must be enabled before drawing the scene with textures. Texturing is enabled or disabled using glenable() and gldisable(); The type of texturing to enable is then specified using either GL_TEXTURE_1D GL_TEXTURE_2D GL_TEXTURE_3D Specifying a Texture 1 void glteximage2d( 2 GLenum Target, GLint level, 3 GLint internalformat, 4 GLsizei width, GLsizei height, 5 GLint border, GLenum format, 6 GLenum type, const GLvoid *texels); The function glteximage2d defines a 2D texture it takes several arguments as shown below The Target is set to either GL_TEXTURE_2D or GL_PROXY_TEXTURE_2D Level is used to specify the level of multiple images (mipmaps) if this is not used set to 0. The internalformat specifies the format of the data there are 38 different constants but most common are GL_RGB and GL_RGBA
Specifying a Texture II width and height specify the extents of the image and values must be a power of 2 (128, 256, 512 etc) border specifies the width of a border which is either 0 (no border) or 1 border format and type specify the format of the data type of the texture image data. format is usually is GL_RGB GL_RGBA GL_LUMINANCE type tells how the data in the image is actually stored (i.e. unsigned int float char etc) and is set using GL_BYTE GL_INT GL_FLOAT GL_UNSIGNED_BYTE etc. Finally texels contains the texture image data. gltexparameter 1 gltexparameter{if}(glenum target, glenum pname, TYPE param); gltexparameter is used to specify how textures behave. It has many different parameters as follows The target parameter is GL_TEXTURE_[1D,2D,3D] depending on the texture type The pname and param types are shown in the following table gltexparameter values Parameter GL_TEXTURE_WRAP_S GL_TEXTURE_WRAP_T GL_TEXTURE_WRAP_R Values GL_CLAMP, GL_CLAMP_TO_EDGE, GL_REPEAT GL_CLAMP, GL_CLAMP_TO_EDGE, GL_REPEAT GL_CLAMP, GL_CLAMP_TO_EDGE, GL_REPEAT GL_TEXTURE_MAG_FILTER GL_NEAREST, GL_LINEAR GL_TEXTURE_MIN_FILTER GL_TEXTURE_BORDER_CO LOR GL_TEXTURE_PRIORITY GL_TEXTURE_MIN_LOD GL_TEXTURE_MAX_LOD GL_NEAREST, GL_LINEAR, GL_NEAREST_MIPMAP_NEAREST,GL_NEAREST_MIPMAP_LINEAR,GL_LINEAR_ MIPMAP_NEAREST,GL_LINEAR_MIPMAP_LINEAR any four colour values in [0.0 1.0] [0.0, 1.0] for the current texture object any floating point value any floating point value GL_TEXTURE_BASE_LEVEL any non-negative integer GL_TEXTURE_MAX_LEVEL any non-negative integer Creating a Texture Object with OpenGL 1 GLuint texturename; 2 float Data = some image data 3 4 glgentextures(1,&texturename); 5 glbindtexture(gl_texture_2d,texturename); 6 gltexparameteri(gl_texture_2d,gl_texture_mag_filter,gl_linear); 7 gltexparameteri(gl_texture_2d,gl_texture_min_filter,gl_linear); 8 gltexparameteri(gl_texture_2d,gl_texture_wrap_s,gl_clamp); 9 gltexparameteri(gl_texture_2d,gl_texture_wrap_t,gl_clamp); 10 glteximage2d(gl_texture_2d,0,gl_rgb,size,size,0,gl_rgb,gl_float,data); 11 gltexenvf(gl_texture_env, GL_TEXTURE_ENV_MODE, GL_REPLACE); In the above example texturename is the id of the texture object Data is an array of the RGB tuple data created for the texture (either procedurally or loaded in from a file)
Texture Co-ordinates Normally each call to glvertex3f is preceded by a call to gltexcoord2f so each vertex gets a new pair of texture coordinates. For example to define a quadrilateral face and to position a texture on it we send OpenGL four texture coordinates and four 3D points. Attaching a Pi to each Vi is equivalent to prescribing a polygon P in texture space that has the same number of vertices as F. 1 glbegin(gl_quads); 2 gltexcoord2f(0.0,0.0); glvertex(1.0,2.5,1.5); 3 gltexcoord2f(0.0,0.6); glvertex(1.0,3.7,1.5); 4 gltexcoord2f(0.8,0.6); glvertex(2.0,3.7,1.5); 5 gltexcoord2f(0.8,0.0); glvertex(2.0,2.5,1.5); 6 glend(); Usually P has the same shape as F so the mapping is linear and adds little distortion Loading Images There are many ways to load image data and a number of libraries are available under linux. The easiest method is to use the ImageMagick libraries which act as a front end to all of the image libs. Ultimately when dealing with images for OpenGL we need the data in a contiguous block of RGB(A) memory To get this data we can build a simple texture structure to load from file and store this data Loading Images 1 Magick::Image image; 2 3 image.read(filename); 4 5 Width=image.rows(); 6 Height=image.columns(); 7 8 if (image.depth() == 8) 9 data = new GLfloat[image.rows() * image.columns() * 4]; 10 unsigned long int index=0; 11 12 Magick::ColorRGB c; 13 for(unsigned int y=0; y<height; ++y) 14 for(unsigned int x=0; x<width; ++x) 15 { 16 c=image.pixelcolor(x,y); 17 data[index]=c.red(); 18 data[index+1]=c.green(); 19 data[index+2]=c.blue(); 20 data[index+3]=1.0-c.alpha(); 21 index+=4; 22 } 23 24 return 1; Billboarding good example of texture use is Billboarding. This allows us to create a texture object on a small quad which is always aligned with the camera. Using billboards we can create Sprites and other object in 2D which will always be facing the camera. If we use alpha blending and depth sorting we can get different effects
Billboard References 1 void Billboard::CalcBB( 2 const ngl::camera *_cam 3 ) 4 { 5 // cam axis is assumed to be three 4tuples, 6 // ordered x, y, z so... 7 // strip out the camera axis and build vectors 8 ngl::vector cam_up=_cam->m_v; 9 ngl::vector cam_left=_cam->m_u; 10 // find diagonal vectors (4 points of poly) 11 m_ul=cam_up + cam_left; 12 m_ur=cam_up - cam_left; 13 m_ul.normalize(); 14 m_ur.normalize(); 15 m_dl=m_ur; 16 m_dl=-m_dl; //Reverse 17 m_dr=m_ul; 18 m_dr=-m_dr; //Reverse 19 } Computer Graphics With OpenGL 2nd Ed, F.S. Hill Jr The OpenGL Programming Guide 4th Ed Shreiner et-al