SkyBox Utilizando Cube Maps

En el tutorial OpenGL anterior aprendimos a generar un terreno, aplicando correctamente diversas texturas, iluminación, mapas de relieve, etc., podemos lograr efectos realistas, sin embargo si miramos al horizonte veremos el color usado para limpiar el frame, en el mundo real vemos el cielo, sol, nubes, etc., un sky box es una caja que rodea la escena, aplicando las texturas adecuadas generamos el efecto deseado.

terreno sky box

Cube Maps


Un cube map es una textura que puede ser aplicada a un cubo, es similar a las texturas 2D solo que este caso tendremos 6 de ellas, una para cada cara del cubo, un cube map tiene diversos usos en este tutorial nos enfocaremos en los sky boxes.

Creamos un cube map de manera similar a como lo hacíamos con las texturas 2D, cambiamos el tipo de textura a GL_TEXTURE_CUBE_MAP, también debemos indicar a que cara del cubo pertenece la textura, para ello usamos: GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z.

skybox
Para utilizar un cube map primero necesitamos el conjunto de vértices que forman el cubo al cual se le aplicaran cada una de las texturas, lo haremos del mismos modo que lo hicimos en el tutorial dibujar figura 3D con OpenGL.

Cargamos las 6 texturas correspondientes a cada una de las caras del cubo, usamos el tipo de textura GL_TEXTURE_CUBE_MAP e indicamos a que cara del cubo pertenece cada textura, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i para iterar y asignar la correspondiente textura a la cara del cubo.

glGenTextures(1, &skyboxTextureID);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, skyboxTextureID);

glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
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);

for (int i = 0; i < 6; i++) {
    int channels, width, height;
  
    unsigned char *pData = stbi_load(textures[i].c_str(), &width, &height, &channels, STBI_rgb);

    if (pData == nullptr) cout << "Error al cargar: " << textures[i] << endl;

    glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, pData);
    stbi_image_free(pData);
}

glBindTexture(GL_TEXTURE_CUBE_MAP, 0);

El vertex shader transforma la posición de cada vértice como siempre lo hacemos, las coordenadas UV de la textura son obtenidas de la posición del vértice, el fragment shader utiliza las coordenadas UV sobre el samplerCube para obtener el color final correspondiente.

//------------- VERTEX SHADER --------------------//
layout (location = 0) in vec4 position;

uniform mat4 MVP, MV;
out vec3 uv;

void main()
{
     gl_Position = MVP * position;
     uv = position.xyz;
}

//----------- FRAGMENT SHADER ------------------//
out vec4 color;
uniform samplerCube cubeMap;
in vec3 uv;

void main()
{
 color = texture(cubeMap, uv);
}

Para finalizar ubicamos la caja en la misma posición de la cámara de modo que la misma permanezca siempre en el centro de ella, la caja se mueve con la cámara así nunca saldremos de ella, cuando la cámara gira la caja permanece estática logrando así el efecto deseado.

glm::mat4 M;
M = glm::translate(M, camera.getPosition());
M = glm::scale(M, glm::vec3(100.0f));

glm::mat4 MV = camera.getViewMatrix() * M;
glm::mat4 MVP = camera.getProjectionMatrix() * MV;

glUniformMatrix4fv(shader.getUniformLocation("MVP"), 1, GL_FALSE, glm::value_ptr(MVP));
glUniformMatrix4fv(shader.getUniformLocation("MV"), 1, GL_FALSE, glm::value_ptr(MV));

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, skyboxTextureID);

glBindVertexArray(boxVAO);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, 0);

En la web podemos encontrar sitios dedicados al diseño de sky boxes para distintos usos, solo debemos descargar el grupo de imágenes y utilizarlas.

GitHub: Sky Box en OpenGL con Cube Maps

Comentarios

Entradas populares de este blog

Conectar SQL Server con Java

Detección de rostros

Instalar OpenCV para Python en Windows