OpenGL tutorial 3 (texture)
https://github.com/nothings/stb
To use the texture, normally use the stb opensource library.
in this case, we just use the stb_image.h.
but the recommend progress is using stb_image.cpp
//stb_image.cpp
#define STB_IMAGE_IMPLEMENTATION
#include<stb/stb_image.h>
the reason of the above process (GPT)
Using the STB library for loading images, such as `stb_image.h`, requires some additional steps to ensure proper compilation and linking. The process you described is known as the "STB_IMAGE_IMPLEMENTATION" macro trick.
The reason for this additional step is related to how the STB library is designed. STB libraries are header-only libraries, which means they are implemented entirely in header files. When you include the header file in your project, the implementation is also brought into your code. However, the actual implementation of functions and data resides in the header file itself, which can lead to multiple definition errors during linking if the same functions and data are included in multiple translation units (source files).
By defining the `STB_IMAGE_IMPLEMENTATION` macro before including `stb_image.h`, you are telling the STB library to include the implementation only in one translation unit. This effectively means that the implementation of the functions in `stb_image.h` will be present in the `stb.cpp` file, and you should compile `stb.cpp` along with your project.
Here's why this works:
1. When you include `stb_image.h` in your project without the `STB_IMAGE_IMPLEMENTATION` macro, you are essentially including function declarations and external variable declarations.
2. When you define the `STB_IMAGE_IMPLEMENTATION` macro before including `stb_image.h`, it will replace the function declarations and external variable declarations with actual function definitions and variable initializations. This effectively puts the implementation in that translation unit (in this case, `stb.cpp`).
3. By compiling `stb.cpp` together with your project, you avoid multiple definition errors. Each translation unit that includes `stb_image.h` will only see the declarations (from the header), and the actual implementation will be present only in `stb.cpp`.
So, the key takeaway is that using the `STB_IMAGE_IMPLEMENTATION` macro trick allows you to include the STB library in multiple source files without running into linking errors due to multiple definitions of the same functions and data.
In summary, including the header file alone is not sufficient for the STB library; you need to use the `STB_IMAGE_IMPLEMENTATION` macro and compile the implementation once in a separate file to avoid multiple definition issues.
In bytes variable, we include the image location and name, width, height, channel(rgb), and we dont have to know
stbi_load() function detailed (GPT)
The parameters are as follows:
filename: A string containing the path to the image file you want to load.
x: A pointer to an integer that will receive the width of the loaded image in pixels.
y: A pointer to an integer that will receive the height of the loaded image in pixels.
comp: A pointer to an integer that will receive the number of components per pixel in the loaded image (e.g., 3 for RGB, 4 for RGBA).
req_comp: An integer indicating the number of color components you want to load per pixel. This parameter allows you to request a specific number of components, even if the original image has more components per pixel. The possible values are:
0: If req_comp is set to 0, stbi_load() will load the number of components present in the image file. For example, if the image is in RGB format, it will load 3 components per pixel.
1: Load a single color channel (e.g., red) for each pixel.
2: Load two color channels (e.g., red and green) for each pixel.
3: Load three color channels (e.g., red, green, and blue) for each pixel.
4: Load all four color channels (red, green, blue, and alpha) for each pixel.
By specifying req_comp, you can choose to load only the color channels you need, which can be useful for various image processing tasks or when you want to reduce memory usage.
For example, if you only need the grayscale representation of an image, you can set req_comp to 1. If you need to work with RGB images, you can set req_comp to 3 to get the red, green, and blue color channels without the alpha channel.
Keep in mind that stbi_load() returns a pointer to the image data in memory, which you'll need to manage and eventually free using stbi_image_free() when you're done with it.
//texture
int widthImg, heightImg, numColCh;
unsigned char* bytes = stbi_load("pop_cat.png", &widthImg, &heightImg, &numColCh, 0);
GLuint texture; //reference variable
glGenTextures(1, &texture);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// the delete order is important!
glDeleteTexture(1, &texture);
and create the texture object, its just like any opengl object.
1. we first create a reference variable of type GLuint and name it texture
2. use glGenTextures to generate the texture object
giving it the number of textures(1), and the pointer to the reference variable (texture)
3. to activate the texture unit, we use the glActiveTexture
4. finally binding our texture with glBindTexture.
glTexParameteri() function is tweak our texture's settings
1. input the type of our texture,
2. the setting we want to modify
3. the value we want to give our setting
How the texture is sampled and interpolated?
in this case we modify both of the MIN_FILTER and MAG_FILTER settings
How the texture is repeated?
in this case we modify both of the GL_TEXTURE_WRAP_S and GL_TEXTURE_WRAP_T settings (cause its 2D object)
axes : in stb library, the axes named s, t, r
coreesponding to the common x, y, z axes.
The glTexImage2D function in OpenGL is used to specify a two dimensional texture image.
It allocates texture memory and sets the texture image data for the currently bound texture object.
Here's the function signature: (GPT)
void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height,
GLint border, GLenum format, GLenum type, const void *data);
Let's go through each parameter of glTexImage2D:
- target: Specifies the target texture. It can be one of the following:
- GL_TEXTURE_2D: A two-dimensional texture.
- Other texture targets like GL_TEXTURE_CUBE_MAP, GL_TEXTURE_3D, etc., are also available, but for glTexImage2D, we focus on GL_TEXTURE_2D.
- level: Specifies the level-of-detail number. This parameter is used in mipmapping, where you can have multiple versions of the same texture at different levels of detail. The base level is 0, representing the original texture size, and each subsequent level halves the width and height. For example, level 1 is half the size of level 0, level 2 is half the size of level 1, and so on. Mipmapping helps improve rendering performance and image quality in certain situations.
- internalformat: Specifies the number of color components and the data type used for each component in the texture image. This parameter is used to define how the texture data is stored in GPU memory. Some common formats include:
- GL_RGBA: 4 components (Red, Green, Blue, Alpha) using 8 bits per component.
- GL_RGB: 3 components (Red, Green, Blue) using 8 bits per component.
- GL_RGBA16F: 4 components using 16-bit floating-point format (half-float).
- GL_RGB32F: 3 components using 32-bit floating-point format.
- width: Specifies the width of the texture image in pixels.
- height: Specifies the height of the texture image in pixels.
- border: Specifies the width of the border of the texture. It must be 0 for glTexImage2D.
- format: Specifies the format of the pixel data provided in the data parameter. This is how the data is organized in memory. Common formats include:
- GL_RGBA: 4 components (Red, Green, Blue, Alpha).
- GL_RGB: 3 components (Red, Green, Blue).
- GL_RED: Single component (Red).
- type: Specifies the data type of the pixel data provided in the data parameter. Common types include:
- GL_UNSIGNED_BYTE: Each color component is an unsigned byte (8 bits).
- GL_FLOAT: Each color component is a single-precision floating-point value.
- data: A pointer to the actual pixel data of the texture image. This is where you provide the image data to be used for the texture. The layout and organization of the data should match the format and type parameters.
After calling glTexImage2D, the texture image data is uploaded to the GPU memory and associated with the currently bound texture object of the specified target. This allows you to use the texture in subsequent rendering operations.
After calling glTexImage2D, the texture image data is uploaded to the GPU memory and
associated with the currently bound texture object of the specified target.
This allows you to use the texture in subsequent rendering operations.
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, widthImg, heightImg, 0, GL_RGBA, GL_UNSIGNED_INT, bytes);
glGenerateMipmap(GL_TEXTURE_2D);
stbi_image_free(bytes);
glBindTexture(GL_TEXTURE_2D, 0);
so we've already imported the image data into the texture, we'll want to delete the data using stbi_image_free
and unbind the texture so we don't accidentally do something
glGenerateMipmap(GL_TEXTURE_2D);
The glGenerateMipmap function generates mipmaps for the specified texture target.
A mipmap is a set of precomputed, downsampled versions of the original texture at different levels of detail.
By calling this function after glTexImage2D, you are instructing OpenGL to automatically generate mipmaps for the 2D texture. Each mipmap level is half the size of the previous level, starting from the original texture resolution. Mipmapping helps improve rendering quality, especially when a textured object appears at different distances or scales in the scene.
Now, to address your question regarding mipmaps for a 2D texture: While it's true that you might not need mipmaps for a basic 2D texture, using mipmaps can still provide benefits, especially in cases where the texture might be displayed at various scales, or if the texture covers a large portion of the screen. Mipmaps can help reduce texture aliasing and visual artifacts during minification (making the texture smaller) or magnification (making the texture larger).
and finally, since we create it, we also want to delete it