PDA

View Full Version : [SOLVED] Skybox using cube map



IneQuation.pl
10-22-2008, 11:52 AM
Hey there,

I've just got round to drawing a skybox with my little engine. I'm using this geometry (SQRT_3_3 is a define that equals to the value of the reciprocal square root of 3):

skybox::skybox(void) {
glGenBuffers(2, this->m_bufferIDs);
glBindBuffer(GL_ARRAY_BUFFER, this->m_bufferIDs[0]);
float vertices[24] = { // the idea is that all vectors have a length of 1 so that position can also be used as cubemap texture coords
-SQRT_3_3, -SQRT_3_3, -SQRT_3_3,
SQRT_3_3, -SQRT_3_3, -SQRT_3_3,
-SQRT_3_3, SQRT_3_3, -SQRT_3_3,
SQRT_3_3, SQRT_3_3, -SQRT_3_3,
-SQRT_3_3, -SQRT_3_3, SQRT_3_3,
SQRT_3_3, -SQRT_3_3, SQRT_3_3,
-SQRT_3_3, SQRT_3_3, SQRT_3_3,
SQRT_3_3, SQRT_3_3, SQRT_3_3
};
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->m_bufferIDs[1]);
byte indices[24] = {
1, 5, 7, 3, // positive x
2, 0, 4, 6, // negative x
4, 5, 7, 6, // positive y
0, 1, 3, 2, // negative y
0, 1, 5, 4, // positive z
3, 2, 6, 7 // negative z
};
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
}
Drawn like this:

void skybox::draw(math::vec3 *pos, math::mat3x3 *rot) {
glDisable(GL_DEPTH_TEST); // skybox should be drawn behind anything else
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->m_bufferIDs[1]);
glBindBuffer(GL_ARRAY_BUFFER, this->m_bufferIDs[0]);
glVertexPointer(3, GL_FLOAT, 0, 0);
glDrawElements(GL_QUADS, 6 * 4, GL_UNSIGNED_BYTE, 0);
glEnable(GL_DEPTH_TEST);
}
Using a cube map which is created this way:

cubeMap::cubeMap(const char *name, byte *data, imageInfo_t *info)
: texture(name), m_width(info->width), m_height(info->height) {
// TODO: mipmapping
glGenTextures(1, (GLuint *)(&this->m_id));
glBindTexture(GL_TEXTURE_CUBE_MAP, this->m_id);
for (int i = 0; i < 6; i++) {
if (info->format == GL_RGBA8)
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
0, info->format, info->width, info->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data + info->ofsCMSides[i]);
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
}
With these shaders (vertex and fragment, respectively):

void main() {
gl_Position = ftransform();
gl_TexCoord[0].xyz = gl_Position;
}

uniform samplerCube tex0;

void main() {
vec3 cube = vec3(textureCube(tex0, gl_TexCoord[0].xyz));
gl_FragColor = vec4(cube, 1.0);
}

Here's the problem. The skybox is drawn OK, the texture is there and everything appears correctly until the camera moves. What happens is that the geometry visibly moves, but the textures do not. I.e., they "slide" over the geometry in such a way that I always end up looking at the same side of the sky, regardless of my camera's orientation. What am I doing wrong?

dletozeun
10-22-2008, 02:00 PM
Where do you generate texture coordinates? I can't see it in the above code.

You need to create a vertex array for texture coordinates. Then in your draw function do this:

// choose the vertex array client state to be modified. Here it is the texture unit 0
glClientActiveTexture( GL_TEXTURE0_ARB );

// enable vertex array for the chosen state (texture unit 0)
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
// bind your vertex array that contains all texture coordinates and set its location
glBindBuffer( GL_ARRAY_BUFFER, mapTexCoordBuffer );
// specify data location offset
glTexCoordPointer(2, GL_FLOAT, 0, NULL);

// active texture unit 0 state
glActiveTexture( GL_TEXTURE0_ARB );

// enable 2D texture mapping
glEnable( GL_TEXTURE_2D );

// bind your texture
glBindTexture(...);

// draw something: glDrawElements

// get back to the previous context
glClientActiveTexture( GL_TEXTURE0_ARB );
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glActiveTexture( GL_TEXTURE0_ARB );
glDisable( GL_TEXTURE_2D );

IneQuation.pl
10-22-2008, 02:21 PM
I'm generating them in the vertex shader. Look, I'm not using 2D textures but a cube map texture unit (GL_TEXTURE_CUBE_MAP). The idea is that when using cube maps, the texture coords are 3-dimensional vectors pointing out of the cube's centre to the vertex. I've made my vertices always 1 unit away from the centre of the cube so as to reuse the vertex positions as texture coordinates.

It works well apart from the fact that the texture slides around together with the camera, making it so that I always look at the same side of the sky. I can tell the geometry is moving by the distortions in the output image, but the texture appears "glued" to the corners of the screen. Any ideas on what could be causing that?

dletozeun
10-22-2008, 02:41 PM
You are completly right, I was about to correct it realizing that you are using a cube map :)

IMO, your problem is due to the fact that you use transformed fragment position (by modelview and projection matrices) as texture coordinates. You need to use fragment position in object space. So just do:

gl_TexCoord[0].xyz = gl_Vertex.xyz;

When you rotate the camera, gl_Position will change for each skybox fragment and also texture coordinates, that is your problem.

IneQuation.pl
10-22-2008, 02:50 PM
Ah, right! Yes, sir, that did it! :D Thanks a lot, man! :)

dletozeun
10-22-2008, 03:00 PM
You are welcome, I am glad for you! :)