Динамически загружать шейдеры в OpenGL бессмысленно? - PullRequest
0 голосов
/ 05 июня 2018

Я только начал свой путь в графических вычислениях, поэтому я начал с понимания этого урока: https://learnopengl.com/Getting-started/Hello-Triangle.

Прежде чем приступить к созданию "динамического" рисования, я подумал, что преобразовать его в класс "Renderer"Интересно, что это был результат:

#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 () во время цикла главного окна.Идея состоит в том, чтобы сделать первое приближение к двумерному движку рендеринга без камеры.

В любом случае, исходный код:

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

Проблема возникает во время шейдера, я могу читать файлы безпроблема, но они ничего не делают.

Вопросы следующие: имеет ли смысл хранить все шейдеры в массиве?Может ли шейдерная программа состоять из нескольких вершинных / фрагментных шейдеров?Ошибка в другом месте?Весь класс имеет смысл в любом случае?

Спасибо.

edit: Код из 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;
}

1 Ответ

0 голосов
/ 05 июня 2018

Достаточно один раз связать программный объект шейдера после того, как все присоединенные шейдерные объекты были скомпилированы.

Спецификация профиля ядра API OpenGL 4.6;7.3.ПРОГРАММНЫЕ ОБЪЕКТЫ;стр. 94 :

Объекты шейдера могут быть присоединены к программным объектам до загрузки исходного кода в объект шейдера или до того, как объект шейдера был скомпилирован или специализирован.

Обратите внимание, что достаточно, чтобы шейдерный объект был успешно скомпилирован до того, как программный объект шейдера, к которому он присоединен, будет связан.

Спецификация профиля ядра API OpenGL 4.6;7.3.ПРОГРАММНЫЕ ОБЪЕКТЫ;стр. 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);
}

Оба приведенных выше фрагмента кода можно поместить в 2 отдельных объекта шейдера типа GL_FRAGMENT_SHADER.Каждый из 2-х шейдерных объектов может быть успешно скомпилирован.И если они присоединены к одному и тому же программному объекту шейдера, тогда программный объект шейдера может быть успешно использован.

См. Также Присоединение нескольких шейдеров одного типа в одной программе OpenGL?


Далее, я рекомендую проверить, был ли успешно скомпилирован шейдерный объект:

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;
}
...