GLSL Circle Program Linking Issues. Blank Output

I have been working to create a circle based on a shader that changes color periodically. My code prints out a blank window. I decided to set-up some methods to check for errors using GL_LINK_STATUS and it seems that my code is encountering an issue linking the program and the shader. There is no error log. I have looked through other similar posts on this site with similar issues but they don’t seem to have similar causes. I have posted my shader code, the driver program(named Primary) and the Shader Program below.

I receive a blank output, and a console window printing my custom log message “Linking Error with Program and Shader.”

Vertex Shader

    #version 330 core

layout (location = 0) in vec3 pos;

uniform vec2 posOffset;

void main()
{
    gl_Position = vec4(pos.x + posOffset.x, pos.y + posOffset.y, pos.z, 1.0);
}

Fragment Shader

    #version 330 core

uniform vec4 vertColor;
out vec4 frag_color;

void main()
{
    frag_color = vertColor;
}

Shader Program

    #include "ShaderProgram.h"
#include <fstream>
#include <iostream>
#include <sstream>


ShaderProgram::ShaderProgram()
    : mProgram(0){
}


ShaderProgram::~ShaderProgram()
{
    glDeleteProgram(mProgram);
}

bool ShaderProgram::assignShaders(const char* vertFileName, const char* fragFileName)
{
    //Shaders output objects called programs that define their relationship and lead to .exe functionality

    //assigning pointer to the shader

        string vsString = readFile(vertFileName);
        string fsString = readFile(fragFileName);

        // c_str returns a const char* that points to a null-terminated string (i.e. a C-style string). It is useful when you want to pass the "contents"¹ of an std::string to a function that expects to work with a C-style string.
        const GLchar* vsSourcePtr = vsString.c_str();
        const GLchar* fsSourcePtr = fsString.c_str();

    //creating vertex shader(vs) shader object
    GLuint vs = glCreateShader(GL_VERTEX_SHADER);
    GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);

    //assigning shader source using address. Replaces the source code in a shader object //@arg (shader, count Strings, pointer to const File ,size)
    glShaderSource(vs, 1, &vsSourcePtr, NULL);
    glShaderSource(fs, 1, &fsSourcePtr, NULL);

    glCompileShader(vs);
    testShaderCompile(vs);

    glCompileShader(fs);
    testShaderCompile(fs);

    //createProgram returns GLUint which is basically an unsigned int... we will use This Handler to create a program object
    mProgram = glCreateProgram();
    if (mProgram == 0)
    {
        std::cerr << "Shader cannot be created" << std::endl;
        return false;
    }
    //assign the program object(mProgram) to the Shader
    glAttachShader(mProgram, vs);
    glAttachShader(mProgram, fs);

    //this method accepts a GLuint "program" . If its an object of type GL_VERTEX_SHADER,
    //itll create a .exe that runs on the programmable vertex processor. same goes for geometric and fragment shaders if they were included
    //it will also bind all user defined uniform variables and attributes to the program 
    //The program can then be made part of a defined state by calling useProgram
    glLinkProgram(mProgram);
    testProgramCompile();

    //cleaning up the elements we already used
    glDeleteShader(vs);
    glDeleteShader(fs);

        //clear the identifier lookup map(in this case, there's only one)
    mUniformIdentifiers.clear();

    return true;
}//end main

    //Read the shaderFile. strngstream for reading multiple lines
string ShaderProgram:: readFile(const string& filename) {

    std::stringstream strgstream;
    std::ifstream file;

    try
    {
        file.open(filename, std::ios::in);

        if (!file.fail())
        {
            strgstream << file.rdbuf();
        }

        file.close();
    }
    catch (std::exception ex)
    {
        std::cerr << "Error: File or File Name Issues" << std::endl;
    }

    return strgstream.str();
}
    //use the Program Object we created in this current state(color)
void ShaderProgram::use()
{
        //check if it is not null
    if (mProgram > 0)
        glUseProgram(mProgram);
}

void ShaderProgram::testProgramCompile() {
    int status = 0;

    GLuint program = mProgram;

        // ///CHECKING GL_LINK_STATUS to see if Program Link was successul. Link Status will return GL_TRUE if it was
        glGetProgramiv( mProgram, GL_LINK_STATUS, &status); //requesting the status
        if (status == GL_FALSE)
        {
            GLint length = 0;
        glGetProgramiv(mProgram, GL_INFO_LOG_LENGTH, &length);

        string errorLog(length, ' ');   // Resize and fill with space character
        glGetProgramInfoLog(mProgram, length, &length, &errorLog[0]);
            std::cerr << "Linking Error with Program and Shader" << std::endl;
        }

}
void ShaderProgram :: testShaderCompile(GLuint shader) {
    int status = 0;


    // ///CHECKING GL_LINK_STATUS to see if Program Link was successul. Link Status will return GL_TRUE if it was
   glGetShaderiv (shader, GL_COMPILE_STATUS, &status); //requesting the status
    if (status == GL_FALSE)
    {
        GLint length = 0;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);

        string errorLog(length, ' ');  // Resize and fill with space character
        glGetShaderInfoLog(shader, length, &length, &errorLog[0]);
        std::cerr << "SHADER COMPILE TEST ERROR " << std::endl;
    }

}
////GETTERS AND SETTERS

GLuint ShaderProgram::getProgram() const
{
    return mProgram;
}



void ShaderProgram::setUniform(const GLchar* name, const glm::vec2& v)
{
    GLint address = getUniformIdentifier(name);
    glUniform2f(address, v.x, v.y);
}

void ShaderProgram::setUniform(const GLchar* name, const glm::vec3& v)
{
    GLint address = getUniformIdentifier(name);
    glUniform3f(address, v.x, v.y, v.z);
}

void ShaderProgram:: setUniform(const GLchar* name, const glm::vec4& v) {
    GLint address = getUniformIdentifier(name);
    glUniform4f(address, v.x, v.y, v.z, v.w);
}

//Maybe need to switch places with setUniform
GLint ShaderProgram :: getUniformIdentifier(const GLchar* name) {

    std::map<std::string, GLint>::iterator it;
    it = mUniformIdentifiers.find(name);
    //std::map<std::string, GLint>


        // Only need to query the shader program IF it doesn't already exist.
        if (it == mUniformIdentifiers.end())
        {
            // Find it and add it to the map
            mUniformIdentifiers[name] = glGetUniformLocation(mProgram, name);
        }

        // Return it
        return mUniformIdentifiers[name];
    }

Primary Program


#include <iostream>
#include <vector>

#include <sstream>
#define GLEW_STATIC
    //always GLEW before GLFW
#include "GL/glew.h"    
#include "GLFW/glfw3.h"
#include "glm/glm.hpp"

#include "ShaderProgram.h"

#ifndef M_PI
# define M_PI 3.141592653
#endif


/////gLOBAL
GLFWwindow* w = NULL;
const int wWidth = 800;
const int wHeight = 600;
 std::vector<glm::vec3> vertices;
std::vector<unsigned int> indices;
GLfloat Radius = 6;
GLfloat Stacks = 5;
GLfloat Slices = 5;



void key_callback(GLFWwindow *w, int key, int scancode, int action, int mode);
//update colors based on average framerate
void averageFPS(GLFWwindow* window);
//screen resizing
void glfw_onFramebufferSize(GLFWwindow* window, int width, int height);
bool initOpenGL();


static void error(int error, const char *desc)
{
    fputs(desc, stderr);
}
//setting up values for keys


int main() {
    //pointing to GLFW window
    //GLFWwindow *w; //may want to initialize as null outside before main
    if (!initOpenGL())  ///5IMPR
    {
        // An error occured
        std::cerr << "GLFW not initialized" << std::endl;
        return -1;
    }

    glfwSetErrorCallback(error);

    ///TEMP CIRCLE VERTICES

    // Calc The Vertices

    for (int i = 0; i <= Stacks; ++i) {

        float V = i / (float)Stacks;
        float phi = V * M_PI; //change to glm:: pi

        // Loop Through Slices
        for (int j = 0; j <= Slices; ++j) {

            float U = j / (float)Slices;
            float theta = U * (M_PI * 2);

            // Calc The Vertex Positions
            float x = cosf(theta) * sinf(phi);
            float y = cosf(phi);
            float z = sinf(theta) * sinf(phi);

            // Push Back Vertex Data //push_back is a standard vector function which adds a parameter to the end of the vector

            //std::vector<glm::vec3> vertices; //moved to global variables at top

            vertices.push_back(glm::vec3(x, y, z) * Radius);

        }

    }

    // Calc The Index Positions
    for (int i = 0; i < Slices * Stacks + Slices; ++i) {




        indices.push_back(i);
        indices.push_back(i + Slices + 1);
        indices.push_back(i + Slices);

        indices.push_back(i + Slices + 1);
        indices.push_back(i);
        indices.push_back(i + 1);
    }

    ////TEMP CIRCLE VERTICES END

    // 2. Set up buffers on the GPU
    GLuint vbo, ibo, vao;                       ///5IMPROVEdown

    glGenBuffers(1, &vbo);                  // Generate an empty vertex buffer on the GPU
    glBindBuffer(GL_ARRAY_BUFFER, vbo);     // "bind" or set as the current buffer we are working with
        //3rd argument of glBufferData needs a pointer to the std:vector data...A pointer to the data can be obtained by vertices.data() accrding to std: ector docs...
    glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec3), vertices.data(), GL_STATIC_DRAW);// copy the data from CPU to GPU


    glGenVertexArrays(1, &vao);             // Tell OpenGL to create new Vertex Array Object
    glBindVertexArray(vao);                 // Make it the current one
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);   // Define a layout for the first vertex buffer "0"
    glEnableVertexAttribArray(0);           // Enable the first attribute or attribute "0"

                                            // Set up index buffer
    glGenBuffers(1, &ibo);  // Create buffer space on the GPU for the index buffer
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW);
        // glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); - REPLACED

    glBindVertexArray(0);                   // unbind to make sure other code doesn't change it

    ShaderProgram shaderProgram;        ///5ADD
    shaderProgram.assignShaders("shaders\\ColorShader.vert", "shaders\\ColorShader.frag"); ///5ADD

            ////////SETUP RENDERING
    while (!glfwWindowShouldClose(w))
    {
        averageFPS(w);

            //process events
        glfwPollEvents();           

            // Clear the screen
        glClear(GL_COLOR_BUFFER_BIT);   

        shaderProgram.use();    ///5ADD

        GLfloat time = (GLfloat)glfwGetTime();          ///5ADD
        GLfloat blueSetting = (sin(time) / 2) + 0.5f;       ///5ADD
        glm::vec2 pos;
        pos.x = sin(time) / 2;
        pos.y = cos(time) / 2;
        shaderProgram.setUniform("vertColor", glm::vec4(0.0f, 0.0f, blueSetting, 1.0f));    ///5ADD
        shaderProgram.setUniform("posOffset", pos);


        glBindVertexArray(vao);

            //og for quad
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
            //glDrawElements(GL_LINE_LOOP, 6, GL_UNSIGNED_INT, 0);
        glBindVertexArray(0);
        // Swap buffers and look for events

        glfwSwapBuffers(w);


    }
        //clean up
    glDeleteVertexArrays(1, &vao);
    glDeleteBuffers(1, &vbo);
    glDeleteBuffers(1, &ibo);

    //glfwDestroyWindow(w);
    glfwTerminate();

    return 0;
    }


            ///////START Initializing glfw glew etc
bool initOpenGL(){

    //this method will exit on these conditions
    GLuint error = glfwInit();
    if (!error)
        return false;

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);


    w = glfwCreateWindow(wWidth, wHeight, "Exercise", NULL, NULL);


    if (w== NULL)
    {
        std::cerr << "glfw window not created" << std::endl;
        glfwTerminate();
        return false;
    }

        //update context
    glfwMakeContextCurrent(w);

    // Initialize GLEWunifor
    glewExperimental = GL_TRUE;
    GLuint err = glewInit();
    if (err != GLEW_OK)
    {
        std::cerr << "initialize GLEW Failed" << std::endl;
        return false;
    }

        //setup key callbacks
    glfwSetKeyCallback(w, key_callback);
    glfwSetFramebufferSizeCallback(w, glfw_onFramebufferSize);

    glClearColor(0.23f, 0.38f, 0.47f, 1.0f);    ///5ADD

                                                // Define the viewport dimensions
    glViewport(0, 0, wWidth, wHeight);  //necessary?

    return true;

}

void key_callback(GLFWwindow *w, int key, int scancode, int action, int mode)
{
    // See http://www.glfw.org/docs/latest/group__keys.html
    if ((key == GLFW_KEY_ESCAPE || key == GLFW_KEY_Q) && action == GLFW_PRESS)
        glfwSetWindowShouldClose(w, GL_TRUE);

    if (key == GLFW_KEY_W && action == GLFW_PRESS)
    {
        bool showWires = false;
        if (showWires)
            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
        else
            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    }

}

    //whever window resizes, do this
void glfw_onFramebufferSize(GLFWwindow* window, int width, int height)
{
    glViewport(0, 0, width, height);
}

void averageFPS(GLFWwindow* window) ///5ADDdown
{
    static double previousSeconds = 0.0;
    static int frameCount = 0;
    double passedSeconds;
    double currentSeconds = glfwGetTime(); //seconds since GLFW started

    passedSeconds = currentSeconds - previousSeconds;

    // Limit time updates to 4 times per second
    if (passedSeconds > 0.25)
    {
        previousSeconds = currentSeconds;
        double fps = (double)frameCount / passedSeconds;
    //  double frameInMilSecs = 1000.0 / fps;
         frameCount = 0;} 
    frameCount++;
}

Try using this to dump the shader info log after a link error instead:


void dumpShaderLog( bool is_shader, GLuint obj )
{
  int maxlen = 0;
  if ( is_shader ) glGetShaderiv ( obj, GL_INFO_LOG_LENGTH, &maxlen );
  else             glGetProgramiv( obj, GL_INFO_LOG_LENGTH, &maxlen );

  if ( maxlen > 0 )
  {
    char *log = new char [ maxlen ] ;
    int   len ;

    if ( is_shader )  glGetShaderInfoLog ( obj, maxlen, &len, log );
    else              glGetProgramInfoLog( obj, maxlen, &len, log );

    if( len > 0 && log[0] != '\0' )
      fprintf( stderr, "%s
", log ) ;

    delete [] log ;
  }
}

Pass “is_shader = false” and “obj = <your program handle>”.

You may also want to read this: writing directly to std::string internal buffers. Don’t know that this is your problem for sure, but it’s one theory.

I see.

That does help narrow things down. The dump reveals the following Error. “Linker definition for void main not found”.

It’s my understanding that this generally means that the shader is not being properly read-in line by line. I still haven’t found a resolution however.

Look at your readFile() function. Print the string.

	shaderProgram.assignShaders("C:\\Users\\FOLARANMI\\___Visual_Studio_Projects\\source\\repos\\SphereOpenGL\\SphereOpenGL\\shaders\\ColorShader.vert", "C:\\Users\\FOLARANMI\\___Visual_Studio_Projects\\source\\repos\\SphereOpenGL\\SphereOpenGL\\shaders\\ColorShader.frag"); 

So I replaced the relative path with the absolute (C:…\…\) paths for both the vert and frag shaders. I tested to have it print the file names and the file contents to the screen and it showed the output. The Linker Error in the console is also gone.

string ShaderProgram:: readFile(const string& filename) {

	std::stringstream strgstream;
	std::ifstream file;

	
	try
	{
		file.open(filename, std::ios::in);

		if (!file.fail())
		{
			std::cerr << "Currently Reading " + filename << std::endl;
			strgstream << file.rdbuf();
		}

		file.close();
	}
	catch (std::exception ex)
	{
		std::cerr << "Error: File or File Name Issues" << std::endl;
	}

       // For testing std::cerr << "File Contents: " + "strgstream.str() << std::endl;
	return strgstream.str();
}

It looks like the files weren’t being read. Now my console returns no error messages, but unfortunately, I still have nothing drawn in OpenGL.