I have a renderer based on SDL2 and OpenGL (3.3 core profile), which gives me expected results with regards to transformations and texture(2D)ing.
However, when I'm trying to display a skybox using a cubemap created from these textures (though I've tried others too), there are two steps in the process that no other tutorial or example that I have encountered seems to have to do, and I cannot explain:
1, The top / bottom faces have to be swapped upon uploading, i.e.: the top one is uploaded as GL_TEXTURE_CUBEMAP_NEGATIVE_Y, and the bottom one is GL_TEXTURE_CUBEMAP_POSITIVE_Y;
2, When sampling the cube map, I have to invert vertex positions along y, but also along z;
Without this, I'm getting the following result:

(N.B. the left-bottom-far vertex was scaled by .8 to clarify that my coordinate system is the right way around)
The image files are named correctly.
The cube is the only draw I'm performing.
If I remove [the indices for] any of the sides, I get the expected results (i.e. no swapping / mirroring there).
I seem to be getting the same results with my integrated and dedicated GPUs.
My OpenGL constants, from a glLoadGen (originally) generated header:
#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516
#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518
#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A
#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515
#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517
#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519
The texture uploading code (much the same as LearnOpenGL's tutorial):
GLuint name;
glGenTextures(1, &name);
glBindTexture(GL_TEXTURE_CUBE_MAP, name);
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);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
GLint target = GL_TEXTURE_CUBE_MAP_POSITIVE_X;
for (uint8_t i = 0; i < 6; ++i)
{
glTexImage2D(target + i, 0, GL_RGB8, width, height, 0, GL_RGB,
GL_UNSIGNED_BYTE, pixelData[i]));
}
Vertex shader:
#version 330
precision mediump float;
uniform mat4 uModelViewProjection;
in vec3 aPosition;
out vec3 vTexCoord;
void main()
{
vec4 position = uModelViewProjection * vec4(aPosition, 1.f);
gl_Position = position.xyww;
vTexCoord = aPosition;
}
Fragment shader:
#version 330
precision mediump float;
uniform samplerCube uTexture0;
in vec3 vTexCoord;
out vec4 FragColor;
void main()
{
FragColor = texture(uTexture0, vTexCoord);
// using textureCube() yields a compile error asking for #extension GL_NV_shadow_samplers_cube : enable, but even with that, the issue perists.
}
Mesh setup (semi-pseudo-code):
// 4----5
// /| /|
// 6----7 |
// | | | |
// | 0--|-1
// |/ |/
// 2----3
VertexType vertices[8] = {
Vector3(-1.f, -1.f, -1.f) * .8f, // debug coordinate system
Vector3(1.f, -1.f, -1.f),
Vector3(-1.f, -1.f, 1.f),
Vector3(1.f, -1.f, 1.f),
Vector3(-1.f, 1.f, -1.f),
Vector3(1.f, 1.f, -1.f),
Vector3(-1.f, 1.f, 1.f),
Vector3(1.f, 1.f, 1.f),
};
uint16_t indices[] = {
4, 0, 5,
0, 1, 5,
6, 2, 4,
2, 0, 4,
7, 3, 6,
3, 2, 6,
5, 1, 7,
1, 3, 7,
0, 2, 1,
2, 3, 1,
5, 7, 4,
7, 6, 4,
};
// create buffers & upload data
Rendering (pseudo-code):
// -clear color & depth buffers;
// -set the model transform to a translation of -10 units along z;
// view transform is identity; projection is perspective with .25
// radians vertical FOV, zNear of .1, zFar of 100.; viewport is full screen
// -set shader program;
// -bind texture (same name, same target as upon uploading);
// -enable backface culling only (no depth test / write);
// -draw the cube
// -glFlush() and swap buffers;
What on earth can be causing the two issues described above?
The issue is caused by the mapping of the .str texture coordinates to the cubemap:
OpenGL 4.6 API Core Profile Specification, 8.13 Cube Map Texture Selection, page 253:
When a cube map texture is sampled, the (s, t, r) texture coordinates are treated as a direction vector (rx, ry, rz) emanating from the center of a cube. The q coordinate is ignored. At texture application time, the interpolated per-fragment direction vector selects one of the cube map face’s two-dimensional images based on the largest magnitude coordinate direction (the major axis direction). If two or more coordinates have the identical magnitude, the implementation may define the rule to disambiguate this situation. The rule must be deterministic and depend only on (rx, ry, rz). The target column in table 8.19 explains how the major axis direction maps to the two-dimensional image of a particular cube map target. Using the sc, tc, and ma determined by the major axis direction as specified in table 8.19, an updated (s, t) is calculated as follows:
s = 1/2 (sc / |m_a| + 1)
t = 1/2 (tc / |m_a| + 1)Major Axis Direction| Target |sc |tc |ma | --------------------+---------------------------+---+---+---+ +rx |TEXTURE_CUBE_MAP_POSITIVE_X|−rz|−ry| rx| −rx |TEXTURE_CUBE_MAP_NEGATIVE_X| rz|−ry| rx| +ry |TEXTURE_CUBE_MAP_POSITIVE_Y| rx| rz| ry| −ry |TEXTURE_CUBE_MAP_NEGATIVE_Y| rx|−rz| ry| +rz |TEXTURE_CUBE_MAP_POSITIVE_Z| rx|−ry| rz| −rz |TEXTURE_CUBE_MAP_NEGATIVE_Z|−rx|−ry| rz| --------------------+---------------------------+---+---+---+Table 8.19: Selection of cube map images based on major axis direction of texture coordinates
The rotation can be achieved by either rotating the 6 cubemap images before loading them to the cubemap sampler or by rotating the texture coordinates.

It cubemap is used as an environment map in a scene and the texture coordinates are get by a direction vector, then it makes sense to rotate the images. If the cubemap is wrapped on a mesh then the texture coordinates can be specified in the right manner.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With