OpenGL 程序着色器链接错误

Posted

技术标签:

【中文标题】OpenGL 程序着色器链接错误【英文标题】:OpenGL Program Shader Linking Error 【发布时间】:2018-04-16 15:20:41 【问题描述】:

我一直在努力创建一个基于定期改变颜色的着色器的圆。我的代码打印出一个空白窗口。我决定设置一些方法来使用 GL_LINK_STATUS 检查错误,似乎我的代码在链接程序和着色器时遇到了问题。 我对人们遇到的类似问题进行了一些研究,但他们似乎没有类似的原因。 我已经在下面发布了我的着色器代码、驱动程序(名为 Priomary)和着色器程序。

顶点着色器

 #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);

片段着色器

    #version 330 core

uniform vec4 vertColor;
out vec4 frag_color;

void main()

    frag_color = vertColor;

着色器程序

    #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::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\n", log);

        delete[] log;
    


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]);


dumpShaderLog(false, mProgram);
        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];
    

初级课程

#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++;

【问题讨论】:

“遇到问题”对于错误描述来说有点不准确。您可能想确切地告诉我们链接器说了什么。 抱歉,着色器编译器返回的错误究竟是什么? 对不起。我的控制台打印 SHADER COMPILE TEST ERROR SHADER COMPILE TEST ERROR Linking Error with Program and Shader。所以着色器没有编译,自然没有任何链接。 你为什么不实际打印你查询的信息日志? 我更新了上面的代码以显示更多错误。当我dump Log时,它告诉我“找不到void main()。似乎读取着色器文件的字符串流有问题? 【参考方案1】:

glGetShaderiv 从着色器对象返回参数,glGetProgramiv 从程序对象返回参数。

这意味着您必须像这样更改代码:

// glGetProgramiv(shader, GL_COMPILE_STATUS, &status); <--- delete
glGetShaderiv (shader, GL_COMPILE_STATUS, &status);

确保着色器文件已正确加载并出于调试原因对其进行跟踪:

glShaderSource(vs, 1, &vsSourcePtr, NULL);
glShaderSource(fs, 1, &fsSourcePtr, NULL);

std::cout << "VERTEX SHADER:" << std::endl << vsSourcePtr << std::endl << std::endl;
glCompileShader(vs);
testShaderCompile(vs);

std::cout << "FRAGMENT SHADER:" << std::endl << fsSourcePtr << std::endl << std::endl;
glCompileShader(fs);
testShaderCompile(fs);

由于您没有设置任何矩阵,因此您必须在标准化设备空间中设置所有顶点坐标,即 [-1.0, 1.0]。

要使网格在视口上正确“可见”,请将球体的半径设置为 0.5:

GLfloat Radius = 0.5f;

并绘制所有图元:

glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);

【讨论】:

我明白了。这是一个小小的疏忽。恐怕我仍然会收到与空白屏幕相同的整体问题。着色器编译问题现在很好,但程序仍然存在问题。 “程序和着色器的链接错误” @FolaranmiOgunfemi 我测试了你的代码,它对我来说很好用。确保正确加载着色器程序。

以上是关于OpenGL 程序着色器链接错误的主要内容,如果未能解决你的问题,请参考以下文章

初识OpenGL 链接着色器

初识OpenGL 链接着色器

openGL一些概念02

无法将着色器链接到 OpenGL 中的程序对象,无法调试

OpenGL 3.3 - glDrawArrays 后的无效操作错误 (1282)

损坏的顶点和片段着色器