How it works...

In OpenGL, a cube map texture consists of six separate images. To fully initialize a cube map texture, we need to bind to the cube map texture and then load each image individually into the six "slots" within that texture. In the preceding code (within the Texture::loadCubeMap function), we start by binding to texture unit zero with glActiveTexture. Then, we create a new texture object by calling glGenTextures, store its handle within the variable texID, and then bind that texture object to the GL_TEXTURE_CUBE_MAP target using glBindTexture. We load the first image to determine the dimensions of the image, and then load the others in a loop. The following loop loads each texture file and copies the texture data into OpenGL memory using glTexSubImage2D. Note that the first argument to this function is the texture target, which corresponds to GL_TEXTURE_CUBE_MAP_POSITIVE_X + i. OpenGL defines consecutive constants that correspond to the six faces of the cube, so we can just add an integer to the value of the constant for the first face. After the loop is finished, the cube map texture should be fully initialized with the six images.

Following this, we set up the cube map texture environment. We use linear filtering, and we also set the texture wrap mode to GL_CLAMP_TO_EDGE for all three of the texture coordinate's components. This tends to work well, avoiding the possibility of a border color appearing between the cube edges.

Even better would be to use seamless cube map textures (available since OpenGL 3.2).  It is a simple matter to enable them, just call: glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS) .

Within the vertex shader, the main goal is to compute the direction of reflection and pass that to the fragment shader to be used to access the cube map. The output variable ReflectDir will store this result. We can compute the reflected direction (in world coordinates) by reflecting the vector toward the viewer about the normal vector.

We choose to compute the reflection direction in world coordinates because, if we were to use eye coordinates, the reflection would not change as the camera moved within the scene.

In the else branch within the main function, we start by converting the position to world coordinates and storing them in worldPos. We then do the same for the normal, storing the result in worldNorm. Note that the ModelMatrix is used to transform the vertex normal. It is important when doing this to use a value of 0.0 for the fourth coordinate of the normal to avoid the translation component of the model matrix affecting the normal. Also, the model matrix must not contain any non-uniform scaling component; otherwise the normal vector will be transformed incorrectly.

The direction toward the viewer is computed in world coordinates and stored in worldView.

Finally, we reflect worldView about the normal and store the result in the output variable ReflectDir. The fragment shader will use this direction to access the cube map texture and apply the corresponding color to the fragment. One can think of this as a light ray that begins at the viewer's eye, strikes the surface, reflects off the surface, and hits the cube map. The color that the ray sees when it strikes the cube map is the color that we need for the object.

When drawing the sky box, we use the vertex position as the reflection direction. Why? Well, when the sky box is rendered, we want the location on the sky box to correspond to the equivalent location in the cube map (the sky box is really just a rendering of the cube map).  Therefore, if we want to access a position on the cube map corresponding to a location on a cube centered at the origin, we need a vector that points at that location. The vector we need is the position of that point minus the origin (which is (0,0,0)). Hence, we just need the position of the vertex.

Sky boxes can be rendered with the viewer at the center of the sky box and the sky box moving along with the viewer (so the viewer is always at the center of the sky box). We have not done so in this example; however, we could do so by transforming the sky box using the rotational component of the view matrix (not the translational).

Within the fragment shader, we simply use the value of ReflectDir to access the cube map texture:

vec4 cubeMapColor = texture(CubeMapTex, ReflectDir) 

We'll mix the sky box color with some material color. This allows us to provide some slight tint to the object. The amount of tint is adjusted by the variable ReflectFactor. A value of 1.0 would correspond to zero tint (all reflection), and a value of 0.0 corresponds to no reflection. The following images show a teapot rendered with different values of ReflectFactor. The teapot on the left uses a reflection factor of 0.5, and the one on the right uses a value of 0.85. The base material color is grey (the cube map used is an image of St. Peter's Basilica, Rome. ©Paul Debevec):

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.141.31.240