【问题标题】:Is dynamically load shaders in OpenGL senseless?在OpenGL中动态加载着色器毫无意义吗?
【发布时间】:2018-06-05 11:08:09
【问题描述】:

我刚刚开始我的图形计算之旅,所以我从了解本教程开始:https://learnopengl.com/Getting-started/Hello-Triangle

在冒险自己做一些“动态”绘图之前,我认为变换 将其放入“渲染器”类会很有趣,结果如下:

#pragma once

#include <fstream>
#include <vector>

#include "pch.h"

class Renderer {
public:
    Renderer(GLFWwindow*);

    void initVertexShaders(std::vector<std::string>&);
    void initFragmentShaders(std::vector<std::string>&);
    void load(float*, size_t);
    void draw();

    const GLubyte* renderer;
    const GLubyte* version;

private:
    GLFWwindow* window = nullptr;

    GLuint vbo;
    GLuint vao;

    GLuint shaderProgram = 0; //= glCreateProgram();
    GLuint vs[100];
    GLuint fs[100];
};

pch.h 是一个预编译头文件,以便在每次构建项目时都不编译 glm、glfw 和 glew。

这个想法是隔离不同的实用程序,我将使用 load(float*, size_t) 将信息发送到 gpu,并在主窗口循环期间使用 draw()。这个想法是对 2d 无相机渲染引擎进行初步近似。

反正源码是:

#include "Renderer.hpp"

Renderer::Renderer(GLFWwindow* window) {
    this->window = window;
    glfwMakeContextCurrent(window);

    glewExperimental = GL_TRUE;
    glewInit();

    this->renderer  =   glGetString(GL_RENDERER);
    this->version   =   glGetString(GL_VERSION);

    this->vao       =   0;
    this->vbo       =   0;
}

void Renderer::load(float* points, size_t memory) {
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, memory, points, GL_STATIC_DRAW);

    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);
    glEnableVertexAttribArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
}

void Renderer::draw() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glUseProgram(shaderProgram);
    glBindVertexArray(vao);

    glDrawArrays(GL_TRIANGLES, 0, 6);
}

void Renderer::initVertexShaders(std::vector<std::string>& paths) {
    int i = 0;
    std::ifstream ifs;

    if(this->shaderProgram == 0) shaderProgram = glCreateProgram();

    for (const auto& path : paths){
        ifs.open(path);    
        std::string program_str((  std::istreambuf_iterator<char>(ifs)),
                                (  std::istreambuf_iterator<char>()  ));
        const char* program_src = program_str.c_str();
        vs[i] = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vs[i], 1, &program_src, NULL);
        glCompileShader(vs[i]);
        glAttachShader(shaderProgram, vs[i]);
        i++;
    }
    glLinkProgram(shaderProgram);
}

void Renderer::initFragmentShaders(std::vector<std::string>& paths) {
    int i = 0;
    std::ifstream ifs;

    if(this->shaderProgram == 0) shaderProgram = glCreateProgram();

    for (const auto& path : paths){

        ifs.open();
        std::string program_str((  std::istreambuf_iterator<char>(ifs)),
                                (  std::istreambuf_iterator<char>()  ));
        const char* program_src = program_str.c_str();

        fs[i] = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fs[i], 1, &program_src, NULL);
        glCompileShader(fs[i]);
        glAttachShader(shaderProgram, fs[i]);
        i++;
    }
    glLinkProgram(shaderProgram);
}

问题出现在着色器时,我可以毫无问题地读取文件,但它们什么也不做。

问题是:将所有着色器存储在一个数组中是否有意义? 一个着色器程序可以包含多个顶点着色器/片段着色器吗? 错误在另一个地方吗? 整个课程是否有意义?

谢谢。

编辑:来自 main.cpp 的代码

#include "Renderer.hpp"

    float points[] = {
        0.5f,  0.5f,  0.0f,
        0.5f, -0.5f,  0.0f,
        -0.5f, -0.5f, 0.0f,
        //
         0.5f, 0.5f,  0.0f,
        -0.5f, 0.5f,  0.0f,
        -0.5f, -0.5f, 0.0f

    };

int main() {

    std::vector<std::string> vertexShaders;
    std::vector<std::string> fragmentShaders;

    vertexShaders.push_back("shader/vs.glsl");
    fragmentShaders.push_back("shader/fs.glsl");

    glfwInit();                                                         //glfwGetPrimaryMonitor()
    GLFWwindow *window = glfwCreateWindow(960, 540, "Hello Triangle",   NULL, NULL);
    Renderer Ren(window);

    Ren.load(points, sizeof(points));
    Ren.initVertexShaders(vertexShaders);
    Ren.initFragmentShaders(fragmentShaders);

    while (!glfwWindowShouldClose(window)) {
        Ren.draw();
        glfwPollEvents();
        glfwSwapBuffers(window);
    }

    glfwTerminate();
    return 0;
}

【问题讨论】:

  • 您似乎没有执行任何错误处理。您永远不会检查文件是否已打开或着色器是否已构建。
  • glCreateShader(GL_VERTEX_SHADER) 应该在每个程序中使用一次。而你缺少glCreateShader(GL_FRAGMENT_SHADER)
  • 当只连接了一个顶点着色器时,您也不应该链接着色器程序。在附加顶点和片段着色器后执行此操作。并且作为记录:该方法仅在应用程序中只有一个着色器程序时才有效。但在这种情况下,加载多个顶点或片段着色器没有任何意义。
  • simple complete GL+VAO/VBO+GLSL+shaders example in C++(尤其是gl_simple.h) 我将这些东西分成了init/exit/draw 函数,这样你就可以看到VAO/VBO、GL 和GLSL 着色器需要在哪里。该示例不在类中,但可以轻松制作...

标签: c++ opengl glsl shader glew


【解决方案1】:

在所有附加的着色器对象都被编译后,链接一次着色器程序对象就足够了。


OpenGL 4.6 API Core Profile Specification; 7.3. PROGRAM OBJECTS; page 94:

可以在将源代码加载到着色器对象之前,或者在编译或专门化着色器对象之前,将着色器对象附加到程序对象。

注意,在着色器对象附加到的着色器程序对象被链接之前,着色器对象被成功编译就足够了。


OpenGL 4.6 API Core Profile Specification; 7.3. PROGRAM OBJECTS; page 94:

同一类型的多个着色器对象可以附加到单个程序对象,单个着色器对象可以附加到多个程序对象。

例如

一个着色器对象包含一个函数 (FragColor)

#version 460

uniform sampler2D u_texture;

vec4 FragColor(vec2 uv)
{
    return texture(u_texture, uv);
}

第二个相同类型的着色器对象包含函数签名(但不包括实现)和函数的用法。

#version 460

in vec2 vUV;
out vec4 fragColor;

vec4 FragColor(vec2 uv);

void main()
{
    fragColor = FragColor(vUV);
}

上面的两个代码sn-ps都可以放置到GL_FRAGMENT_SHADER类型的2个单独的着色器对象中。 2 个着色器对象中的每一个都可以成功编译。并且如果它们附加到同一个着色器程序对象上,那么着色器程序对象就可以成功被点赞。

另见Attaching multiple shaders of the same type in a single OpenGL program?


此外,我建议检查着色器对象是否已成功编译:

GLuint shaderObj = .... ;
glCompileShader( shaderObj );

GLint status = GL_TRUE;
glGetShaderiv( shaderObj, GL_COMPILE_STATUS, &status );
if ( status == GL_FALSE )
{
    GLint logLen;
    glGetShaderiv( shaderObj, GL_INFO_LOG_LENGTH, &logLen );
    std::vector< char >log( logLen );
    GLsizei written;
    glGetShaderInfoLog( shaderObj, logLen, &written, log.data() );
    std::cout << "compile error:" << std::endl << log.data() << std::endl;
}

并且一个着色器程序对象已成功链接:

GLuint progObj = ....;
glLinkProgram( progObj );

GLint status = GL_TRUE;
glGetProgramiv( progObj, GL_LINK_STATUS, &status );
if ( status == GL_FALSE )
{
    GLint logLen;
    glGetProgramiv( progObj, GL_INFO_LOG_LENGTH, &logLen );
    std::vector< char >log( logLen );
    GLsizei written;
    glGetProgramInfoLog( progObj, logLen, &written, log.data() );
    std::cout  << "link error:" << std::endl << log.data() << std::endl;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-04-19
    • 2021-09-06
    • 1970-01-01
    • 2018-05-30
    • 1970-01-01
    • 2017-12-16
    相关资源
    最近更新 更多