Handmade Hero»Forums»Code
100 posts
OpenGL: weird problem with GL_TEXTURE_CUBE_MAP

This is to build the skybox texture.

What happens is that the skybox textures are upside down and switched. I'm using code from tutorials and searched everywhere what might cause this and maybe it is a silly thing. Ok I can just flip the actual textures and pass them in a different order, but that shouldn't be happening.

I load with stb header img loader. Here's all the pertinent code:

LOADER

GLuint load_texture_cube( const std::string& front, const std::string& back,
						 const std::string& up, const std::string& down,
						 const std::string& left, const std::string& right)
{
	GLuint tex_cube;
	glGenTextures( 1, &tex_cube );
	glActiveTexture( GL_TEXTURE0 );

	auto load_cube_side = [=]( GLuint texture, GLenum side_target, const std::string& file_name ) {
		int x, y, n;
		/// RGBA channels
		int force_channels = 4;
		unsigned char* image_data = stbi_load( file_name.c_str(), &x, &y, &n, force_channels );
		if ( !image_data ) {
			fprintf( stderr, "ERROR: could not load %s\n", file_name.c_str() );
		}
		/// no need to power of 2 check https://www.khronos.org/opengl/wiki/Cubemap_Texture#Creation

		glBindTexture( GL_TEXTURE_CUBE_MAP, texture );

		glTexImage2D( side_target, 0, GL_RGBA, x, y, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data );

		free( image_data );
	};

	load_cube_side( tex_cube, GL_TEXTURE_CUBE_MAP_POSITIVE_X, right );
	load_cube_side( tex_cube, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, left );
	load_cube_side( tex_cube, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, up );
	load_cube_side( tex_cube, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, down );
	load_cube_side( tex_cube, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, back );
	load_cube_side( tex_cube, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, front );

	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_MAG_FILTER, GL_LINEAR );
	glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
	return tex_cube;
}

Loading

auto tex_skybox = load_texture_cube("textures/skybox/midday/front.png","textures/skybox/midday/back.png",
										"textures/skybox/midday/up.png","textures/skybox/midday/down.png",
										"textures/skybox/midday/left.png","textures/skybox/midday/right.png"
										);

SHADER

#version 410

layout (location = 0) in vec3 vPoints;

uniform mat4 proj_mat, view_mat;

out vec3 texcoords;

void main () {
	texcoords = vPoints;
	// transforming view matrix into mat3 removes the translation part (last row), then putting it back
	// zeroes it
	// this is to make the skybox update with camera rotation but not with camera translation, because that's
	// unecessary
	#if 1
	vec4 pos = proj_mat * mat4(mat3(view_mat)) * vec4 (vPoints, 1.0);
	#else
	vec4 pos = proj_mat * view_mat * vec4 (vPoints, 1.0);

	#endif

	gl_Position = pos.xyww;
}

/////////////////////////////

#version 410

in vec3 texcoords;

uniform samplerCube cube_texture;

out vec4 frag_color;

void main () {
	frag_color = texture(cube_texture, texcoords);
}

VERTICES

shapes::Skybox::Skybox()
{
	points = {
		-1,  1, -1,
		-1, -1, -1,
		1, -1, -1,

		1, -1, -1,
		1,  1, -1,
		-1,  1, -1,
		//////////
		-1, -1,  1,
		-1, -1, -1,
		-1,  1, -1,

		-1,  1, -1,
		-1,  1,  1,
		-1, -1,  1,
		//////////
		1, -1, -1,
		1, -1,  1,
		1,  1,  1,

		1,  1,  1,
		1,  1, -1,
		1, -1, -1,
		//////////
		-1, -1,  1,
		-1,  1,  1,
		1,  1,  1,

		1,  1,  1,
		1, -1,  1,
		-1, -1,  1,
		//////////
		-1,  1, -1,
		1,  1, -1,
		1,  1,  1,

		1,  1,  1,
		-1,  1,  1,
		-1,  1, -1,
		//////////
		-1, -1, -1,
		-1, -1,  1,
		1, -1, -1,

		1, -1, -1,
		-1, -1,  1,
		1, -1,  1
	};
}

VAO Generating

void Model_lean::generate(const V_Float& points)
{
	glGenVertexArrays(1, &m_VAO);
	glBindVertexArray(m_VAO);

	glGenBuffers(1, &m_VBO);
	glBindBuffer(GL_ARRAY_BUFFER, m_VBO);

	glBufferData( GL_ARRAY_BUFFER,
				 points.size() * sizeof( points[0] ),
				 points.data(),
				 GL_STATIC_DRAW );

	glEnableVertexAttribArray( 0 );
	glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, 0, 0 );

}

REndering

Call in main loop, where proj_mat is the regular perspective matrix, and vmat is the camera view matrix that will be stripped out of translation in the shader (it doesn't matter if i try to do different things here, like regenerating only the rotation matrices, skybox sides still upside down and some switched)

     Use_programme(skybox_prog);
Load_matrix4(skybox_prog, "proj_mat", proj_mat.m);
Load_matrix4(skybox_prog, "view_mat", vmat.m);
draw_skybox(&skybox_model,tex_skybox);
void draw_skybox(Model_lean* model, GLuint texture)
{
	glDepthFunc(GL_LEQUAL);
	glActiveTexture( GL_TEXTURE0 );
	glBindTexture(GL_TEXTURE_CUBE_MAP, texture);
	model->bind_VAO();
	glDrawArrays( GL_TRIANGLES, 0, 36 );
	glDepthFunc(GL_LESS);
}

                
Mārtiņš Možeiko
2568 posts / 2 projects
OpenGL: weird problem with GL_TEXTURE_CUBE_MAP

Be careful on how cubemap uses coordinate system. It is left handed. See here: https://www.khronos.org/opengl/wiki/Cubemap_Texture#Upload_and_orientation So if your view matrix is right handed, then it will render wrong. Either use separate texcoords attribute that you can specify independently from vertex coords. Or flip the coordinate system in shader, by negating Z component if I'm not mistaken: texcoords = vec3(vPoints.xy, -vPoints.z);

Simon Anciaux
1341 posts
OpenGL: weird problem with GL_TEXTURE_CUBE_MAP

Maybe you also need to flip the image you get from stb image ?

// Before calling the stbi functions
stbi_set_flip_vertically_on_load( true );
100 posts
OpenGL: weird problem with GL_TEXTURE_CUBE_MAP
Edited by da447m on
Replying to mmozeiko (#29805)

Yes, that should have been solved by uploading the back texture to GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, right? Or is there anything else with left handed system that I'm not seeing here? That doesn't work.

I've tried calling stbi_set_flip_vertically_on_load( true ) but it won't do anything at all for some reason.

The solution you suggested didn't work, it didn't perform any changes, which is weird, the shader should at least switch back to front and flip left and right horizontally, up and down vertically, but nothing.

I also just created a conversion from right-handed sys to lhs and I cannot just apply that to the projection matrix. I've followed the conversion matrix as here: https://stackoverflow.com/questions/1263072/changing-a-matrix-from-right-handed-to-left-handed-coordinate-system

mat4 rhs_to_lhs(const mat4& m)
{
	return mat4
	{
				m.m[ 0], m.m[ 2], m.m[ 1], 0,
				m.m[ 8], m.m[10], m.m[ 9], 0,
				m.m[ 4], m.m[ 6], m.m[ 5], 0,
				m.m[12], m.m[14], m.m[13], 1
	};
}

So for now I'm literally rotating the png files themselves but something is not right for sure.

Mārtiņš Možeiko
2568 posts / 2 projects
OpenGL: weird problem with GL_TEXTURE_CUBE_MAP
Edited by Mārtiņš Možeiko on
Replying to da447m (#29807)

Yes, that should have been solved by uploading the back texture to GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, right?

No, it changes also X/Y orientation.

That code in rhs_to_lhs does not change rhs to lhs. It needs to involve at least one negative sign.

Also projection matrix is not affecting texcoords of your cubemap lookup. You should not change projection matrix at all.

If all those changes you do "does not change anything at all" then you're doing something wrong. Like editing wrong source code, running old exe or similar. Double check what exactly are you modifying.

To debug coordinate orientation problems I usually do structured art - create textures where I draw axis in directions I expect them to be. Put a text where top or bottom should be (literally text "top" or "bottom" and similar). Use images in OpenGL wiki link I gave as reference. Then draw using these images & debug values for known locations (like corner of texture) to see if they come up as expected. Use RenderDoc for that - to verify values in each stage of pipeline.