Textures in OpenGL - hls333555/OpenGL GitHub Wiki
We will use a library called stb_image which can be grabbed from stb library to load the png file.
Basically, what it's going to do is to return a pointer to a buffer of RGBA pixels after giving it a file path. Then, we can take that pixel array and upload it to the GPU via OpenGL as a texture. After that, we can modify the shader to read the texture when it is drawing.
Here are the main steps:
-
Add stb_image.h to the project and create stb_image.cpp containing the following code:
// This is required above #include "stb_image.h", see stb_image.h for details #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h"
-
Load the png file:
// You should assign a png file path std::string filePath; unsigned char* localBuffer; int width, height, BPP; // OpenGL expects the texture pixels to start at the bottom-left(0,0) instead of the top-left // Typically, when png image is being loaded, it is stored in scanlines from the top to the bottom of the image, so we need to flip it on load stbi_set_flip_vertically_on_load(1); localBuffer = stbi_load(filePath.c_str(), &width, &height, &BPP, 4/*RGBA*/);
-
Create the texture and set its parameters:
unsigned int texture; // Generate texture names glGenTextures(1, &texture); // Bind a named texture to a texturing target, you can specify the slot using glActiveTexture() in advance glBindTexture(GL_TEXTURE_2D, texture); // Set texture parameters // This is the minification filter that how the texture will be resampled down if it needs to be rendered smaller per pixel glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
Send OpenGL the texture data:
// Send OpenGL the texture data glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8/*8-bits per channel*/, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, localBuffer); // Unbind textures from a texturing target glBindTexture(GL_TEXTURE_2D, 0);
-
Free the local buffer when you don't need it:
if (localBuffer) { // Free the local buffer stbi_image_free(localBuffer); }
-
Bind texture to a slot and tell the shader:
// Select active texture unit(slot) glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture); int location = glGetUniformLocation(program, "u_Texture"); // Tell the shader which texture slot to sample from, the slot must be the same as the selected active texture slot glUniform1i(location, GL_TEXTURE0);
-
Add texture coordinates to tell the geometry being rendered which part of the texture to sample from. Furthermore, when it's up to rendering a certain pixel, tell the shader to sample a certain area of the texture to retrieve what color the pixel should be.
-
Modify the vertex data array to accept texture coordinates:
// Two floats for vertex position and two floats for texture coordinate // For texture coordinate system, the bottom-left is (0,0), the top-right is (1,1) float positions[] = { -0.5f, -0.5f, 0.f, 0.f, // 0 0.5f, -0.5f, 1.f, 0.f, // 1 0.5f, 0.5f, 1.f, 1.f, // 2 -0.5f, 0.5f, 0.f, 1.f // 3 };
-
Enable and define the texture coordinate vertex attribute data:
// Enable the texture coordinate vertex attribute data glEnableVertexAttribArray(1); // Do not forget this! // Define a texture coordinate vertex attribute data glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (const void*)8);
-
Replace
glBufferData(GL_ARRAY_BUFFER, 4 * 2 * sizeof(float), positions, GL_STATIC_DRAW);
with:
glBufferData(GL_ARRAY_BUFFER, 4 * 4 * sizeof(float), positions, GL_STATIC_DRAW);
-
Next is to modify the shader code:
-
For the vertex shader part:
-
Pass in the texture coordinate vertex attribute data:
layout(location = 1) in vec2 texCoord;
-
Define a variable to be passed out to the fragment shader:
out vec2 v_texCoord;
-
Assign that variable in the
main()
function:v_texCoord = texCoord;
-
-
For the fragment shader part:
-
Pass in the texture coordinate from the vertex shader:
in vec2 v_texCoord;
-
Define a variable to get texture via uniform:
uniform sampler2D u_Texture;
-
Output the final color in the
main()
function:// Draw the pixel from the texture in the scene by knowing the precise location in the texture to look up color = texture(u_Texture, v_texCoord);
-
-
-
-
That's it! You will now see a beautiful texture like below: