Search code examples
c++openglglslshader

OpenGL shader problem (not displaying anything)


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?

  • I have a list of vertices, which i then store in the VBO, and it's attributes bind by VAO
  • Setting vertex attributes in order 3 vertex + 3 colors + 2 tex coords
  • glEnable(GL_TEXTURE_2D);
  • made sure to make GL_TEXTURE0 active so the next call to
  • glBindTexture(GL_TEXTURE_2D, texture) is referenced to this texture unit.
  • I load the texture with stbi_load function
  • Load up texture with glTexImage2D with parameters (width, height, texture_data) that was passed on to stbi_load beforehand as pointers so it loads them.
  • Create vertex shader, attach source (in VertexShaders.h), compile shader, print shader errors (none show up)
  • Create fragment shader, attach source(in FragmentShaders.h), compile shader, print shader errors(none show up)
  • Create a shader program (ourShader), attach vertex shader and fragment shader. Link "ourShader" program, and again print errors (none show up).

Considerations:

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

  • Don't need glEnable(GL_TEXTURE_2D) when implementing shader
  • Projection, View & Model transform go inside the shader, use

    glm::projection glm::lookat

  • Enable attributes (color, texture) before render

  • Freeglut support for OpenGL API (>=3.2)

glutInitContextVersion(3, 3); glutInitContextProfile(GLUT_CORE_PROFILE);

Final code is here : [ https://pastebin.com/D8DgPqaT ]

Psychedelic Shaders: [ https://pastebin.com/vtJ3Cr6i ]


Solution

  • 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.