Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 25 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
25
Dung lượng
626,5 KB
Nội dung
for you. This works in situations where the texture coordinates can be determined using well-defined mathematical steps. Examples include reflections, contouring, and projective texturing. We’ll be discussing a couple of specific examples here. Texture coordinate generation is controlled independently for each coordinate. To use it, you must first enable it by passing GL_TEXTURE_GEN_S , GL_TEXTURE_GEN_T , GL_TEXTURE_GEN_R ,or GL_TEXTURE_GEN_Q (each corresponding to the indicated coordinate) to glEnable() . To specify how texture coordinates are generated, you use one of the following: void glTexGen{ifd}(GLenum coord, GLenum pname, TYPE param); void glTexGen{ifd}v(GLenum coord, GLenum pname, const TYPE *params); coord indicates which texture coordinate to apply the parameter to. Valid values are GL_S , GL_T , GL_R , and GL_Q , corresponding to the s, t, r, and q texture coordinates. The accepted values of pname and the param or params associated with them are listed in Table 9.1. If the texture generation mode is GL_OBJECT_LINEAR , then the texture coordinates are gener- ated using the following equation: texcoord = p 1 * x o + p 2 * y o + p 3 * z o + p 4 * w o x o ,y o ,z o , and w o are the object-space coordinates of the vertex the texture coordinate is being generated for. p 1 ,p 2 ,p 3 , and p 4 are the four parameters provided via GL_OBJECT_PLANE . These are used to pass the A, B, C, and D coefficients of a plane, so this equation is in effect calculating the distance from the plane and using that as the texture coordinate. The GL_EYE_LINEAR texture generation mode uses a similar equation, except that eye-space vertex coordinates are used, and the inverse modelview matrix is applied to the plane parameters when they are specified. Texture Coordinate Generation 203 Table 9.1 Texture Generation Parameters Parameter Meaning GL_TEXTURE_GEN_MODE param specifies the texture generation mode, which must be GL_OBJECT_LINEAR , GL_EYE_LINEAR , GL_SPHERE_MAP , GL_REFLECTION_MAP *, or GL_NORMAL_MAP *. GL_OBJECT_PLANE params is a pointer to a four-element array of values that are used as parameters for the texture coordinate generation function. Used in conjunction with GL_OBJECT_LINEAR . GL_EYE_PLANE Same as above, but used with GL_EYE_LINEAR . * Available only via the ARB_texture_cube_map extension under Windows. 09 BOGL_GP CH09 3/1/04 10:04 AM Page 203 TLFeBOOK When using the GL_NORMAL_MAP mode, the (s, t, r) texture coordinates are generated by using the vertex’s normal, transformed into eye-space. These are intended to be used with cube maps. The remaining two texture generation modes will be covered in the next section, but for now, let’s look at an example of generating texture coordinates based on the distance from a plane. When rendering terrain, you can create a visually appealing effect by varying the color based on the height of the terrain. Areas close to sea level appear as sand; slightly higher areas appear as grass, and then dirt or rock, and finally snow on the highest peaks. One way to achieve this is to create a one-dimensional texture like the one shown in Figure 9.1. Then, you would enable texture generation based on the height above sea level by passing the coef- ficients for the sea level plane to glTexGen() and enable texture generation for the s coordi- nate. Later in this chapter, in the “Multitexturing” section, you’ll see a demo that does exactly that, so let’s look at the code that sets up and uses texture coordinate generation. First, during initialization, texture coordinate generation is enabled, GL_OBJECT_LINEAR mode is selected, and the reference plane is passed via GL_OBJECT_PLANE : glEnable(GL_TEXTURE_GEN_S); glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); GLfloat waterPlane[] = { 0.0, 1.0, 0.0, -WATER_HEIGHT }; glTexGenfv(GL_S, GL_OBJECT_PLANE, waterPlane); Then, when actually rendering the terrain, the only thing that needs to be done is to scale the generated texture coordinate. This is because the generated coordinate is the distance from the plane, which could be any value. We want the texture coordinates to fall in the range [0, 1] so that the lowest points correspond to sand and the highest points corre- spond to snow. This is simply a matter of dividing the texture coordinate by the maximum terrain height. Using the texture matrix you learned about in the last section, this is done as follows: glMatrixMode(GL_TEXTURE); glLoadIdentity(); glScalef(1.0f/MAX_HEIGHT, 1.0, 1.0); Chapter 9 ■ More on Texture Mapping204 Figure 9.1 A 1D texture used to color terrain. 09 BOGL_GP CH09 3/1/04 10:04 AM Page 204 TLFeBOOK That’s all there is to it. Similar methods can be used for creating contours or projective textures. Now let’s look at the other two texture generation modes. Environment Mapping If you’ve ever tried to model things like chrome, polished metal, or glass, you know that no matter how you tweak the materials or what texture you use, it doesn’t look very much like the real thing. This is because all of these things reflect the environment they are in, so to model them correctly, you need to use environment mapping.The GL_SPHERE_MAP and GL_REFLECTION_MAP texture generation modes can be used in conjunction with an appropri- ate texture map to create realistic reflections. For the GL_SPHERE_MAP mode, or sphere mapping, the texture coordinates are generated using a vector from the eye to the point on the surface and the surface normal (trans- formed into eye space) to create a reflection vector. The reflection vector is used to calcu- late the texture coordinates. These coordinates are then used to index into a 2D texture map like that shown in Figure 9.2. The image is a picture of a sphere completely reflect- ing the world around it. Both the s and t coordinates need to be generated via sphere map- ping to have it work correctly. Sphere mapping comes with many drawbacks, one of the most significant being that it’s view depen- dent, so viewing a reflective object from anywhere other than the center of projection can produce incorrect results. They also tend to not look very accurate on objects that aren’t roughly spherical. Finally, obtaining the texture image in the first place presents a challenge. Traditionally, they were obtained by taking a photograph of a perfectly reflective sphere placed in the room that is being modeled. For this to be completely mathematically correct, the camera needs to be infinitely far away, but since this is impossible, a fish eye lens is used instead to get results that are reasonably close. This approach isn’t really viable for game environments. Another drawback is that the reflection won’t pick up any objects moving in the world, so it will be immediately obvious that the surface isn’t really reflective. An alternative way to generate the texture image is to render the world six times (once for each direction) from the reflective object’s perspective. The results are then stored in the six faces of a cube map, which is then applied to a sphere. This is actually how the image in Figure 9.2 was generated. This approach is much better, since a reflection image can be generated anywhere in your world, and it can be updated dynamically to reflect objects in Texture Coordinate Generation 205 Figure 9.2 A typical texture used with GL_SPHERE_MAP . 09 BOGL_GP CH09 3/1/04 10:04 AM Page 205 TLFeBOOK motion. However, as you’re about to see, you can make use of the cube map directly, so the additional step of generating a sphere map is wasteful. Cube maps are also view inde- pendent and can be easily mapped onto any objects. For these reasons, sphere mapping is generally not used anymore. When cube map textures were introduced to OpenGL, they brought with them the GL_REFLECTION_MAP texture coordinate generation mode. The texture coordinates are gener- ated in a manner similar to GL_SPHERE_MAP , except that instead of creating s and t coordi- nates for a 2D texture, s, t, and r coordinates are generated that are used to index into the faces of the cube map. Cube maps are much easier to update dynamically and do a better job of capturing the entire environment. The example in the next section shows you how cube maps can be used for reflections. Example: Reflective Cube Mapping On the CD, you’ll find an example program for this chapter entitled EnvironmentMap- ping, that puts reflective cube maps to use. As you can see from Figure 9.3, this program shows a reflective sphere that is being orbited by two colored balls, placed in an outdoor environment. In the “Copying from the Color Buffer” section earlier in this chapter, you saw the portion of this code that creates the faces of the cube map texture by Chapter 9 ■ More on Texture Mapping206 Figure 9.3 A reflective sphere, made possible with cube maps and reflection mapping. 09 BOGL_GP CH09 3/1/04 10:04 AM Page 206 TLFeBOOK rendering the scene six times from the perspective of the sphere. The cube map is then applied to the sphere using texture coordinate generation. To do this, the texture genera- tion mode is first set up during initialization, as follows: glTexGenf(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP); glTexGenf(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP); glTexGenf(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP); Then, to actually apply the texture to the sphere, texture coordinate generation for the s, t and r coordinates has to be enabled. This happens in the Render() method, as shown below: void CGfxOpenGL::Render() { GenerateEnvTexture(); glClear(GL_DEPTH_BUFFER_BIT); glLoadIdentity(); m_skybox.Render(0.0, 0.0, 0.0); glTranslatef(0.0, 0.0, -5.0); GLfloat lightPos[] = { 0.5f, 0.5, 1.0, 0.0 }; glLightfv(GL_LIGHT0, GL_POSITION, lightPos); glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_GEN_T); glEnable(GL_TEXTURE_GEN_R); glEnable(GL_TEXTURE_CUBE_MAP); glBindTexture(GL_TEXTURE_CUBE_MAP, m_envTexID); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor3f(1.0f, 1.0f, 1.0f); gluSphere(m_pObj, 1.0, 64, 64); glDisable(GL_TEXTURE_CUBE_MAP); glDisable(GL_TEXTURE_GEN_S); glDisable(GL_TEXTURE_GEN_T); glDisable(GL_TEXTURE_GEN_R); DrawBalls(); } Texture Coordinate Generation 207 09 BOGL_GP CH09 3/1/04 10:04 AM Page 207 TLFeBOOK As you can see, creating dynamic reflections with cube maps is quite easy. The cost involved is not trivial because it requires that the scene be rendered an additional six times per reflective object. When used in moderation however, the visual payoff is worth it. Multitexturing Extension Extension name: ARB_multitexture Name string: GL_ARB_multitexture Promoted to core: OpenGL 1.2.1 Function names: glActiveTextureARB() , glMultiTexCoord{1234}{sifd}[v]ARB() Tokens: GL_TEXTUREn_ARB , GL_MAX_TEXTURE_UNITS_ARB In the examples you’ve seen so far, when you texture-map a polygon, you apply only one texture to it. It’s actually possible to apply several textures to the same polygon through a series of texture operations. This is called multitexturing. Up to this point, the textures you’ve seen assign colors to the polygons they are applied to. Textures used in this way are often referred to as diffuse maps. When using multitexturing, typically only one of the textures will be used in this way. The other textures will be used to either modify the diffuse map values or provide additional information. For example, grayscale images can be used to modulate the diffuse color to simulate per pixel lighting or to vary the details. A texture may include normals or other parameters encoded as RGBA values that are used to perform calculations to simulate bumpy surfaces. You’ll see some specific examples of multitexturing in this section, but we’ll be giving you only a small taste of the many possibilities they offer. Multitexturing makes use of a series of texture units. Each texture unit represents a single texture application, and when you perform multitexturing, each texture unit passes its results to the next texture unit as shown in Figure 9.4. You’ve actually been making use of texture units all along; everything you’ve done so far has used the default texture unit (which is texture unit 0). Let’s look more closely at what texture units represent and see how to use them. Texture Units Each texture unit has a set of states associated with it that allows it to keep settings sepa- rate from the other texture units. Each texture unit has its own texture environment, tex- ture matrix stack, texture coordinate generation states, and texture image and filtering Chapter 9 ■ More on Texture Mapping208 09 BOGL_GP CH09 3/1/04 10:04 AM Page 208 TLFeBOOK parameters. The latter two are usually derived from the texture object that is bound to the texture unit. In addition, each of the texture targets ( GL_TEXTURE_1D , GL_TEXTURE_2D , GL_TEX- TURE_3D , and GL_TEXTURE_CUBE_MAP ) are enabled or disabled on a per–texture-unit basis. You use the glActiveTexture() function to change the currently active texture unit. It is defined as: void glActiveTexture (GLenum texUnit); After this function is called, all calls to glTexImage() (including the copy and subimage ver- sions), glTexParameter() , glTexEnv() , glTexGen() , and glBindTexture() affect the texture unit defined in texUnit .The texUnit parameter is of the form GL_TEXTUREn ,where n is equal to any integer between 0 and 1 less than the number of supported texture units. For example, GL_TEXTURE0 is for the first texture unit available.You can find out how many texture units are supported by your OpenGL implementation by using GL_MAX_TEXTURE_UNITS , as follows: int maxTexUnits; // holds the maximum number of supported texture units glGetIntegerv(GL_MAX_TEXTURE_UNITS, &maxTexUnits); If glGetIntegerv() returns 1, then the OpenGL implementation does not support multitexturing. Any texture object can be used with any texture unit. When you make a call to glBindTexture() , the texture object gets bound to the currently active texture unit, and its parameters and image data become the texture unit’s parameters and texture data. To enable a texture unit, you simply have to assign valid texture data to it and then enable the appropriate texture target. To later disable it, you need to make sure that all texture tar- gets are disabled. Always remember that OpenGL is a state machine, so the current tex- ture unit will always be whatever you set it to the last time you called glActiveTexture() . Multitexturing 209 Figure 9.4 Texture unit pipeline. 09 BOGL_GP CH09 3/1/04 10:04 AM Page 209 TLFeBOOK Take care to always be aware of what the active texture unit is when making calls to any texture related functions. The following example should help you to better understand how to use texture units. Assuming that texture1 and texture2 are handles to valid texture objects for 2D textures, the following code binds them to texture units 0 and 1: glActiveTexture(GL_TEXTURE0); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, texture1); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glActiveTexture(GL_TEXTURE1); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, texture2); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); Pay particular attention to the calls to glTexEnv() . The first one causes the color from texture1 to replace the incoming fragment color. This value is then used as the input to texture unit 1, which modulates the value with the color in texture2 . Specifying Texture Coordinates Now that you know how to assign textures to each texture unit and configure the texture units’ states, you need to define how to apply the textures to polygons. Because you are applying more than one texture to a single polygon, you’ll need to define more than one set of texture coordinates as well. In fact, you’ll need one set of texture coordinates for each texture unit that you create. glTexCoord() isn’t up to the task, because it only specifies coordinates for texture unit 0—it completely ignores the active texture unit. Instead, you’ll need to use glMultiTexCoord() : void glMultiTexCoord{1234}{sifd}(GLenum texUnit, TYPE coords); void glMultiTexCoord{1234}{sifd}v(GLenum texUnit, const TYPE *coords); texUnit is used to indicate which texture unit this coordinate is for. It uses the same GL_TEXTUREn values as glActiveTexture() . The parameters are otherwise the same as glTexCoord() . In fact, using glTexCoord() is equivalent to using glMutliTexCoord() with GL_TEXTURE0 . Example: Multitextured Terrain It’s time to take a look at a real example of using multitexturing. On the CD, you’ll find a demo for this chapter entitled MultitexTerrain, shown in Figure 9.5. This example uses one texture to color the terrain based on the height above sea level, as described earlier under “Texture Coordinate Generation.” This is combined with a second grayscale texture that contains grass-like detail. Chapter 9 ■ More on Texture Mapping210 09 BOGL_GP CH09 3/1/04 10:04 AM Page 210 TLFeBOOK This is the how the textures are initialized: image.Load(“grass.tga”); glGenTextures(1, &m_grassTexture); glBindTexture(GL_TEXTURE_2D, m_grassTexture); gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, image.GetWidth(), image.GetHeight(), GL_RGB, GL_UNSIGNED_BYTE, image.GetImage()); image.Release(); image.Load(“water.tga”); glGenTextures(1, &m_waterTexture); glBindTexture(GL_TEXTURE_2D, m_waterTexture); gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, image.GetWidth(), image.GetHeight(), GL_RGB, GL_UNSIGNED_BYTE, image.GetImage()); image.Release(); image.Load(“height.tga”); glGenTextures(1, &m_heightTexture); glBindTexture(GL_TEXTURE_1D, m_heightTexture); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB, image.GetWidth(), 0, GL_RGB, Multitexturing 211 Figure 9.5 Terrain demo modified to use multitexturing. 09 BOGL_GP CH09 3/1/04 10:04 AM Page 211 TLFeBOOK GL_UNSIGNED_BYTE, image.GetImage()); image.Release(); glActiveTexture(GL_TEXTURE1); glEnable(GL_TEXTURE_GEN_S); glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); GLfloat waterPlane[] = { 0.0, 1.0, 0.0, -WATER_HEIGHT }; glTexGenfv(GL_S, GL_OBJECT_PLANE, waterPlane); glActiveTexture(GL_TEXTURE0); The interesting thing to notice here is that most of the texture creation code doesn’t have to concern itself with the currently active texture unit. This is because the parameters and images are being bound to texture objects, which will later be bound to texture units as they are needed. The texture unit matters only at the end, when texture coordinate gener- ation is enabled. Next up is the code that draws the terrain: glBindTexture(GL_TEXTURE_2D, m_grassTexture); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_1D, m_heightTexture); glMatrixMode(GL_TEXTURE); glLoadIdentity(); glScalef(1.0f/MAX_HEIGHT, 1.0, 1.0); glMatrixMode(GL_MODELVIEW); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glEnable(GL_TEXTURE_1D); for (int z = 0; z < TERRAIN_SIZE - 1; ++z) { glBegin(GL_TRIANGLE_STRIP); for (int x = 0; x < TERRAIN_SIZE; ++x) { GLfloat scaledHeight = heightmap[z * TERRAIN_SIZE + x] / SCALE_FACTOR; GLfloat nextScaledHeight = heightmap[(z + 1)*TERRAIN_SIZE + x]/SCALE_FACTOR; glMultiTexCoord2f(GL_TEXTURE0, x * TC_SCALE, z * TC_SCALE); glVertex3f((GLfloat)x - TERRAIN_SIZE/2, scaledHeight, (GLfloat)z - TERRAIN_SIZE/2); Chapter 9 ■ More on Texture Mapping212 09 BOGL_GP CH09 3/1/04 10:04 AM Page 212 TLFeBOOK [...]... Windows The ARB_texture_env_combine and ARB_texture_env_dot3 extensions were added in OpenGL 1.3, and ARB_texture_env_crossbar was added in OpenGL 1.4 In Chapter 7, you learned about various texture modes that can be selected by using glTexEnv() In addition to the modes you’ve been using such as GL_MODULATE and GL_REPLACE, OpenGL supports a number of functions that were introduced in 1.3 and 1.4 for use... is updated over time so that the first image gradually fades into the second image This can be see in the Prepare() and Render() routines: void CGfxOpenGL::Prepare(float dt) { m_interpol += dt/TOTAL_TIME; if (m_interpol > 1.0) m_interpol = 1.0; } void CGfxOpenGL::Render() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); GLfloat texEnvColor[] = { 0.0, 0.0, 0.0, 0.0 }; TLFeBOOK... interpolation using texture combiners TLFeBOOK Summary 219 Summary You’ve now seen just how powerful OpenGL s texturing support really is You can update textures dynamically, even using the screen as an image source You can apply transformations to texture coordinates to move, scale, or rotate them on the fly You can have OpenGL generate texture coordinates automatically for reflections and other effects Most... Your Own 1 Write the code to set up a texture combiner that does the same thing as the GL_ADD environment mode described in Chapter 7 TLFeBOOK This page intentionally left blank TLFeBOOK chapter 10 Up Your Performance I n many graphics applications, and in virtually all games, maintaining an interactive frame rate and smooth animation is of utmost importance Although rapid advancements in graphics hardware... that, through the graphics API, harnesses the full power of the underlying hardware In this chapter, you’ll learn several methods for improving your game s performance ■ ■ ■ Display lists Vertex arrays Frustum culling Display Lists After you’ve been writing OpenGL code for a while, you’ll probably notice that there are sections of code that you are calling frequently, with the same state machine settings... glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, m_texID[1]); image.Load(“1.tga”); gluBuild2DMipmaps(GL_TEXTURE_2D, 3, image.GetWidth(), image.GetHeight(), GL_RGB, TLFeBOOK Multitexturing 2 17 GL_UNSIGNED_BYTE, image.GetImage()); image.Release(); glEnable(GL_TEXTURE_2D); // set the combine mode glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); // use the interpolate combiner function... textures from the screen using glCopyTexImage() or glCopyTexSubImage() A texture matrix is applied to texture coordinates just as the modelview and projection matrices are applied to vertex coordinates OpenGL can automatically generate texture coordinates for you with glTexGen() Environment mapping enables you to create complex reflective surfaces Multitexturing is the process of applying more than one... and then send the preprocessed commands down the graphics pipeline, storing them on the video card for future use, rather than processing them all from scratch every time? That’s exactly the idea behind OpenGL s display lists As you’ll see momentarily, display lists are quite easy to create and use; the only catch to using them is that it’s not always obvious when they will help improve performance In... these calls into a display list? Creating a Display List First of all, before you can place items in a display list, you must get a name for one, much as you got a name for a texture object in Chapter 7, “Texture Mapping.” This is done by using glGenLists(): GLuint glGenLists(GLsizei range); Here, range is the number of display lists you need The function returns an unsigned integer representing the... accessed by adding one to this value, and so on You can think of the values returned by glGenLists() as the names, or IDs, of your display lists They just provide a unique handle that allows you to tell OpenGL which display list you are currently working with You should always check the return value of glGenLists() to make sure that it is not 0 This is not a valid list name, and it indicates that some . ARB_texture_env_combine and ARB_texture_env_dot3 extensions were added in OpenGL 1.3, and ARB_texture_env_crossbar was added in OpenGL 1.4. In Chapter 7, you learned about various texture modes that can be selected. 64); glDisable(GL_TEXTURE_CUBE_MAP); glDisable(GL_TEXTURE_GEN_S); glDisable(GL_TEXTURE_GEN_T); glDisable(GL_TEXTURE_GEN_R); DrawBalls(); } Texture Coordinate Generation 2 07 09 BOGL_GP CH09 3/1/04 10:04 AM Page 2 07 TLFeBOOK As you can see, creating dynamic reflections with cube maps is quite. Prepare() and Render() routines: void CGfxOpenGL::Prepare(float dt) { m_interpol += dt/TOTAL_TIME; if (m_interpol > 1.0) m_interpol = 1.0; } void CGfxOpenGL::Render() { glClear(GL_COLOR_BUFFER_BIT