{
Welcome to lesson nine. By now, you should have a good understanding of OpenGL.
"CKER: If not, it must be my translator's fault...".
(Myling added: My sin is even greater, haha)
You've learned every detail of setting up an OpenGL window.
Learn to map and add light and color mixing (transparency) to rotating objects.
This lesson should be considered an intermediate tutorial.
You will learn how to move a bitmap around a 3D scene and remove black pixels from the bitmap (using color blending).
Next, color the black and white textures, and finally you'll learn to create rich colors,
And mix textures with different colors with each other to get a simple animation effect.
We will make modifications based on the code from the first lesson. First add a few variables at the beginning of the program source code.
I rewrote the entire code for clarity.
}
Var
h_RC: HGLRC; // Rendering Context (shading description table).
h_DC: HDC; // Device Context (device description table)
h_Wnd: HWND; // window handle
h_Instance: HINST; // Program Instance (instance).
keys : Array[0..255] Of Boolean; // Array for keyboard routines
{The following lines are newly added.
twinkle and tp are Boolean variables, which means they can only be set to TRUE or FALSE.
twinkle is used to track whether the flicker effect is enabled.
tp is used to check whether the 'T' key is pressed or released.
(tp=TRUE when pressed, tp=FALSE when released).}
twinkle : Boolean; // Twinkle stars (new)
tp : Boolean; // Is 'T' pressed? (new)
{Now let's create a structure.
The word structure sounds scary, but it’s not. (This is the record type of Delphi)
A structure uses a set of simple types of data (and variables, etc.) to express a larger combination of similar data.
We know we are keeping track of the stars.
You can see the stars below;
Each star has three integer color values. One red (r), one green (g), and one blue (b).
Additionally, each star is at a different distance from the center of the screen,
And it can be any angle in 360 degrees with the center of the screen as the origin.
A floating point number of dist to keep track of distance.
A floating point number of angle keeps track of the angle value of the star.
So we used a set of data to describe the color, distance, and angle of the stars on the screen.
Unfortunately we are tracking more than one star.
But there is no need to create 50 red values, 50 green values, 50 blue values, 50 distance values
and 50 angle values, and just create an array star. }
Type
stars = Record //Create a structure for stars, name the structure stars
r, g, b: integer; // color of stars
dist: GLfloat; // The distance of the star from the center
angle: GLfloat; // The angle of the current star
End;
Var
star : Array[0..49] Of stars; // Use the 'stars' structure to generate a 'star' array containing 50 elements
{Next we set up several tracking variables:
The distance variable (zoom) of the star from the observer,
The angle (tilt) at which we see the stars,
and the variable spin that causes the twinkling star to rotate around the Z-axis.
The loop variable is used to draw 50 stars.
texture[1] is used to store a black and white texture.
If you need more textures,
You should increase the size of the texture array to the number of textures you decide to use.
}
zoom : GLfloat = -15.0; // The distance of the star from the observer
tilt : GLfloat = 90.0; // The tilt of the star
spin : GLfloat; // The rotation of the twinkling star
loop : GLuint; // Global l Loop variable
texture : Array[0..1] Of GLuint; // Store a texture
PRocedure glGenTextures(n: GLsizei; Var textures: GLuint); stdcall; external
opengl32;
Procedure glBindTexture(target: GLenum; texture: GLuint); stdcall; external
opengl32;
{
The code immediately above is the code we use to load the texture.
I'm not going to explain this code in detail.
This is exactly the same code we used in Lessons 6, 7, and 8.
The bitmap loaded this time is called star.bmp.
Here we use glGenTextures(1, &texture[0]),
to generate a texture. The texture uses linear filtering.
}
Function LoadTexture: boolean; //Load the bitmap and convert it into a texture
Var
Status : boolean; // Status indicator
TextureImage : Array[0..1] Of PTAUX_RGBImageRec; // Create texture storage space
Begin
Status := false;
ZeroMemory(@TextureImage, sizeof(TextureImage)); // Set pointer to NULL
TextureImage[0] := LoadBMP('Star.bmp');
If TextureImage[0] <> Nil Then
Begin
Status := TRUE; // Set Status to TRUE
glGenTextures(1, texture[0]); // Create texture
// Create Nearest filter map
glBindTexture(GL_TEXTURE_2D, texture[0]);
// Generate texture
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);
End;
If assigned(TextureImage[0]) Then // Whether the texture exists
If assigned(TextureImage[0].data) Then // Whether the texture image exists
TextureImage[0].data := Nil; // Release the memory occupied by the texture image
TextureImage[0] := Nil; // Release the image structure
result := Status; // Return Status
End;
{Set the OpenGL rendering mode in glInit(). We are not going to use depth testing here,
If you use the code from Lesson 1,
Please confirm whether glDepthFunc(GL_LEQUAL) and glEnable(GL_DEPTH_TEST) have been removed.
Otherwise, the results you'll see will be a mess.
Here we use texture mapping,
So please make sure you have added any code that was not included in the first lesson.
You'll notice that we enabled texture mapping by mixing colors.
}
Procedure glInit();
Begin
If (Not LoadTexture) Then // Call the texture loading subroutine (new)
exit; // If failed to load, exit (new)
glEnable(GL_TEXTURE_2D); // Enable texture mapping
glShadeModel(GL_SMOOTH); // Enable shadow smoothing
glClearColor(0.0, 0.0, 0.0, 0.5); // black background
glClearDepth(1.0); //Set the depth buffer
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Really fine perspective correction
glBlendFunc(GL_SRC_ALPHA, GL_ONE); // Set the color mixing function to achieve a translucent effect
glEnable(GL_BLEND); // Enable color blending
{The following is the new code.
The starting angle, distance, and color of each star are set.
You'll notice how easy it is to modify a structure's properties.
All 50 stars will be cycled through.
To change the angle of star[1] all we have to do is star[1].angle=a certain value;
It's that simple! }
For loop := 0 To 49 Do // Create a loop to set all stars
Begin
star[loop].angle := 0.0; // All stars start from zero angle
{The distance of the loop-th star from the center is divided by the value of loop by the total number of stars, and then multiplied by 5.0.
Basically this makes the latter star a little further from the center than the previous star.
In this way, when loop is 50 (the last star), loop divided by num is exactly 1.0.
The reason why we need to multiply by 5.0 is because 1.0*5.0 is 5.0.
『CKER: Nonsense, nonsense! Why does this foreigner look like Kong Yiji! :)』
5.0 is already very close to the edge of the screen. I don’t want the stars to fly out of the screen, so 5.0 is the best choice.
Of course, if you set the scene deeper into the screen,
Maybe you could use a value greater than 5.0, but the stars would look smaller (all because of perspective).
You'll also notice that each star's color is a random number from 0 to 255.
You may be wondering why the color value range here is not OpenGL's usual range of 0.0 to 1.0.
The color setting function we use here is glColor4ub instead of the previous glColor4f.
ub means that the parameter is of type Unsigned Byte.
The value range of a byte is 0~255.
It seems easier to use byte value to get a random integer than to get a floating point random number.
}
star[loop].dist := (Trunc(loop) / 50) * 5.0; // Calculate the distance of the star from the center
star[loop].r := random(256); // Set a random red component for star[loop]
star[loop].g := random(256); // Set a random red component for star[loop]
star[loop].b := random(256); // Set a random red component for star[loop]
End;
End;
{
Now we turn to glDraw() drawing code.
If you are using the code from Lesson 1, delete the old DrawGLScene code and simply copy the code below.
In fact, the code for the first lesson only has two lines, so there isn't much to cut out.
}
Procedure glDraw();
Begin
glClear(GL_COLOR_BUFFER_BIT Or GL_DEPTH_BUFFER_BIT); // Clear screen and depth buffer
glBindTexture(GL_TEXTURE_2D, texture[0]); // Select texture
For loop := 0 To 49 Do // Loop to set all stars
Begin
glLoadIdentity(); // Before drawing each star, reset the model observation matrix
glTranslatef(0.0, 0.0, zoom); // Go deeper into the screen (using the value of 'zoom')
glRotatef(tilt, 1.0, 0.0, 0.0); // Tilt viewing angle (using the value of 'tilt')
{
Now let's move the stars.
The star starts in the center of the screen.
The first thing we need to do is rotate the scene along the Y axis.
If we rotate it 90 degrees, the X-axis is no longer from left to right, it goes out of the screen from the inside out.
To make it clearer, let’s give an example. Imagine you are standing in the middle of a house.
Now imagine that the wall to your left says -x, and the wall in front of you says -z.
The wall on the right is +x, and the wall behind you is +z.
If the entire house is turned 90 degrees to the right, but you don't move, then the wall in front will be -x instead of -z.
All other walls move as well. -z appears on the right, +z appears on the left, and +x appears behind you.
Are you out of your mind? By rotating the scene, we change the orientation of the x and z planes.
The second line of code moves a positive value along the x-axis.
Usually a positive value on the x-axis means moving to the right side of the screen (that is, the usual positive direction of the x-axis).
But here since we rotate the coordinate system around the y-axis, the positive direction of the x-axis can be in any direction.
If we turn it 180 degrees, the left and right sides of the screen will be mirrored.
So when we move along the positive x-axis, it could be left, right, forward or backward.
}
glRotatef(star[loop].angle, 0.0, 1.0, 0.0); //Rotate to the angle of the currently drawn star
glTranslatef(star[loop].dist, 0.0, 0.0); // Move forward along the X-axis
{
The following code has a little trick.
The stars are actually a flat texture.
Now you draw a flat quad in the center of the screen and apply a texture, and it looks good.
Everything is as you imagined. But when you rotate 90 degrees along the y-axis,
The only two sides of the texture on the screen facing you are the right and left sides. It just looks like a thin line.
This is not what we want. We want the stars to always be facing us, no matter how the screen is rotated or tilted.
We accomplish this by canceling out any rotations made to the star before drawing it.
You can reverse the rotation to counteract the rotation. When we tilt the screen, we actually rotate the star at its current angle.
By reversing the order, we "anti-rotate" the star at its current angle. That is, the star is rotated by the negative value of the current angle.
that is,
If we rotated the star 10 degrees, we rotate it -10 degrees so that the star faces the screen again on that axis.
The first line below cancels the rotation along the y-axis. Then, we also need to offset the screen tilt along the x-axis.
To do this, we just need to rotate the screen again -tilt.
After canceling out the rotation on the x and y axes, the star is now completely facing us again.
}
glRotatef(-star[loop].angle, 0.0, 1.0, 0.0); // Cancel the angle of the current star
glRotatef(-tilt, 1.0, 0.0, 0.0); // Cancel screen tilt
{If twinkle is TRUE, we first draw a non-rotated star on the screen:
Subtract the current number of stars (loop) from the total number of stars (num) and then subtract 1.
to extract the different colors of each star (this is done because the loop range is from 0 to num-1).
For example, when the result is 10, we use the color of star No. 10.
This way the colors of adjacent stars are always different. It's not a good idea, but it works.
The last value is the alpha channel component. The smaller the value, the dimmer the star.
Since twinkle is enabled, each star will end up being drawn twice.
The program will run slower, depending on the performance of your machine.
But the colors of the stars drawn in two passes blend into each other and produce a great effect.
At the same time, since the stars in the first pass did not rotate, the stars after enabling twinkle look like an animation effect.
(If you don’t understand here, just go and see the running effect of the program yourself.)
It's worth noting that coloring textures is easy.
Even though the texture itself is black and white, the texture will change to whatever color we selected before painting it.
In addition, it is also worth noting that the color value we use here is of byte type,
instead of the usual floating point numbers. Even the alpha channel component is like this. }
If (twinkle) Then // Enable twinkle effect
Begin
//Specify a color using a byte value
glColor4ub(star[(50 - loop) - 1].r, star[(50 - loop) - 1].g,
star[(50 - loop) - 1].b, 255);
glBegin(GL_QUADS); // Start drawing texture-mapped quads
glTexCoord2f(0.0, 0.0);
glVertex3f(-1.0, -1.0, 0.0);
glTexCoord2f(1.0, 0.0);
glVertex3f(1.0, -1.0, 0.0);
glTexCoord2f(1.0, 1.0);
glVertex3f(1.0, 1.0, 0.0);
glTexCoord2f(0.0, 1.0);
glVertex3f(-1.0, 1.0, 0.0);
glEnd(); // End of quadrilateral drawing
End;
{
Now draw the second pass of the stars.
The only difference from the previous code is that the stars this time will definitely be drawn, and this time the stars will rotate around the z-axis.
}
glRotatef(spin, 0.0, 0.0, 1.0); // Rotate the star around the z-axis
//Specify a color using a byte value
glColor4ub(star[loop].r, star[loop].g, star[loop].b, 255);
glBegin(GL_QUADS); // Start drawing texture-mapped quads
glTexCoord2f(0.0, 0.0);
glVertex3f(-1.0, -1.0, 0.0);
glTexCoord2f(1.0, 0.0);
glVertex3f(1.0, -1.0, 0.0);
glTexCoord2f(1.0, 1.0);
glVertex3f(1.0, 1.0, 0.0);
glTexCoord2f(0.0, 1.0);
glVertex3f(-1.0, 1.0, 0.0);
glEnd(); // End of quadrilateral drawing
{The code below represents the movement of stars.
We increase the spin value to rotate all stars (revolution).
Then, increase the rotation angle of each star by loop/num.
This causes stars farther from the center to spin faster. Finally reduce the distance of each star from the center of the screen.
It looks like the stars are constantly being sucked into the center of the screen. }
spin := spin + 0.01; // The revolution of the star
star[loop].angle := star[loop].angle + Trunc(loop) / 50; // Change the rotation angle of the star
star[loop].dist := star[loop].dist - 0.01; // Change the distance of the star from the center
{The next few lines check whether the star has touched the center of the screen.
When the star hits the center of the screen, we give it a new color and move it 5 units outward.
The star will embark on its journey back to the center of the screen. }
If (star[loop].dist < 0.0) Then // Has the star reached the center?
Begin
star[loop].dist := star[loop].dist + 5.0; // Move 5 units outward
star[loop].r := random(256); // Assign a new red component
star[loop].g := random(256); // Assign a new green component
star[loop].b := random(256); // Assign a new blue component
End;
End;
End;
{
Now we add the code to monitor the keyboard.
Move down to WinMain(). Find the SwapBuffers(hDC) line.
We will add the keyboard monitoring code after this line.
The code will check if the T key has been pressed.
If the T key is pressed and released, the code in the if block will be executed.
If twinkle is FALSE, it will become TRUE.
vice versa. As long as the T key is pressed, tp becomes TRUE.
This prevents the code within the block from being executed repeatedly if you keep pressing the T key.
}
If (keys[ord('T')] And Not tp) Then // Whether the T key has been pressed and the tp value is FALSE
Begin
tp := TRUE; // If yes, set tp to TRUE
twinkle := Not twinkle; // Flip the value of twinkle
End;
{
The code below checks whether the T key has been released.
If so, set tp=FALSE.
Unless the value of tp is FALSE,
Otherwise nothing will happen while holding down the T key. So this line of code is important.
}
If (Not keys[Ord('T')]) Then // Has the T key been released?
Begin
tp := FALSE; // If yes, tp is FALSE
End;
{The remaining code checks whether the up and down arrow keys, page up key or page down key are pressed. }
If (keys[VK_UP]) Then // Is the up arrow key pressed?
tilt := tilt - 0.5; // Tilt the screen upwards
If (keys[VK_DOWN]) Then // Is the down arrow key pressed?
tilt := tilt + 0.5; // Tilt the screen downwards
If (keys[VK_PRIOR]) Then // Is the page up key pressed?
zoom := zoom - 0.2; // Zoom out
If (keys[VK_NEXT]) Then // Is the page down key pressed?
zoom := zoom + 0.2; // Zoom in
{
In this lesson I try my best to explain how to load a grayscale bitmap texture,
After removing its background color (using mixed colors), color it again, and finally make it move in the 3D scene.
I've shown you how to create beautiful color and animation effects.
The implementation principle is to overlap a bitmap copy on the original bitmap.
Until now, as long as you have a good understanding of everything I have taught you,
You should be able to create your own 3D demo without any problem.
All the basics covered! }
//========myling:
//Lectures 1-9 have been translated. As NEHE said, the basic knowledge has been basically explained.
//I looked at the following tutorials, and they seem to be written by other people. If there are good examples, I will selectively follow them.
//Renewed, I’m so tired, take a nap :), see you next time