0

Mipmapping: effects of not having it and OpenGL implementation

Today I want to share the reason you should implement mipmapping in your engine/renderer.

The definition of mipmapping is basically a set of pre-calculated, optimized sequences of images, each of which is a progressively lower resolution representation of the same image. So, whenever you upload an image into the GPU, you should upload as well different versions of the same image with lower resolution:

Mipmap example, courtesy of wikipedia 🙂

Why we need it? Well, basically because otherwise you will see moire-effects like the shown in this video: https://youtu.be/fbz_E0aKVXM

So, for solving this effect, you should implement a piece of code that generates the images and uses automatically, and avoid this effect, as shown here: https://youtu.be/HwsCyCJwKyY

So the code for generating this mipmaps in opengl 4.5 is the following (please note that I am using stbi for loading the image file):

bool Texture::load(const std::string & file_name, bool flip)
{
	// If we already have loaded this texture, we unload it first
	if (m_textureID > 0) {
		glDeleteTextures(1, &m_textureID);
		m_textureID = 0;
		m_mipmapLevels = 1;
		mem = 0;
	}

	stbi_set_flip_vertically_on_load(flip); // required for loading textures properly

	filename = file_name;
	if (filename.empty())
		return false;

	bool is_loaded = true;

	unsigned char* data = stbi_load((filename).c_str(), &width, &height, &components, 0);

	if (data) {
		GLenum internalFormat = 0;
		GLenum dataFormat = 0;
		if (components == 1) {
			internalFormat = GL_R8;
			dataFormat = GL_RED;
		}
		else if (components == 2) {
			internalFormat = GL_RG8;
			dataFormat = GL_RG;
		}			
		else if (components == 3) {
			internalFormat = GL_RGB8;
			dataFormat = GL_RGB;
		}			
		else if (components == 4) {
			internalFormat = GL_RGBA8;
			dataFormat = GL_RGBA;
		}
			

		glCreateTextures(GL_TEXTURE_2D, 1, &m_textureID);
		glBindTexture(GL_TEXTURE_2D, m_textureID);
		glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
		
	
		m_mipmapLevels = (GLsizei)floor(log2(std::max(width, height)));
		if (m_mipmapLevels == 0)
			m_mipmapLevels = 1;
		glTextureStorage2D(m_textureID, m_mipmapLevels, internalFormat, width, height);

		glTextureParameteri(m_textureID, GL_TEXTURE_MIN_FILTER, m_mipmapLevels == 1 ? GL_LINEAR : GL_LINEAR_MIPMAP_LINEAR);
		glTextureParameteri(m_textureID, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

		glTextureParameteri(m_textureID, GL_TEXTURE_WRAP_S, GL_REPEAT);
		glTextureParameteri(m_textureID, GL_TEXTURE_WRAP_T, GL_REPEAT);
		
		glTextureSubImage2D(m_textureID, 0, 0, 0, width, height, dataFormat, GL_UNSIGNED_BYTE, data);
		if (m_mipmapLevels > 1)
			glGenerateTextureMipmap(m_textureID);
		
		if (m_mipmapLevels == 1)
			mem = (float)(width * height * components) / 1048576.0f;		// Calculate the texture mem (in mb)
		else
			mem = (float)(width * height * components * 1.33f) / 1048576.0f;		// Calculate the texture mem (in mb) for mipmaps

		// Unbind and restore pixel alignment
		glBindTexture(GL_TEXTURE_2D, 0);
		glPixelStorei(GL_UNPACK_ALIGNMENT, 4);

	}
	else {
		LOG->Error("Failed loading texture from file: %s", filename.c_str());
		is_loaded = false;
	}

	stbi_set_flip_vertically_on_load(false); // Set always to "false"
	stbi_image_free(data);

	return is_loaded;
}

The key code here is the following, which calculate the appropriate mipmap levels, and uses them:

m_mipmapLevels = (GLsizei)floor(log2(std::max(width, height)));
if (m_mipmapLevels == 0)
	m_mipmapLevels = 1;
glTextureStorage2D(m_textureID, m_mipmapLevels, internalFormat, width, height);

glTextureParameteri(m_textureID, GL_TEXTURE_MIN_FILTER, m_mipmapLevels == 1 ? GL_LINEAR : GL_LINEAR_MIPMAP_LINEAR);
glTextureParameteri(m_textureID, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

glTextureParameteri(m_textureID, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTextureParameteri(m_textureID, GL_TEXTURE_WRAP_T, GL_REPEAT);
		
glTextureSubImage2D(m_textureID, 0, 0, 0, width, height, dataFormat, GL_UNSIGNED_BYTE, data);
if (m_mipmapLevels > 1)
	glGenerateTextureMipmap(m_textureID);
	
if (m_mipmapLevels == 1)
	mem = (float)(width * height * components) / 1048576.0f;		// Calculate the texture mem (in mb)
else
	mem = (float)(width * height * components * 1.33f) / 1048576.0f;		// Calculate the texture mem (in mb) for mipmaps

Leave a Reply

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

spammer, go home! * Time limit is exhausted. Please reload the CAPTCHA.

This site uses Akismet to reduce spam. Learn how your comment data is processed.