To render an image with reflection based on a cube map, and also render the cube map itself, carry out the following steps:
- We'll start by defining a function that will load the six images of the cube map into a single texture target:
GLuint Texture::loadCubeMap(const std::string &baseName,
const std::string &extension) {
GLuint texID;
glGenTextures(1, &texID);
glBindTexture(GL_TEXTURE_CUBE_MAP, texID);
const char * suffixes[] = { "posx", "negx", "posy",
"negy", "posz", "negz" };
GLint w, h;
// Load the first one to get width/height
std::string texName = baseName + "_" + suffixes[0] + extension;
GLubyte * data = Texture::loadPixels(texName, w, h, false);
// Allocate immutable storage for the whole cube map texture
glTexStorage2D(GL_TEXTURE_CUBE_MAP, 1, GL_RGBA8, w, h);
glTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X,
0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, data);
stbi_image_free(data);
// Load the other 5 cube-map faces
for( int i = 1; i < 6; i++ ) {
std::string texName = baseName + "_" + suffixes[i] +
extension;
data = Texture::loadPixels(texName, w, h, false);
glTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, data);
stbi_image_free(data);
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER,
GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER,
GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S,
GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T,
GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R,
GL_CLAMP_TO_EDGE);
return texID;
}
- Use the following code for the vertex shader:
layout (location = 0) in vec3 VertexPosition; layout (location = 1) in vec3 VertexNormal; layout (location = 2) in vec2 VertexTexCoord; out vec3 ReflectDir; // The direction of the reflected ray
uniform vec3 WorldCameraPosition; uniform mat4 ModelViewMatrix; uniform mat4 ModelMatrix; uniform mat3 NormalMatrix; uniform mat4 ProjectionMatrix; uniform mat4 MVP; void main() { // Compute the reflected direction in world coords. vec3 worldPos = vec3(ModelMatrix * vec4(VertexPosition,1.0) ); vec3 worldNorm = vec3(ModelMatrix * vec4(VertexNormal, 0.0)); vec3 worldView = normalize( WorldCameraPosition - worldPos ); ReflectDir = reflect(-worldView, worldNorm );
gl_Position = MVP * vec4(VertexPosition,1.0); }
- Use the following code for the fragment shader:
in vec3 ReflectDir; // The direction of the reflected ray // The cube map layout(binding=0) uniform samplerCube CubeMapTex; uniform float ReflectFactor; // Amount of reflection uniform vec4 MaterialColor; // Color of the object's "Tint" layout( location = 0 ) out vec4 FragColor; void main() { // Access the cube map texture vec4 cubeMapColor = texture(CubeMapTex, ReflectDir); FragColor = mix(MaterialColor, CubeMapColor, ReflectFactor); }
- In the render portion of the OpenGL program, draw a cube centered at the origin and apply the cube map to the cube. You can use the normalized position as the texture coordinate. Use a separate shader for this sky box. See the example code for details.
- Switch to the preceding shaders and draw the object(s) within the scene.