Выбрать главу

  glTexCoord2f(0.0f, 0.0f); // Top Left Of Texture

  glVertex3f(-2.0, 0.0,-2.0); // Top Left Corner Of Floor

  glTexCoord2f(1.0f, 0.0f); // Top Right Of Texture

  glVertex3f( 2.0, 0.0,-2.0); // Top Right Corner Of Floor

  glTexCoord2f(1.0f, 1.0f); // Bottom Right Of Texture

  glVertex3f( 2.0, 0.0, 2.0); // Bottom Right Corner Of Floor

 glEnd(); // Done Drawing The Quad

}

Now for the fun stuff. Here's where we combine all the objects and images to create our reflective scene.

We start off by clearing the screen (GL_COLOR_BUFFER_BIT) to our default clear color (off blue). The depth (GL_DEPTH_BUFFER_BIT) and stencil (GL_STENCIL_BUFFER_BIT) buffers are also cleared. Make sure you include the stencil buffer code, it's new and easy to overlook! It's important to note when we clear the stencil buffer, we are filling it with 0's.

After clearing the screen and buffers, we define our clipping plane equation. The plane equation is used for clipping the reflected image.

The equation eqr[]={0.0f,-1.0f, 0.0f, 0.0f} will be used when we draw the reflected image. As you can see, the value for the y-plane is a negative value. Meaning we will only see pixels if they are drawn below the floor or at a negative value on the y-axis. Anything drawn above the floor will not show up when using this equation.

More on clipping later… read on.

int DrawGLScene(GLvoid) // Draw Everything

{

 // Clear Screen, Depth Buffer & Stencil Buffer

 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

 // Clip Plane Equations

 double eqr[] = {0.0f,-1.0f, 0.0f, 0.0f}; // Plane Equation To Use For The Reflected Objects

So we have cleared the screen, and defined our clipping planes. Now for the fun stuff!

We start off by resetting the modelview matrix. Which of course starts all drawing in the center of the screen. We then translate down 0.6f units (to add a small perspective tilt to the floor) and into the screen based on the value of zoom. To better explain why we translate down 0.6f units, I'll explain using a simple example. If you were looking at the side of a piece of paper at exactly eye level, you would barely be able to see it. It would more than likely look like a thin line. If you moved the paper down a little, it would no longer look like a line. You would see more of the paper, because your eyes would be looking down at the page instead of directly at the edge of the paper.

 glLoadIdentity(); // Reset The Modelview Matrix

 glTranslatef(0.0f, –0.6f, zoom); // Zoom And Raise Camera Above The Floor (Up 0.6 Units)

Next we set the color mask. Something new to this tutorial! The 4 values for color mask represent red, green, blue and alpha. By default all the values are set to GL_TRUE.

If the red value of glColorMask({red},{green},{blue},{alpha}) was set to GL_TRUE, and all of the other values were 0 (GL_FALSE), the only color that would show up on the screen is red. If the value for red was 0 (GL_FALSE), but the other values were all GL_TRUE, every color except red would be drawn to the screen.

We don't want anything drawn to the screen at the moment, with all of the values set to 0 (GL_FALSE), colors will not be drawn to the screen.

 glColorMask(0,0,0,0); // Set Color Mask

Now even more fun stuff… Setting up the stencil buffer and stencil testing!

We start off by enabling stencil testing. Once stencil testing has been enabled, we are able to modify the stencil buffer.

It's very hard to explain the commands below so please bear with me, and if you have a better explanation, please let me know. In the code below we set up a test. The line glStencilFunc(GL_ALWAYS, 1, 1) tells OpenGL what type of test we want to do on each pixel when an object is drawn to the screen.

GL_ALWAYS just tells OpenGL the test will always pass. The second parameter (1) is a reference value that we will test in the third line of code, and the third parameter is a mask. The mask is a value that is ANDed with the reference value and stored in the stencil buffer when the test is done. A reference value of 1 ANDed with a mask value of 1 is 1. So if the test goes well and we tell OpenGL to, it will place a one in the stencil buffer (reference&mask=1).

Quick note: Stencil testing is a per pixel test done each time an object is drawn to the screen. The reference value ANDed with the mask value is tested against the current stencil value ANDed with the mask value.

The third line of code tests for three different conditions based on the stencil function we decided to use. The first two parameters are GL_KEEP, and the third is GL_REPLACE.

The first parameter tells OpenGL what to do if the test fails. Because the first parameter is GL_KEEP, if the test fails (which it can't because we have the funtion set to GL_ALWAYS), we would leave the stencil value set at whatever it currently is.

The second parameter tells OpenGL what do do if the stencil test passes, but the depth test fails. In the code below, we eventually disable depth testing so this parameter can be ignored.

The third parameter is the important one. It tells OpenGL what to do if the test passes! In our code we tell OpenGL to replace (GL_REPLACE) the value in the stencil buffer. The value we put into the stencil buffer is our reference value ANDed with our mask value which is 1.

After setting up the type of testing we want to do, we disable depth testing and jump to the code that draws our floor.

In simple english I will try to sum up everything that the code does up until now…

We tell OpenGL not to draw any colors to the screen. This means that when we draw the floor, it wont show up on the screen. BUT… each spot on the screen where the object (our floor) should be if we could see it will be tested based on the type of stencil testing we decide to do. The stencil buffer starts out full of 0's (empty). We want to set the stencil value to 1 wherever our object would have been drawn if we could see it. So we tell OpenGL we don't care about testing. If a pixel should have been drawn to the screen, we want that spot marked with a 1. GL_ALWAYS does exactly that. Our reference and mask values of 1 make sure that the value placed into the stencil buffer is indeed going to be 1! As we invisibly draw, our stencil operation checks each pixel location, and replaces the 0 with a 1.

 glEnable(GL_STENCIL_TEST); // Enable Stencil Buffer For "marking" The Floor

 glStencilFunc(GL_ALWAYS, 1, 1); // Always Passes, 1 Bit Plane, 1 As Mask

 glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); // We Set The Stencil Buffer To 1 Where We Draw Any Polygon

 // Keep If Test Fails, Keep If Test Passes But Buffer Test Fails

 // Replace If Test Passes

 glDisable(GL_DEPTH_TEST); // Disable Depth Testing

 DrawFloor(); // Draw The Floor (Draws To The Stencil Buffer)

 // We Only Want To Mark It In The Stencil Buffer

So now we have an invisible stencil mask of the floor. As long as stencil testing is enabled, the only places pixels will show up are places where the stencil buffer has a value of 1. All of the pixels on the screen where the invisible floor was drawn will have a stencil value of 1. Meaning as long as stencil testing is enabled, the only pixels that we will see are the pixels that we draw in the same spot our invisible floor was defined in the stencil buffer. The trick behind creating a real looking reflection that reflects in the floor and nowhere else!

So now that we know the ball reflection will only be drawn where the floor should be, it's time to draw the reflection! We enable depth testing, and set the color mask back to all ones (meaning all the colors will be drawn to the screen).