Cubemaps

Les textures cubemap són una eina que ens proporcionen les GPUs que ens permet implementar algunes tècniques de rendering com skyboxes, skylight illumination, reflexos d’entorn, etc. Amb ells podem mapejar un entorn visible sobre les 6 cares d’un cub i més endavant podem obtenir aquesta informació fàcilment des d’un shader.

Cubemap 3d

A la imatge anterior es pot veure una textura cubemap representada en 3D.

Les textures cubemap poden emmagatzemar cadascuna de les imatges corresponents a les cares d’aquest cub en una sub-textura. Per fer això, OpenGL proporciona un target específic per cadascuna de les cares del cubemap. L’orientació de les textures ha de complir el següent esquema:

Cubemap unfold

Fixeu-vos que aquesta imatge és el resultat de desplegar el cub 3D vist anteriorment. Les textures que espera OpenGL són les imatges de l’entorn projectat sobre les cares d’aquest cubemap vist des de fora (Remarco això perquè en una ocasió vaig tenir problemes intentant entendre amb quina orientació havien de carregar-se les textures a la GPU; com que estava intentant implementar un skybox, estava carregant les textures tal com es visualitzarien des de dins del cub… i com a resultat les cares de l’skybox no encaixaven entre elles).

Codi OpenGL per generar la textura cubemap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// Create and bind a new cubemap texture
GLuint tex;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_CUBE_MAP, tex);

// Texture parameters (don't forget R coordinate)
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_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);

// Pixels
GLint w, h; // Width and height
void *texelsPX = readPixels("cubemap_positive_x.png", &w, &h);
void *texelsNX = readPixels("cubemap_negative_x.png", &w, &h);
void *texelsPY = readPixels("cubemap_positive_y.png", &w, &h);
void *texelsNY = readPixels("cubemap_negative_y.png", &w, &h);
void *texelsPZ = readPixels("cubemap_positive_z.png", &w, &h);
void *texelsNZ = readPixels("cubemap_negative_z.png", &w, &h);

// Fixed format (could be different...)
GLenum ifmt = GL_RGB; // Internal format
GLenum dfmt = GL_RGBA; // Data format
GLenum dtype = GL_UNSIGNED_BYTE; // Data type

// Upload texels for each cube face
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, ifmt, w, h, 0, dfmt, dtype, texelsPX);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, ifmt, w, h, 0, dfmt, dtype, texelsNX);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, ifmt, w, h, 0, dfmt, dtype, texelsPY);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, ifmt, w, h, 0, dfmt, dtype, texelsNY);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, ifmt, w, h, 0, dfmt, dtype, texelsPZ);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, ifmt, w, h, 0, dfmt, dtype, texelsNZ);

Codi OpenGL per enviar la textura a un shader (skybox)

La manera d’enviar la textura cubemap al shader és exactament la mateixa que quan s’envia una textura 2D normal (a excepció del target per fer el binding – GL_TEXTURE_CUBE_MAP):

1
2
3
4
5
6
7
8
9
10
11
12
// Bind the texture
const int textureSlot = 0;
glActiveTexture(GL_TEXTURE0 + textureSlot);
glBindTexture(GL_TEXTURE_CUBE_MAP, tex);

// Configure program
glUseProgram(program);
GLint skyboxLocation = glGetUniformLocation(program, "skybox");
glUniform1i(skyboxLocation, textureSlot);

// Send some geometry to draw
// ...

GLSL skybox vertex shader

De fet no fa res. Tot i que no es necessita processar cap vèrtex en aquesta etapa del pipeline, un vèrtex s’ha d’enviar a renderitzar al pipeline (sense importar el seu valor) per tal que el programa es pugui executar.

1
2
#version 400
void main() { }

GLSL skybox geometry shader

Aquí generem tota la geometria del cub. Podem veure que no s’espera cap entrada des del vertex shader (ja que aquest estava buit). El geometry shader construeix tota la geometría del cub generant dos tires de triangles (triangle strips) de zero:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#version 400

uniform mat4 projectionMatrix;
uniform mat4 orientationMatrix;

layout(points) in;
layout(triangle_strip, max_vertices = 16) out;

// Corner position(and texture coordinate for the next stage)
out vec3 pos;

void main()
{
  // Orientation-projection matrix
  mat4 m  = projectionMatrix * orientationMatrix;

  // Macro to emit a vertex
#define EmitVertex3f(x,y,z) \
  pos = vec3(x,y,z); \
  gl_Position = m * vec4(pos,1.0); \
  EmitVertex();


  // Horizontal triangle strip (-X -Z +X)
  EmitVertex3f(-1.0, 1.0, 1.0);
  EmitVertex3f(-1.0,-1.0, 1.0);
  EmitVertex3f(-1.0, 1.0,-1.0);
  EmitVertex3f(-1.0,-1.0,-1.0);
  EmitVertex3f( 1.0, 1.0,-1.0);
  EmitVertex3f( 1.0,-1.0,-1.0);
  EmitVertex3f( 1.0, 1.0, 1.0);
  EmitVertex3f( 1.0,-1.0, 1.0);
  EndPrimitive();

  // Vertical triangle strip (-Y +Z +Y)
  EmitVertex3f( 1.0,-1.0,-1.0);
  EmitVertex3f(-1.0,-1.0,-1.0);
  EmitVertex3f( 1.0,-1.0, 1.0);
  EmitVertex3f(-1.0,-1.0, 1.0);
  EmitVertex3f( 1.0, 1.0, 1.0);
  EmitVertex3f(-1.0, 1.0, 1.0);
  EmitVertex3f( 1.0, 1.0,-1.0);
  EmitVertex3f(-1.0, 1.0,-1.0);
  EndPrimitive();
}

GLSL skybox fragment shader

Per últim, els fragments es pinten amb el valor obtingut de la textura cubemap. Per consultar la textura, usem com a coordenades de textura els valors interpolats de les posicions dels vèrtexs del cub generat al geometry shader. Com que aquest cub està centrat a zero (0,0,0), aquestes coordenades concorden perfectament amb les direccions que s’han d’utilitzar per consultar el cubemap.

1
2
3
4
5
6
7
8
9
10
#version 400

uniform samplerCube skybox; // Cubemap sampler

in vec3 pos; // Texture coordinate
out vec4 out_Color; // Output color

void main() {    
  out_Color = texture(skybox, pos);
}

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>