This question has an answer here:
https://stackoverflow.com/questions/42596516/generating-mipmaps-manually
It was a silly mistake. Sorry for the inconvenience.
OpenGL is fairly difficult to learn.
According to my understanding of mipmaps, I can set each mipmap level manually, so I could change the image to use when my texture looks greater or smaller.
I tried the following code but doesn't matter the size of the triangle, it always take the first mipmap level. Why? if the triangle is closer to any other mipmap level, why does not take it?
I really hate putting a bunch of code in my questions because I force others to waste time analyzing the code instead of putting the most revelant snippet code, but I don't know where is the problem and I have not alternative. Sorry :(
#include <iostream>
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
const GLchar* VERTEX_SHADER_SOURCE =
"#version 330 core \n"
"layout (location = 0) in vec2 position; \n"
"layout (location = 1) in vec2 myTexCoord; \n"
" \n"
"out vec2 TexCoord; \n"
" \n"
"void main() \n"
"{ \n"
" gl_Position = vec4(position,0.0f, 1.0f);\n"
" TexCoord = myTexCoord; \n"
"} \n";
const GLchar* FRAGMENT_SHADER_SOURCE =
"#version 330 core \n"
"in vec2 TexCoord; \n"
" \n"
"out vec4 color; \n"
" \n"
"uniform sampler2D ourTexture; \n"
" \n"
"void main() \n"
"{ \n"
" color = texture(ourTexture, TexCoord);\n"
"} \n";
int main()
{
//INIT STUFFS
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
GLFWwindow* window = glfwCreateWindow(800, 600, "", nullptr, nullptr);
glfwMakeContextCurrent(window);
glewExperimental = GL_TRUE;
glewInit();
glViewport(0, 0, 800, 600);
//THE MOST INTERESTING PART
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
//Our image for the mipmap with level 0 and white color
//The dimensions are 200 x 200 and is multiplied by 3 because I defined R,G and B colors
unsigned char imageArray[200*200*3];
for(int i = 0; i < 200*200*3; i++){
imageArray[i] = 255;
}
//Our image for the mipmap with level 1-8 and blue color
unsigned char imageArray2[100*100*3];
for(int i = 0; i < 100*100*3; i++){
if((i+1)%3 == 0)
imageArray2[i] = 255;
else
imageArray2[i] = 0;
}
//All mipmap levels
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 200, 200, 0, GL_RGB, GL_UNSIGNED_BYTE, imageArray);
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGB, 100, 100, 0, GL_RGB, GL_UNSIGNED_BYTE, imageArray2);
glTexImage2D(GL_TEXTURE_2D, 2, GL_RGB, 50, 50, 0, GL_RGB, GL_UNSIGNED_BYTE, imageArray2);
glTexImage2D(GL_TEXTURE_2D, 3, GL_RGB, 25, 25, 0, GL_RGB, GL_UNSIGNED_BYTE, imageArray2);
glTexImage2D(GL_TEXTURE_2D, 4, GL_RGB, 13, 13, 0, GL_RGB, GL_UNSIGNED_BYTE, imageArray2);
glTexImage2D(GL_TEXTURE_2D, 5, GL_RGB, 7, 7, 0, GL_RGB, GL_UNSIGNED_BYTE, imageArray2);
glTexImage2D(GL_TEXTURE_2D, 6, GL_RGB, 4, 4, 0, GL_RGB, GL_UNSIGNED_BYTE, imageArray2);
glTexImage2D(GL_TEXTURE_2D, 7, GL_RGB, 2, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, imageArray2);
glTexImage2D(GL_TEXTURE_2D, 8, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, imageArray2);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
//If I comment the line above and uncomment the following, the program will show a black triangle. Why?
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST_MIPMAP_NEAREST);
GLfloat vertices[] =
//(un)comment the first and second lines of vertex coords to swap between a bigger or smaller triangle
//It doesn't matter, this program always take the first mipmap level
//But... Why? the triangle should be closer to any other mipmap level :( why does not take it?
{
// -1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 1.0f, //Bigger triangle
-0.05f, -0.05f, 0.05f, -0.05f, 0.0f, 0.05f, //Smaller triangle
-1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 1.0f //Triangle's texture coordinates
};
//VAOs AND VBOs
GLuint VAO, VBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (GLvoid*)0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (GLvoid*)(sizeof(GLfloat)*3*2));
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
//SHADERS
GLuint program;
GLuint vertex, fragment;
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &VERTEX_SHADER_SOURCE, NULL);
glCompileShader(vertex);
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &FRAGMENT_SHADER_SOURCE, NULL);
glCompileShader(fragment);
program = glCreateProgram();
glAttachShader(program, vertex);
glAttachShader(program, fragment);
glLinkProgram(program);
glDeleteShader(vertex);
glDeleteShader(fragment);
glUseProgram(program);
//DRAWING OUR TRIANGLE
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0,3);
glfwSwapBuffers(window);
while (!glfwWindowShouldClose(window))
glfwPollEvents();
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glfwTerminate();
return 0;
}
For any reason, these two lines doesn't work together
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST_MIPMAP_NEAREST);
EDIT:
I think that the two lines above doesn't work because mipmaps only works with GL_TEXTURE_MIN_FILTER. However, when I replace
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
//If I comment the line above and uncomment the following, the program will show a black triangle. Why?
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST_MIPMAP_NEAREST);
with
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
it shows a black triangle :(
I used glGetError as Dan Hulme stated in the comments but with the last change I made to my code I have not errors returned by glGetError but still showing a black triangle.
EDIT 2:
I solved the problem setting the sizes by powers of 2. However, it now takes only the first mipmap level. Here is the new source code:
#include <iostream>
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
const GLchar* VERTEX_SHADER_SOURCE =
"#version 330 core \n"
"layout (location = 0) in vec2 position; \n"
"layout (location = 1) in vec2 myTexCoord; \n"
" \n"
"out vec2 TexCoord; \n"
" \n"
"void main() \n"
"{ \n"
" gl_Position = vec4(position,0.0f, 1.0f);\n"
" TexCoord = myTexCoord; \n"
"} \n";
const GLchar* FRAGMENT_SHADER_SOURCE =
"#version 330 core \n"
"in vec2 TexCoord; \n"
" \n"
"out vec4 color; \n"
" \n"
"uniform sampler2D ourTexture; \n"
" \n"
"void main() \n"
"{ \n"
" color = texture(ourTexture, TexCoord);\n"
"} \n";
int main()
{
//Change the value of size by 1,2,4,8,16,32,64,128 or 256 and see how the color of the triangle doesn't change. Why does it happen?
GLint size = 128;
//INIT STUFFS
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
GLFWwindow* window = glfwCreateWindow(size,size, "", nullptr, nullptr);
glfwMakeContextCurrent(window);
glewExperimental = GL_TRUE;
glewInit();
glViewport(0, 0,size,size);
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
//Our image for the mipmap with odd level and green color
//The dimensions are 256 x 256 and is multiplied by 3 because I defined R,G and B colors
unsigned char imageArray[256*256*3];
for(int i = 0; i < 256*256*3; i++){
if((i+2)%3 == 0)
imageArray[i] = 255;
else
imageArray[i] = 0;
}
//Our image for the mipmap with pair level and red color
unsigned char imageArray2[256*256*3];
for(int i = 0; i < 256*256*3; i++){
if(i%3 == 0)
imageArray2[i] = 255;
else
imageArray2[i] = 0;
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 8);
//All mipmap levels defined by hand
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 256, 256, 0, GL_RGB, GL_UNSIGNED_BYTE, imageArray);
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGB, 128, 128, 0, GL_RGB, GL_UNSIGNED_BYTE, imageArray2);
glTexImage2D(GL_TEXTURE_2D, 2, GL_RGB, 64, 64, 0, GL_RGB, GL_UNSIGNED_BYTE, imageArray);
glTexImage2D(GL_TEXTURE_2D, 3, GL_RGB, 32, 32, 0, GL_RGB, GL_UNSIGNED_BYTE, imageArray2);
glTexImage2D(GL_TEXTURE_2D, 4, GL_RGB, 16, 16, 0, GL_RGB, GL_UNSIGNED_BYTE, imageArray);
glTexImage2D(GL_TEXTURE_2D, 5, GL_RGB, 8, 8, 0, GL_RGB, GL_UNSIGNED_BYTE, imageArray2);
glTexImage2D(GL_TEXTURE_2D, 6, GL_RGB, 4, 4, 0, GL_RGB, GL_UNSIGNED_BYTE, imageArray);
glTexImage2D(GL_TEXTURE_2D, 7, GL_RGB, 2, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, imageArray2);
glTexImage2D(GL_TEXTURE_2D, 8, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, imageArray);
GLfloat vertices[] = {
-1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 1.0f, //vertex coordinates
0.0f, 0.0f //Triangle's texture coordinates
};
//VAOs AND VBOs
GLuint VAO, VBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (GLvoid*)0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (GLvoid*)(sizeof(GLfloat)*3*2));
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
//SHADERS
GLuint program;
GLuint vertex, fragment;
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &VERTEX_SHADER_SOURCE, NULL);
glCompileShader(vertex);
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &FRAGMENT_SHADER_SOURCE, NULL);
glCompileShader(fragment);
program = glCreateProgram();
glAttachShader(program, vertex);
glAttachShader(program, fragment);
glLinkProgram(program);
glDeleteShader(vertex);
glDeleteShader(fragment);
glUseProgram(program);
//DRAWING OUR TRIANGLE
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0,3);
glfwSwapBuffers(window);
while (!glfwWindowShouldClose(window))
glfwPollEvents();
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glfwTerminate();
return 0;
}
imageArray2
loop would be faster asfor (int i = 0; i < 100 * 100 * 3; i += 3) { imageArray2[i] = 0; imageArray2[i + 1] = 0; imageArray[i + 2] = 255; }
because it avoids a division inside the loop. I think it's clearer this way too. $\endgroup$glGetError
everywhere and see if any of the gl* function calls are failing. $\endgroup$GL_TEXTURE_MIN_FILTER
toGL_NEAREST
means the mipmaps won't be used, which is why you only see level 0 when you do this. $\endgroup$