Jeff Molofee (Nehe) OpenGL Tutorial Seventh lesson Translated by Cker This lesson I will teach you how to use three different texture filtering methods. Teach you how to use the keyboard to move the object in the scene, you will also teach you to apply simple light in the OpenGL scene. This lesson contains a lot of content, if you have questions about the previous courses, go back to review. Good understanding of basic knowledge is very important before entering the following code. We still modified on the code of the first lesson. As long as before, as long as there is any big change, I will write the entire code. The program starts, let's add a few new variables first. #include
The third value is the displacement on the Z axis. In order to ensure that the light is always in front of the wooden box, we will move the position of the light source toward the observer (where you are.) Moved out the screen. We usually refer to the 0.0F point of the Z-axis in the position of the screen glass of the display. Therefore, the displacement on the z-axis is finally 2.0F. If you can see the light source, it floats in front of your monitor. Of course, if the wooden box is not behind the screen of the display, you can't see the box. "Translator Note: I appreciate NEHE patient. Sometimes I am more troublesome, what is this simple matter? But if everything is clear, you will also cover this page to see it? "The last parameter is taken at 1.0f. This will tell OpenGL here that the coordinates specified here are the location of the source, and I will explain more in the later tutorial. GLFLOAT LIGHTPSITION [] = {0.0F, 0.0F, 2.0F, 1.0F}; // Light source location (new) FILTER variable tracking display The texture type used. The first texture (Texture 0) is built using GL_NEAREST filtering. The second texture (Texture 1) uses GL_LINEAR (linear filtering) mode, the closer the screen, the smoother it is, the smoother. The third texture (Texture 2) uses the MIPMAPped filtering method, which will create a very good texture. According to our type of use, the value of the FILTER variable is equal to 0, 1 or 2, respectively. Below we start from the first texture. Gluint Texture [3] allocates storage space for three different textures. They are located in Texture [0], Texture [1] and Texture [2]. Gluint Filter; // Filter Type Gluint Texture [3]; // 3 Word Space LRESULT CALLBACK WNDPROC (HWND, UINT, WPARAM, LPARAM); // WndProc Definition Now load a bitmap, and create three kinds Different textures. This lesson uses the GLAUX auxiliary library to load the bitmap, so you should confirm whether you contain the GLAUX library when compiling. I know that Delphi and VC include the GLAUX library, but other languages cannot be guaranteed. "Translator Note: Glaux is the OpenGL auxiliary library, according to OpenGL cross-platform characteristics, the code on all platforms should be universal. However, the auxiliary library is not a formal OpenGL standard library, and does not appear on all platforms. But just available on the Win32 platform. Oh, BCB is certainly no problem. Here I only make an annotations for new code. If you have questions about a row code, check out the tutorial. That lesson explains the contents of loading and creation of textures. In the last code, the location before the resizeglscene (), we increase the following code. This is almost the same as the code loaded in the sixth lesson. AUX_RGBIMAGEREC * LOADBMP (Char * filename) // Load bitmap {file * file = null; // File handle if (! Filename) // confirm that the file name has been initialized {Return null; // No return null} file = fopen (FileName, "R"); // Check if the file is existing if (file) // exists? {Fclose (file); // Close the file handle return auxDibimageLoad (filename); / / Load bitmap and return a pointer } return null; // Load Failback Returns NULL} This code calls the previous code load bitmap and converts it to 3 textures. Status variable tracking texture has been loaded and created.
INT loadGLTextures () // Load bitmap and converts into texture {int status = false; // status indicator AUX_RGBIMAGEREC * textureImage [1]; // Create texture storage space Memset (TextureImage, 0, Sizeof (void *) * 1); // Set the pointer to NULL Now load the bitmap and convert to the texture. TextureImage [0] = loadbmp ("data / crate.bmp") calls our LoadBMP () function. Crate.BMP under the Data directory will be loaded. If everything is normal, the image data will be stored in TextureImage [0]. The Status variable is set to TRUE, we will start to create textures. / / Load bitmap, check it wrong, or exit if the bitmap does not exist. IF (TextureImage ["" DATA / CRATE.BMP ")) {status = true; // status is set to TRUE Now we have loaded image data into TextureImage [0]. We will use it to create 3 textures. The following row tells OpenGL We want to create three textures, they will be stored in Texture [0], Texture [1] and Texture [2]. GlGentextures (3, & Texture [0]); // Creating a texture in the sixth lesson we use linear filtering texture map. This requires a fairly high processing capability, but they look very good. In this lesson, we will use the GL_NEAREST method to create the first texture to create. In principle, this method is not really filtered. It only takes a small handling ability and looks very poor. The only benefit is that our project can run normally on a very fast and slow machine. You will notice that we use GL_nearest in MIN and MAG, you can mix using GL_Nearest and GL_LINEAR. Texture looks better, but we are more concerned with speed, so you can use low quality map. MIN_FILTER is employed when image drawing is less than the original size of the text. MAG_FILTER uses when image drawing is greater than the original size of the map. // create a filter map Nearest glBindTexture (GL_TEXTURE_2D, texture [0]); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // (new) glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // (new) glTexImage2D (GL_TEXTURE_2D, 0, 3, TextureImage [0] -> SIZEX, TEXTUREIMAGE [0] -> Sizey, 0, GL_RGB, GL_UNSIGNED_BYTE, TEXTUREIMAGE [0] -> DATA); the next texture is the same, linear filtering in the sixth lesson. The only difference is in TEXTURE [1] this time. Because this is the second texture. If you put in Texture [0], he will cover the GL_NEAREST texture created in front.
// Create a linear filtering texture glBindTexture (GL_TEXTURE_2D, texture [1]); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D (GL_TEXTURE_2D, 0, 3, TextureImage [0] -> sizeX, TextureImage [0] -> Sizey, 0, GL_RGB, GL_UNSIGNED_BYTE, TEXTUREIMAGE [0] -> DATA); the following is a new method for creating textures. Mipmapping! "Translator Note: I can't turn it out in this Chinese, but it doesn't matter. After reading this paragraph, you know the most important thing. "You may notice that many details will be lost when the image becomes very small on the screen. It's hard to see just now. When you tell OpenGL to create a MipMapped texture, OpenGL will attempt to create high quality textures of different sizes. When you draw a MipMapped texture to the screen, OpenGL will select the best texture (with more details) that it has created, not just zooming images (this will cause detail loss). I have said that there is a way to bypass OpenGL's restrictions on texture width and height - 64, 128, 256, and more. The way is Glubuild2DMIPMAPS. According to my discovery, you can create textures using any bitmap. OpenGL will automatically zoom it into normal size. Because it is the third texture, we save it to Texture [2]. This creation is created in this lesson. // Create MipMapped texture glBindTexture (GL_TEXTURE_2D, texture [2]); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); // (new) below mipmapped line generates texture. We use three colors (red, green, blue) to generate a 2D texture. TextureImage [0] -> SIZEX is a bitmap width, ExtureImage [0] -> Sizey is a bitmap height, GL_RGB means we use RGB colors in turn. GL_unsigned_byte means that the unit of texture data is byte. TextureImage [0] -> DATA points to the bitmap used by our creation of textures. Glubuild2DMIPMAPS (GL_Texture_2D, 3, TextureImage [0] -> Sizex, TextureImage [0] -> Sizey, GL_RGB, GL_UNSIGNED_BYTE, TEXTUREIMAGE [0] -> DATA); (new)} Now release to store the memory of the map data . We first check if you are stored in TextureImage [0], if you have, delete it. The bitmap structure is then released to ensure that the memory is released. IF (TextureImage [0]) // Whether the texture exists {if (TextureImage [0] -> DATA) // Whether the image is present {free (TextureImage [0] -> DATA); // Release the texture image occupied memory} Free (TextureImage [0]); // Release the image structure} Finally we return to the Status variable. If all OK, the status variable is true. Otherwise, False. Return status; // Returns Status Variable} You should load texture and initialize OpenGL settings.
The first line of the initGl function uses the above code load texture. After creating a texture, we call Glenable (GL_Texture_2D) Enable 2D Texture Mapping. The shadow mode is set to smooth shadow. Background is set to black, we enable depth testing, then we enable optimization perspective calculations. INT INITGL (GLVOID) // Start all settings {if (! loadGltextures ()) // jump to the texture load routine {RETURN FALSE; // If you cannot load texture Return false} glenable (GL_Texture_2D) ); // Enable texture mapping GLShademodel (GL_SMOOTH); // Enable shadow smooth GlclearColor (0.0F, 0.0F, 0.0F, 0.5F); // Black background GlcleardEpth (1.0F); // Depth Cache Set Glenable (GL_Depth_Test ); // Enable depth test GLDEPTHFUNC (GL_L_LEQUAL); // The depth test type GLHINT (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Highly optimized perspective projection calculation Now sets the light source. Below the next line, set the emission amount of the ambient light, and the light source Light1 begins to illuminate. At the beginning of this lesson, we store the emission amount of the ambient light in the Lightambient array. Now we use this array (half-brightness ambient light). GLLIGHTFV (GL_Light1, GL_AMBIENT, LIGHTAMBIENT); // Setting the ambient light Next, we set the amount of illumination of diffuse light. It is stored in the LightDiffuse array (full brightness white light). GLLIGHTFV (GL_Light1, GL_DIFFUSE, LIGHTDIFFUSE); // Sets the location of the diffuse light and set the light source. Location is stored in the LightPosition array (just in the center of the wooden box, X-0.0F, Y-0.0F, Z direction moved to the observer 2 units "outside the screen>). GLLIGHTFV (GL_Light1, GL_Position, LightPosition); // Light source location, we enable one source. We haven't enable GL_Lighting yet, so you can't see any light. Remember: Only set, position, and even enabled the light source, and the light source will not work. Unless we enable GL_Lighting. Glenable (GL_Light1); // Enable No. 1 Light Source Return True; // Initialize OK} The next section of the code draws the map cube. I only annotate the new code. If you have questions about the code that is not annotated, look back at the sixth lesson. INT Drawglscene (GLVOID) // Start all of the drawing {Glclear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear the screen and depth cache GLloadIdentity (); // Reset the current model observation matrix under three lines of code placed and rotated cube. GLTRANSLATEF (0.0F, 0.0F, Z) move the cube along the Z-axis. Glrotatef (XROT, 1.0F, 0.0F, 0.0F) rotate the cube around the X-axis XROT. GLROTATEF (YROT, 0.0F, 1.0F, 0.0F) rotate the cube around Y axis. GLTRANSLATEF (0.0F, 0.0F, z); // Move in / remove the screen Z unit glrotatef (xROT, 1.0F, 0.0F, 0.0F); // Rotate GLROTATEF around the X-axis (YROT, 0.0F, 1.0F, 0.0F);); // The row around the Y axis is similar to us in the sixth lesson. Different, this time we bind the texture is Texture [Filter], not the Texture [0] in the last lesson.
At any time, we press the F key, and the value of Filter will increase. If this value is greater than 2, the variable Filter will be reset to 0. When the program is initially, the value of the variable Filter will be set to 0. We can choose any of three textures using variables. GlbindTexture (GL_Texture_2D, Texture [Filter]); // Select the texture Glbegin (GL_QUADS) determined by Filter; // Start drawing the quadrilateral Glnormal3f is the new thing of this lesson. Normal is the meaning of the normal, the so-called normal refers to a straight line of the surface (polygon) and is perpendicular to this surface (polygon). A normal line must be specified when using the light source. The normal line tells OpenGL's orientation of this polygon and specifies the positive and back of the polygon. If there is no specified normal, what monster can happen: don't illuminate the face, the back of the polygon is also illuminated ..... By the way, the normal line should point to the outside of the polygon. Watching front of the wooden box you will notice that the normal is in the same direction. This means that the normal is pointing to the observer - yourself. This is exactly what we hope. For the back of the wooden box, as we want, the normal is back to the observer. If the cube rotates 180 degrees along the X or Y axis, the front side of the normal side is still toward the observer, the normal is back to the observer. In other words, regardless of which surface, as long as it is pointing toward the plane of the observer, it points to the observer. Since the light source is close to the observer, this face will be illuminated when the normal is against the observer. The more the normal, the brighter, the brighter is it. If you put the observation point in the inside of the cube, you will be dark in the normal. Because the normal is an outward finger. If there is no light source inside the cube, it is of course black.
// Front side GLNORMAL3F (0.0F, 0.0F, 1.0F); // normal points to the observer GLTEXCOORD2F (0.0F, 0.0F); GlvertEX3F (-1.0F, -1.0F, 1.0F); // Point 1 (Front) GLTEXCOORD2F (1.0F, 0.0F); GlvertEX3F (1.0F, -1.0F, 1.0F); // Point 2 (Front) GLTEXCOORD2F (1.0F, 1.0F); GlvertEX3F (1.0F, 1.0F, 1.0 f); // Point 3 (Front) GLTEXCOORD2F (0.0F, 1.0F); GlvertEX3F (-1.0F, 1.0F, 1.0F); // Point 4 (Front) // back side Glnormal3f (0.0F, 0.0F , -1.0F); // Number back to the observer Gltexcoord2f (1.0F, 0.0F); GlvertEX3F (-1.0F, -1.0F, -1.0F); // Point 1 (back) GltexcoRD2F (1.0F, 1.0F); GlvertEX3F (-1.0F, 1.0F, -1.0F); // Point 2 (back) gltexcoord2f (0.0F, 1.0F); GlvertEX3F (1.0F, 1.0F, -1.0F); // Point 3 (Back) Gltexcoord2f (0.0F, 0.0F); GlvertEX3F (1.0F, -1.0F, -1.0F); // Point 4 (BACK) // top Glnormal3f (0.0F, 1.0F, 0.0F); // The normal line up GLTEXCOORD2F (0.0F, 1.0F); GlvertEX3F (-1.0F, 1.0F, -1.0F); // Point 1 (TOP) GLTEXCOORD2F (0.0F, 0.0F); GlvertEX3F (-1.0F, 1.0F, 1.0F); // Point 2 (TOP) GLTEXCOORD2F (1.0F, 0.0F); GLVERTEX3F (1.0F, 1.0F, 1.0F); // Point 3 (TOP) GLTEXCOORD2F (1.0F, 1.0F ); GLVERTEX3F (1.0F, 1.0F, -1.0F); // Point 4 (TOP) // bottom Glnormal3f (0.0F, -1.0F, 0.0F); // Fair face down GLTEXCOORD2F (1.0F, 1.0 f); GlvertEX3F (-1.0F, -1.0F, -1.0F); // Point 1 (Bottom) Gltexcoord2f (0.0F, 1.0F); GlvertEX3F (1.0F, -1.0F, -1.0F); // Point 2 (Bottom) Gltexcoord2f (0.0F, 0.0F); GlvertEX3F (1.0F, -1.0F, 1.0F); // Point 3 (Bottom) Gltexcoord2f (1.0F, 0.0F); GlvertEX3F (-1.0F, - 1.0F, 1.0F); // Point 4 (Bottom) // Right side Glnormal3f (1.0F, 0.0F, 0.0F); // Fair towards the right gltexcoord2f (1.0F, 0.0F); GLVERTEX3F (1.0F , -1.0F, -1.0F); // Point 1 (Right) GLTEXCOORD2F (1.0F, 1.0F); GLVERTEX3F (1.0F, 1.0F, -1.0F); // Point 2 (Right) Gltexco Coord2f (0.0F 1.0F); GlvertEX3F (1.0F, 1.0F, 1.0F); // Point 3 (Right) GltexcoRD2F (0.0F, 0.0F); GlvertEX3F (1.0F, -1.0F, 1.0F);
// Point 4 (Right) // left side Glnormal3f (-1.0F, 0.0F, 0.0F); // The normal is toward left GLTEXCOORD2F (0.0F, 0.0F); GLVERTEX3F (-1.0F, -1.0F, -1.0F); // Point 1 (Left) gltexcoord2f (1.0F, 0.0F); GlvertEX3F (-1.0F, -1.0F, 1.0F); // Point 2 (Left) GLTEXCOORD2F (1.0F, 1.0F) GlvertEX3F (-1.0F, 1.0F, 1.0F); // Point 3 (Left) GltexcoRD2F (0.0F, 1.0F); GLVERTEX3F (-1.0F, 1.0F, -1.0F); // Point 4 (Left 4 ) glend (); // quadrilateral Drawing End Two lines of code add XSpeed and YSPEED units, respectively. The larger the value of xSpeed and YSPEED, the faster the cube is turning. XROT = xspeed; // xRot Add XSpeed unit YROT = YSPEED; // YROT Add YSPEED unit return true;} Now transfer to WinMain () main function. We will increase the switch light, rotate the wooden box, and switch the filter, and the control code that moves the wooden box is removed. You will see the SwapBuffers (HDC) line code in a place close to the winmain () function. Then add the following code after this line. The code will check if the L button is pressed. If the L key is pressed, the value of LP is not false, meaning the L button has not been released, and what will not happen at this time. SwapBuffers (HDC); // Exchange Cache (Dual Cache) IF (Keys ['L'] &&! Lp) // L button has been pressed and released? {If the value of LP is false, it means the l key Not pressed yet, or already released, then LP will be set to TRUE. At the same time, the reason for these two conditions is to prevent the L key from being pressed, and this code is repeated and causing the form to keep flashing. After the LP is set to True, the computer knows that the L key is pressed, and we can switch the on / off of the light source accordingly: Boolean variable light controls the switch of the source. LP = true; // LP is set to true light =! light; / / Switch the light source True / false below to check if the light source should be opened and the value of the Light variable. If (! Light) // If there is no light source {GLDISABLE (GL_Lighting); // Disable Light source} else // OtherWise {Glenable (GL_Lighting); // Enable Light Source}} The following code is released whether the "L" key is released. If loosen, the variable LP will be set to false. This means that the "L" button is not pressed. If you don't have this check, after the light source is opened for the first time, it can't turn off again. The computer has always pressed as the "L" button. IF (! keys ['l']) // L button open? {lp = false; // If, set the LP to false} and then the "F" key as a similar check. If there is a "F" button and the "F" button is not in the state or it never presses it, set the variable FP to True. This means that this button is pressed. The Filter variable is then added to one. If the Filter variable is greater than 2 (because the arrays of our usage are Texture [3], the texture of greater than 2 does not exist, we reset the Filter variable of 0.
IF (Keys ['F'] &&! fp) // F button presses? {fp = true; // FP is set to true filter = 1; // Filter value plus one IF (Filter> 2) / / Is it greater than 2? {Filter = 0; // If it is reset to 0}} if (! Keys ['f']) // F button let go? {Fp = false; // If FP is set to False} These four lines check if the PageUp button is pressed. If it is, the value of the z variable is reduced. This way to call the GLTranslateF (0.0F, 0.0F, Z) contained in the Drawglscene function will make the wooden box from the observer. IF (Keys [vk_prior]) // PageUp Press? {z- = 0.02f; // If you press it, move the wooden box to the screen inside. } After four lines check if the PageDown button is pressed. If it is, the value of the z variable is added. This way, the GLTRANSLATEF (0.0F, 0.0F, Z) call included in the Drawglscene function will cause the wooden box to move close to the observer. IF (Keys [vk_next]) // PageDown presses? {z = 0.02f; // If you press it, move the wooden box to the observer. } Now check the arrow keys. Press the left and right arrow keys XSPEEDs to decrease or increase. Press the up and down direction button YSPEED to decrease or increase. Remember that if the value of XSpeed, YSPEED is incremented in the later tutorial, the cube is faster. If you have been pressing a direction button, the faster the cube turn in that direction. IF (Keys [vk_up]) // UP direction button presses? {xspeed- = 0.01f; // If it is, reduce XSPEED} IF (Keys [vk_down]) // DOWN direction button presses? {xspeed = 0.01f; // If yes, add xspeed} f (keys [vk_right]) // Right direction button presses? {Yspeed = 0.01f; // If yes, increase yspeed} if (Keys [vk_left]) /// What is the Left direction button to press? {Yspeed- = 0.01f; // If it is, reduce YSPEED} Like the first few lessons, we will finally need to correct the title of the form. IF (Keys [vk_f1]) // F1 presses? {keys [vk_f1] = false; // If you set it to false killglwindow (); // destroy the current window fullscreen =! fullscreen; // Switch full screen / Window mode // Reconstruction GL window if ("" CreateGLWindow ("Nehe's Textures, Lighting & Keyboard Tutorial", 640, 480, 16, fullscreen) {return 0; // If you cannot create a window, the program exits}}}} // Off KillglWindow (); // Destroy window return (msg.wparam); // After this class is over, you should learn to create and use these three different texture mapping filtration methods. And use the objects in the keyboard and the object in the scene. Finally, you should learn to apply simple light sources in the scene, making the scene look more realistic. "Translator: NEHE's document seems very simple, it seems very rushed. But if you want to come to such a master, have you seen a few? Or that sentence, I am not a master, I hope you are sincere. Here is the source code download link.