c++openglglslglfwglm-math

OpenGL program is showing black screen only, while it should be showing a square


Here is my main.cpp

#define STB_IMAGE_IMPLEMENTATION

#include"stb_image.h"
#include<GL/glew.h> // It links from the driver to the opengl function pointers
#include<GLFW/glfw3.h>
#include<iostream>
#include<glm/glm.hpp>
#include<glm/vec2.hpp>
#include<glm/vec3.hpp>
#include<glm/vec4.hpp>
#include<glm/mat4x4.hpp>
#include<glm/gtc/matrix_transform.hpp>
#include<glm/gtc/type_ptr.hpp>
#include<SOIL2/SOIL2.h>
#include<string>
#include<vector>
#include<fstream>

#define GLEW_STATIC

struct Vertex{
    glm::vec3 position;
    glm::vec3 color;
    glm::vec2 texcoord;
};


void framebuffer_size_callback(GLFWwindow* window, int width, int height){ // For the resizeability
    glViewport(0, 0, width, height);
}

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 
    };
unsigned int indices[] = {  
    0, 1, 2,
    0, 2, 3 // second triangle
};

unsigned nofVertices = sizeof(vertices)/sizeof(Vertex); // WIll work here, but if I am sending it to a function and then recienving a pointer then sizeof() will take the size of the pointer and will give errors.


unsigned nofIndices = sizeof(indices)/sizeof(GLuint);







/// Handling Input. This one is currently just closes the window at esc.
void updateInput(GLFWwindow* window){
    if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS){
        glfwSetWindowShouldClose(window, true);
    }
}

// Loading shaders:
bool loadshaders(GLuint &program){
    bool flag = true;
    char infoLog[512];
    GLint success;
    // glsl ig compiles ie links in runtime.

    std::string temp="";
    std::string src="";

    std::ifstream in_file;

    in_file.open("vertex_core.glsl");

    if(in_file.is_open()){
        while(std::getline(in_file, temp)){
            src+=temp+'\n';
        }
    }
    else{
        flag=false;
        std::cout<<"ERROR::LOADSHADERS::COULDNOT_OPEN_THE_FILE\n";
    }

    in_file.close();

    // createing a vertex shader.
    GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); // id of vertex shader beign created in the background, of gl memory. 

    // setting a source to the shader;
    const GLchar* vertexSRC = src.c_str();

    glShaderSource(vertexShader, 1, &vertexSRC, NULL);
    glCompileShader(vertexShader); //  compiles the shader from source.
    
    // checking any compile error:
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);

    if(!success){
        glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
        std::cout<<"ERROR: "<<infoLog<<"\n";
    }

    temp="";
    src="";



    // Making a fragment:
    in_file.open("fragment_core.glsl");

    if(in_file.is_open()){
        while(std::getline(in_file, temp)){
            src+=temp+'\n';
        }
    }
    else{
        std::cout<<"ERROR::LOADSHADERS::COULDNOT_OPEN_THE_FILE\n";
    }

    in_file.close();

    // createing a vertex shader.
    GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER ); // id of vertex shader beign created in the background, of gl memory. 

    // setting a source to the shader;
    const GLchar* fragSRC = src.c_str();

    glShaderSource(fragmentShader, 1, &fragSRC, NULL);
    glCompileShader(fragmentShader); //  compiles the shader from source.
    
    // checking any compile error:
    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);

    if(!success){
        glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
        std::cout<<"ERROR: "<<infoLog<<"\n";
    }


    // Program
    // attaching shaders to a program:
    program = glCreateProgram();

    glAttachShader(program, vertexShader);
    glAttachShader(program, fragmentShader);

    glLinkProgram(program);

    glGetProgramiv(program,GL_LINK_STATUS, &success);

    // error handling:
    if(!success){
        glGetProgramInfoLog(program, 512, NULL, infoLog);
        std::cout<<infoLog<<std::endl;
    }

    // End: resets everything
    glUseProgram(0);
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    return flag;
}

GLuint Texture; // Make this global or return it from function

void setTexture() {
    glGenTextures(1, &Texture);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, Texture);
    
    // Settings
    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);

    // Load image
    stbi_set_flip_vertically_on_load(true);  // Add this
    int width, height, nrChannels;
    unsigned char *data = stbi_load("sumner-mahaffey-7Y0NshQLohk-unsplash.jpg", &width, &height, &nrChannels, 0);
    if (!data) {
    std::cout << "Failed to load texture" << std::endl;
}
    if (data) {
        std::cout << "Channels: " << nrChannels << std::endl;
        GLenum format = (nrChannels == 4) ? GL_RGBA : GL_RGB;
        glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);
    }
    stbi_image_free(data);
}



int main(){
    // GLFWwindow* window = glfwCreateWindow();
    glfwInit(); // initializing the glfw
   
    // ! Consts
    const int WINDOW_WIDTH = 640;
    const int WINDOW_HEIGHT = 800;
    // framebuff can be diff that window
    int frame_buff_width = 0; // Its the canvas that been strecthed to fit the frame.
    int frame_buff_height = 0; 

    // To set some options to OPENGL.
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);  // this one sets the version of opengl to use.
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // To use the modern version of the core functions.
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
    // glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); for Mac

    // Create Window
    GLFWwindow* window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "My window", NULL, NULL);

    glfwGetFramebufferSize(window, &frame_buff_width, &frame_buff_height); // To set the Frame buffer size

    // set Viewport: how much  window we are drawing on
    glViewport(0,0,frame_buff_width,frame_buff_height); // Canvas size

    // Set Context to the window for glew and all libs.
    glfwMakeContextCurrent(window); // ! Very Important.


    // Init GLEW (It needs a window and openGL context to properly run)
    glewExperimental = GL_TRUE; // to use modern opengl functionalities.

    // Check glew Init
    if(glewInit() != GLEW_OK){
        std::cout<<"ERROR::openGL_init.cpp::GLEW_INIT_FAILED\n";
        glfwTerminate();
    }


    // Some OpenGL options:
    // As openGL is a state machine. we can enable some setting that are always running in the background

    glEnable(GL_DEPTH_TEST); // This lets us draw depth

    glEnable(GL_CULL_FACE); // This helps us in such a way that if something is blocked some some other obj it dont get rendered at all.

    glCullFace(GL_BACK); // Telling what not to render. ie the backside.
    glFrontFace(GL_CCW); // counter clock wise

    glEnable(GL_BLEND); // enable the blending of colors etc.
    glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); // doing setting of the blend.

    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); // LINE for jus lines. fill for filling

    // Shader Initialization
    GLuint core_program;
    loadshaders(core_program);

    // MODEL
    
    // sending vertices/data to the shaders
    
    // VAO, VBO. EBO
    // vertex Aarray object. Id for the whole model
    // Vertex buffer object. sends vertex info to GC.
    // Vertex element object. sends indices data to the GC.

    // Generating VAO and Binding
    GLuint VAO;
    glCreateVertexArrays(1, &VAO); // We kind of created a shell to use.
    glBindVertexArray(VAO); // binding it to the variable

    // Generating VBO and sending Data
    GLuint VBO;
    glGenBuffers(1, &VBO); // genertating the buffer
    glBindBuffer(GL_ARRAY_BUFFER, VBO); // 1st: array buffer tells the type of buffer kind of
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);  // static draw cause we don't want to change the drawing often. use other enums for dynamic usage

    // Generating VEO and sending Data
    GLuint EBO;
    glGenBuffers(1, &EBO); // genertating the buffer
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);  // static draw cause we don't want to change the drawing often. use other enums for dynamic usage

    // We don't wnat to send data again. as sending mem from cpu to gpu is expensive.

    // seting VertexAttribute pointer and enable some options (input assembly)
    // GLuint attribLoc = glGetAttribLocation(core_program, "vertex_position"); /// Can give the index like this way also
    
    // Position
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);

    // Color
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);

    // TextureCoord
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
    glEnableVertexAttribArray(2);

    // Bind VAO 0
    glBindVertexArray(0);
    setTexture();
    glUseProgram(core_program);
    GLint texLoc = glGetUniformLocation(core_program, "OurTexture");
    if (texLoc == -1) {
        std::cout << "Failed to find uniform location for 'OurTexture'" << std::endl;
    } else {
        glUniform1i(texLoc, 0); // Ensure this is done after glUseProgram
    }
    

    // glUniform1i(glGetUniformLocation(shader.ID, "texture1"), 0);
    // Main loop
    while(!glfwWindowShouldClose(window)){
        GLenum err;
        while ((err = glGetError()) != GL_NO_ERROR) {
            std::cerr << "OpenGL error: " << err << std::endl;
        }

        /// setting resizing of window. (Currently not working)
        glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
        // Log runtime information
        // std::cout << "Frame buffer size: " << frame_buff_width << "x" << frame_buff_height << std::endl;
        // std::cout << "Window size: " << WINDOW_WIDTH << "x" << WINDOW_HEIGHT << std::endl;
        // std::cout << "Number of vertices: " << nofVertices << std::endl;
        // std::cout << "Number of indices: " << nofIndices << std::endl;

        // update Input --
        glfwPollEvents();

        // update
        updateInput(window);


        // draw
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, Texture);
        // clear
        glClearColor(0.f,0.f,0.f,1.f); // R,G,B,alpha
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // uses to clear buffers. Here we are clearing color buff.

        // use a program
        glUseProgram(core_program);

        
        // Bind Vertex array object. know where our data is
        glBindVertexArray(VAO);

        // draw
        // glDrawArrays(GL_TRIANGLES, 0, nofIndices ); 
        glDrawElements(GL_TRIANGLES, nofIndices, GL_UNSIGNED_INT, 0);

        // Change the fragment shader for the final texture of the drawing. eg I can change the color of triangle by just changining it to not get the index data but hardcoded values.

        // Vertex shader runs for no. verteces and fragment runs vertex shader. Total here they both run 3,3 times.

        // end draw
        glfwSwapBuffers(window); // swap and 
        glFlush(); // flush
    }

    // End
    glfwTerminate(); // to free up the memory etc.

    // delet program
    glDeleteProgram(core_program);
}

here is my fragment_core.glsl:

#version 440 core

out vec4 FragColor;

in vec3 ourColor;
in vec2 TexCoord;

uniform sampler2D OurTexture;

void main()
{
    FragColor = texture(OurTexture, TexCoord) * vec4(ourColor, 1.0);
}

here is my vertex_core.glsl:

#version 440 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;

void main()
{
    gl_Position = vec4(aPos, 1.0);
    ourColor = aColor;
    TexCoord = aTexCoord;
}

Here is the Picture of what issue I am facing:

When I a m running the code, Its just a black window The output of the above program


Solution

  • Both of the triangles in your vertex geometry:

    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
    };
    
    unsigned int indices[] = {
        0, 1, 2,
        0, 2, 3, // second triangle
    };
    

    ...are wound clockwise.

    And since you asked OpenGL to cull those:

    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);
    glFrontFace(GL_CCW);
    

    ...neither triangle will be drawn.

    You have multiple options: