Reputation: 1706
I am fairly new to OpenGL and i was following this tutorial on Shaders https://learnopengl.com/Getting-started/Textures. I can't get mine to work, as the screen ends up just being entirely black. This code uses glew and glut instead of glfw like in the tutorial.
What is going on?
glEnable(GL_TEXTURE_2D);
GL_TEXTURE0
active so the next call to glBindTexture(GL_TEXTURE_2D, texture)
is referenced to this texture unit.stbi_load
functionglTexImage2D
with parameters (width
, height
, texture_data
) that was passed on to stbi_load
beforehand as pointers so it loads them.ourShader
), attach vertex shader and fragment shader. Link "ourShader" program, and again print errors (none show up).Considerations:
gluPerspective
, near clipping plane 0.1 and far 1000 (since it's looking down -z it's shouldn't clipp the object, i also translate the object like so glTranslatef(0, 0, -10)
)This is myDisplay callback:
void myDisplay(void)
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDisable(GL_DEPTH_TEST);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glEnable(GL_TEXTURE_2D);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0, 0, -10);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
glUseProgram(ourShader);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glutSwapBuffers();
}
Main setup
int main(){
//glut initialization stuff...left this out
float vertices[] = {
// positions // colors // texture coords
0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top right
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom right
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom left
-0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f // top left
};
float texCoords[] = {
0.0f, 0.0f, // lower-left corner
1.0f, 0.0f, // lower-right corner
0.5f, 1.0f // top-center corner
};
unsigned int indices[] = {
0, 1, 3, // first triangle
1, 2, 3 // second triangle
};
//frameBuffer
glGenBuffers(1, &FBO);
glBindFramebuffer(GL_FRAMEBUFFER, FBO);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
std::cout << "ERROR: Framebuffer not bound" << std::endl;
glBindFramebuffer(GL_FRAMEBUFFER, 0);
//glDeleteFramebuffers(1, &fbo);
//VAO
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// Vertex Attributes
glEnableVertexAttribArray(0); //pozicija
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (void*)(0));
glEnableVertexAttribArray(1); //boja
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (void*)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(2); //boja
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (void*)(6 * sizeof(GLfloat)));
glEnable(GL_TEXTURE_2D);
glGenTextures(1, &texture);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
std::string path = "C:\\Projects\\Myproj\\container.jpg";
bool success = loadTexture(path,texture_data,&tex_width, &tex_height, &nchannels);
if (success)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tex_width, tex_height, 0, GL_RGB, GL_UNSIGNED_BYTE, texture_data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "ERROR::STBI::FAILED TO LOAD TEXTURE" << std::endl;
}
stbi_image_free(texture_data);
int success_f;
char infoLog[512];
vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
getShaderStatus(vertexShader, &success_f, infoLog);
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
getShaderStatus(fragmentShader, &success_f, infoLog);
ourShader = glCreateProgram();
glAttachShader(ourShader, vertexShader);
glAttachShader(ourShader, fragmentShader);
glLinkProgram(ourShader);
getShaderStatus(ourShader, &success_f, infoLog);
glUseProgram(ourShader);
glUniform1i(glGetUniformLocation(ourShader, "ourTexture"), 0);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
glutMainLoop();
}
Vertex Shader
const char* vertexShaderSource =
"#version 330 core\n"
"layout(location = 0) in vec3 aPos;\n"
"layout(location = 1) in vec3 aColor;\n"
"layout(location = 2) in vec2 aTexCoord;\n"
"out vec3 ourColor;\n"
"out vec2 TexCoord;\n"
"void main()\n"
"{\n"
"gl_Position = vec4(aPos, 1.0);\n"
"ourColor = aColor;\n"
"TexCoord = aTexCoord;\n"
"}\n";
Fragment Shader
const char* fragmentShaderSource =
"#version 330 core\n"
"out vec4 FragColor;\n"
"in vec3 ourColor;\n"
"in vec2 TexCoord;\n"
"uniform sampler2D ourTexture;\n"
"void main()\n"
"{\n"
"FragColor = texture(ourTexture, TexCoord);\n"
"}\n";
UPDATE LOG - SOLVED
Projection, View & Model transform go inside the shader, use
glm::projection
glm::lookat
Enable attributes (color, texture) before render
glutInitContextVersion(3, 3);
glutInitContextProfile(GLUT_CORE_PROFILE);
Final code is here : [ https://pastebin.com/D8DgPqaT ]
Psychedelic Shaders: [ https://pastebin.com/vtJ3Cr6i ]
Upvotes: 2
Views: 2898
Reputation: 210878
glEnable(GL_TEXTURE_2D)
is unnecessary when you use a shader, because weather the texture is used or not is implemented in the (fragment) shader.
Your shader code defines if the texture is applied or not. glEnable(GL_TEXTURE_2D)
is for use of the deprecated Fixed Function Pipeline, without a shader only.
Also using the fixed function matrix stack (glMatrixMode
, glLoadIdentity
, gluPerspective
, ...) is useless. You have to use Uniform variables (of type mat4
).
This functions change the matrices on the fixed function matrix stack. This matrices are applied to the fixed function vertex coordinates which are set by glVertex
or glVertexPointer
, but only if there is no shader code.
If there is a shader you have to do the matrix transformations by yourself.
In GLSL there exist the built-in uniforms like gl_ModelViewMatrix
, gl_ProjectionMatrix
or gl_ModelViewProjectionMatrix
which allow you to access the matrices on the fixed function matrix stack. But this uniforms are deprecated, too and not any more accessible in GLSL #version 330 core
.
Write a shader with 2 uniforms mat4 u_proj
and mat4 u_view
. Transform the vertex coordinate by the shader code.
#version 330 core
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec3 aColor;
layout(location = 2) in vec2 aTexCoord;
out vec3 ourColor;
out vec2 TexCoord;
uniform mat4 u_proj;
uniform mat4 u_view;
void main()
{
gl_Position = u_proj * u_view * vec4(aPos, 1.0);
ourColor = aColor;
TexCoord = aTexCoord;
}
Get The uniform locations after the program is linked:
GLint proj_loc = glGetUniformLocation(ourShader, "u_proj");
GLint view_loc = glGetUniformLocation(ourShader, "u_view");
Calculate the projection matrix an view matrix. I recommend to use the OpenGL Mathematics (GLM) library which is designed to do OpenGL and GLSL relate calculations and based on the OpenGL Shading Language (GLSL). See also glm::perspective
and glm::lookAt
:
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
</>
glm::mat4 proj = glm::perspective(glm::radians(fov_degrees)), aspect, near, far);
glm::mat4 view = glm::lookAt(eye, target, up);
Set the uniforms after the program is installed (glUseProgram
) by glUniformMatrix4fv
:
glUniformMatrix4fv(proj_loc, 1, GL_FALSE, glm::value_ptr(proj));
glUniformMatrix4fv(view_loc, 1, GL_FALSE, glm::value_ptr(view));
Review of code [https://pastebin.com/KWDxcEen] and shaders [https://pastebin.com/yq3rQEga]
If a shader has been successfully compiled has to be checked by glGetShaderiv
glGetShaderiv(shader_ID, GL_COMPILE_STATUS, &status);
in compare to check if a program has been successfully linked, which has to be checked by glGetProgramiv
glGetProgramiv(program_ID, GL_LINK_STATUS, success_f);
The array texture coordinates consist of tuples with a size of 2 (u, v):
glVertexAttribPointer(2,
2, // <---- 2 instead of 3
GL_FLOAT,
GL_FALSE,
8 * sizeof(GLfloat),
(void*)(6 * sizeof(GLfloat))
);
The 3rd parameter of glDrawArrays
is the number of vertces, but not the number of primitives:
glDrawArrays(GL_TRIANGLES, 0, 6); // <---- 6 instead of 2
the 2nd parameter of loadTexture
has to be an output parameter. It has to be a reference (unsigned char *& buff
):
bool loadTexture(
const std::string &filename,
unsigned char*& buff, // < ---- unsigned char*&
int* w, int* h, int* nc);
The sate if a vertex attribute is enabled or disabled is stored in the Vertex Array Object. There is not need of doing this again in the display function:
void myDisplay(void)
{
// ....
glBindVertexArray(VAO);
// unecessary:
//glEnableVertexAttribArray(0);
//glEnableVertexAttribArray(1);
//glEnableVertexAttribArray(2);
Finally I recommend to add a glutPostRedisplay()
call to the display function (myDisplay
), for a continuously update of the scene.
Upvotes: 2