Skip to main content
Applied suggestions in the answer, waiting for more response.
Source Link

UPDATE: Implementing suggestions mentioned in the answer, please review this aswell as development is a constant process. Thanks all in advance.

Shader.h

namespace shader_program
{

    enum class ShaderType
    {
        VERTEX,
        FRAGMENT
    };

    enum class SourceType
    {
        RAW,
        FILE
    };


    struct Shader
    {
        Shader(const std::string_view &source, ShaderType shaderType, SourceType sourceType);

        ~Shader();

        unsigned int getId() const;

    private:
        unsigned int shader = 0;
    };

    struct ShaderProgram
    {
        ShaderProgram(const std::string_view &vertexSource, const std::string_view &fragmentSource,
                      SourceType vertexSourceType, SourceType fragmentSourceType);

        ~ShaderProgram();

        void use() const;

    private:
        unsigned int program;
    };

} // shader_program

Shader.cpp

shader_program::Shader::Shader(const std::string_view &source, shader_program::ShaderType shaderType,
                               shader_program::SourceType sourceType)
{
    auto content = source.data(); // Assuming raw data
    if (sourceType == SourceType::FILE)
    {
        std::ifstream ifs(source.data());
        if (!ifs.good())
        {
            throw std::runtime_error("Invalid shader file");
        }
        content = std::string((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>()).data();
        if (!ifs.eof())
        {
            throw std::runtime_error("Invalid shader file");
        }
    }
    shader = glCreateShader(shaderType == ShaderType::VERTEX ? GL_VERTEX_SHADER : GL_FRAGMENT_SHADER);
    glShaderSource(shader, 1, &content, nullptr);
    glCompileShader(shader);

    int success;

    glGetShaderiv(shader, GL_COMPILE_STATUS, &success);

    if (!success)
    {
        int len;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len);
        if (len == 0)
        {
            glDeleteShader(shader);
            shader = 0;
            throw std::runtime_error("Could not compile shader and could not obtain any log");
        }
        std::vector<char> log(len);
        glGetShaderInfoLog(shader, log.size(), &len, log.data());
        glDeleteShader(shader);
        shader = 0;
        throw std::runtime_error({log.begin(), log.end()});
    }

}

shader_program::Shader::~Shader()
{
    // No need to delete not created shader
    if (shader != 0)
    {
        glDeleteShader(shader);
    }
}

unsigned int shader_program::Shader::getId() const
{
    return shader;
}

shader_program::ShaderProgram::ShaderProgram(const std::string_view &vertexSource,
                                             const std::string_view &fragmentSource,
                                             shader_program::SourceType vertexSourceType,
                                             shader_program::SourceType fragmentSourceType)
{
    Shader vertexShader(vertexSource, ShaderType::VERTEX, vertexSourceType);
    Shader fragmentShader(fragmentSource, ShaderType::FRAGMENT, fragmentSourceType);

    program = glCreateProgram();

    if (program == 0)
    {
        throw std::runtime_error("Could not create a shader program");
    }

    glAttachShader(program, vertexShader.getId());

    int error = glGetError();

    if (error != GL_NO_ERROR)
    {
        std::string errorStr = "Attaching vertex shader failed with code: " + std::to_string(error);
        glDeleteProgram(program);
        program = 0;
        throw std::runtime_error(errorStr);
    }

    glAttachShader(program, fragmentShader.getId());

    error = glGetError();

    if (error != GL_NO_ERROR)
    {
        std::string errorStr = "Attaching fragment shader failed with code: " + std::to_string(error);
        glDeleteProgram(program);
        program = 0;
        throw std::runtime_error(errorStr);
    }

    glLinkProgram(program);

    int success;

    glGetProgramiv(program, GL_LINK_STATUS, &success);

    if (!success)
    {
        int len;
        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &len);
        if (len == 0)
        {
            glDeleteProgram(program);
            program = 0;
            throw std::runtime_error("Could not link program and could not obtain any log");
        }
        std::vector<char> log(len);
        glGetProgramInfoLog(program, log.size(), &len, log.data());
        glDeleteProgram(program);
        program = 0;
        throw std::runtime_error({log.begin(), log.end()});
    }

}

shader_program::ShaderProgram::~ShaderProgram()
{
    // No need to delete not created program
    if (program != 0)
    {
        glDeleteProgram(program);
    }
}

void shader_program::ShaderProgram::use() const
{
    if (program == 0)
    {
        throw std::runtime_error("Cannot use not created program");
    }
    glUseProgram(program);
}

Thanks again.

UPDATE: Implementing suggestions mentioned in the answer, please review this aswell as development is a constant process. Thanks all in advance.

Shader.h

namespace shader_program
{

    enum class ShaderType
    {
        VERTEX,
        FRAGMENT
    };

    enum class SourceType
    {
        RAW,
        FILE
    };


    struct Shader
    {
        Shader(const std::string_view &source, ShaderType shaderType, SourceType sourceType);

        ~Shader();

        unsigned int getId() const;

    private:
        unsigned int shader = 0;
    };

    struct ShaderProgram
    {
        ShaderProgram(const std::string_view &vertexSource, const std::string_view &fragmentSource,
                      SourceType vertexSourceType, SourceType fragmentSourceType);

        ~ShaderProgram();

        void use() const;

    private:
        unsigned int program;
    };

} // shader_program

Shader.cpp

shader_program::Shader::Shader(const std::string_view &source, shader_program::ShaderType shaderType,
                               shader_program::SourceType sourceType)
{
    auto content = source.data(); // Assuming raw data
    if (sourceType == SourceType::FILE)
    {
        std::ifstream ifs(source.data());
        if (!ifs.good())
        {
            throw std::runtime_error("Invalid shader file");
        }
        content = std::string((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>()).data();
        if (!ifs.eof())
        {
            throw std::runtime_error("Invalid shader file");
        }
    }
    shader = glCreateShader(shaderType == ShaderType::VERTEX ? GL_VERTEX_SHADER : GL_FRAGMENT_SHADER);
    glShaderSource(shader, 1, &content, nullptr);
    glCompileShader(shader);

    int success;

    glGetShaderiv(shader, GL_COMPILE_STATUS, &success);

    if (!success)
    {
        int len;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len);
        if (len == 0)
        {
            glDeleteShader(shader);
            shader = 0;
            throw std::runtime_error("Could not compile shader and could not obtain any log");
        }
        std::vector<char> log(len);
        glGetShaderInfoLog(shader, log.size(), &len, log.data());
        glDeleteShader(shader);
        shader = 0;
        throw std::runtime_error({log.begin(), log.end()});
    }

}

shader_program::Shader::~Shader()
{
    // No need to delete not created shader
    if (shader != 0)
    {
        glDeleteShader(shader);
    }
}

unsigned int shader_program::Shader::getId() const
{
    return shader;
}

shader_program::ShaderProgram::ShaderProgram(const std::string_view &vertexSource,
                                             const std::string_view &fragmentSource,
                                             shader_program::SourceType vertexSourceType,
                                             shader_program::SourceType fragmentSourceType)
{
    Shader vertexShader(vertexSource, ShaderType::VERTEX, vertexSourceType);
    Shader fragmentShader(fragmentSource, ShaderType::FRAGMENT, fragmentSourceType);

    program = glCreateProgram();

    if (program == 0)
    {
        throw std::runtime_error("Could not create a shader program");
    }

    glAttachShader(program, vertexShader.getId());

    int error = glGetError();

    if (error != GL_NO_ERROR)
    {
        std::string errorStr = "Attaching vertex shader failed with code: " + std::to_string(error);
        glDeleteProgram(program);
        program = 0;
        throw std::runtime_error(errorStr);
    }

    glAttachShader(program, fragmentShader.getId());

    error = glGetError();

    if (error != GL_NO_ERROR)
    {
        std::string errorStr = "Attaching fragment shader failed with code: " + std::to_string(error);
        glDeleteProgram(program);
        program = 0;
        throw std::runtime_error(errorStr);
    }

    glLinkProgram(program);

    int success;

    glGetProgramiv(program, GL_LINK_STATUS, &success);

    if (!success)
    {
        int len;
        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &len);
        if (len == 0)
        {
            glDeleteProgram(program);
            program = 0;
            throw std::runtime_error("Could not link program and could not obtain any log");
        }
        std::vector<char> log(len);
        glGetProgramInfoLog(program, log.size(), &len, log.data());
        glDeleteProgram(program);
        program = 0;
        throw std::runtime_error({log.begin(), log.end()});
    }

}

shader_program::ShaderProgram::~ShaderProgram()
{
    // No need to delete not created program
    if (program != 0)
    {
        glDeleteProgram(program);
    }
}

void shader_program::ShaderProgram::use() const
{
    if (program == 0)
    {
        throw std::runtime_error("Cannot use not created program");
    }
    glUseProgram(program);
}

Thanks again.

Became Hot Network Question
Source Link

Shader Program OpenGL

I am new to OpenGL learning it on amazing website learnopengl.com I wanted to a convenient way to use shader programs thats why I created this struct, please review it.

ShaderProgram.h

#ifndef HELLO_TRIANGLE_SHADERPROGRAM_H
#define HELLO_TRIANGLE_SHADERPROGRAM_H

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <fstream>
#include <string>

#include <variant>


struct ShaderProgram {

    void loadFromFile(const char* vertexFile, const char* fragmentFile);
    void loadFromSource(const char* vertexSource, const char* fragmentSource);
    unsigned int getProgram() const;
    bool isSuccess() const;
    const std::string_view& getErrorMessage() const;

private:
    std::variant<unsigned int, std::string_view> data;
};

#endif //HELLO_TRIANGLE_SHADERPROGRAM_H

ShaderProgram.cpp

#include "ShaderProgram.h"

void ShaderProgram::loadFromSource(const char *vertexSource, const char *fragmentSource)
{
    unsigned int vertexShaderId = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShaderId, 1, &vertexSource, nullptr);
    glCompileShader(vertexShaderId);

    int success;
    char log[512];
    glGetShaderiv(vertexShaderId, GL_COMPILE_STATUS, &success);

    if (!success)
    {
        glGetShaderInfoLog(vertexShaderId, 512, nullptr, log);
        data = log;
        return;
    }

    unsigned int fragmentShaderId = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShaderId, 1, &fragmentSource, nullptr);
    glCompileShader(fragmentShaderId);

    glGetShaderiv(fragmentShaderId, GL_COMPILE_STATUS, &success);

    if (!success)
    {
        glGetShaderInfoLog(fragmentShaderId, 512, nullptr, log);
        data = log;
        return;
    }

    unsigned int program = glCreateProgram();
    glAttachShader(program, vertexShaderId);
    glAttachShader(program, fragmentShaderId);

    glLinkProgram(program);

    glGetProgramiv(program, GL_LINK_STATUS, &success);

    if (!success)
    {
        glGetProgramInfoLog(program, 512, nullptr, log);
        data = log;
        return;
    }

    data = program;
    glDeleteShader(vertexShaderId);
    glDeleteShader(fragmentShaderId);
}

void ShaderProgram::loadFromFile(const char *vertexFilePath, const char *fragmentFilePath)
{
    try
    {
        std::ifstream vertexFile(vertexFilePath);
        std::string vertexFileContent((std::istreambuf_iterator<char>(vertexFile)), std::istreambuf_iterator<char>());

        std::ifstream fragmentFile(fragmentFilePath);
        std::string fragmentFileContent((std::istreambuf_iterator<char>(fragmentFile)),
                                        std::istreambuf_iterator<char>());

        loadFromSource(vertexFileContent.c_str(), fragmentFileContent.c_str());
    } catch (std::exception &e)
    {
        data = e.what();
    }
}

unsigned int ShaderProgram::getProgram() const
{
    return std::get<unsigned int>(data);
}

bool ShaderProgram::isSuccess() const
{
    return std::holds_alternative<unsigned int>(data);
}

const std::string_view &ShaderProgram::getErrorMessage() const
{
    return std::get<std::string_view>(data);
}

Please review this and advise is there a better way to this. Thanks in advance.