OpenGL着色器不适用于多个文件

时间:2018-08-05 05:47:35

标签: c++ opengl

我正在学习opengl,并按照https://learnopengl.com/In-Practice/2D-Game/Breakout上的2D游戏教程进行操作,一旦进入到应该在屏幕上显示某些内容的部分,就什么也没画。经过几天的挫败,我最终得以将问题缩小到在程序停止之前调用的Shader类析构函数。但是,当我将所有绘图代码放在一个文件中时,没有调用析构函数,并且一切都绘制良好。我最近通过在资源管理器中从getter返回指针来解决析构函数问题。但是,它仍然不会绘制。我的代码在这里:

Main.cpp

#include <iostream>
#include <GL/glew.h>
#include <SFML/Graphics.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include "Game.hpp"

int main() {
    sf::RenderWindow window(sf::VideoMode(800, 600), "Test", sf::Style::Default, sf::ContextSettings(3, 3));

    if (!glewInit() == GLEW_OK) {
        std::cout << "Failed to initialize GLEW!";
        return -1;
    }

    glEnable(GL_TEXTURE_2D);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    Game::Game game(800.f, 600.f);
    game.__init__();

    sf::Clock clock;
    float deltaTime = 0.f;
    while (window.isOpen()) {
        sf::Event event;
        while (window.pollEvent(event)) {
            if (event.type == sf::Event::Closed) {
                window.close();
            }
        }
        glClearColor(0.1f, 0.1f, 0.1f, 1.f);
        glClear(GL_COLOR_BUFFER_BIT);

        game.render();

        window.display();
    }
    Resource::ResourceManager::__delete__();
    return 0;
}

Game.hpp

#pragma once

#include "Resources/ResourceManager.hpp"
#include <glm/gtc/matrix_transform.hpp>
#include <SFML/Window/Keyboard.hpp>
#include <map>

namespace Game {

    enum class GameState {
        TITLE,
        ACTIVE,
        PAUSED,
        WIN,
        LOOSE
    };

    class Game
    {
    public:
        Game(float w_width, float w_height);
        ~Game();

        void __init__();
        void setKey(sf::Keyboard::Key key, bool isPressed);
        void update();
        void render();

    private:
        GLuint _vao;
        float _w_width;
        float _w_height;
        std::map<sf::Keyboard::Key, bool> _key_states;
    };
};

Game.cpp

#include "Game.hpp"

namespace Game {

    Game::Game(float w_width, float w_height)
    {
        _w_width = w_width;
        _w_height = w_height;
    }

    Game::~Game()
    {

    }

    void Game::__init__()
    {
        glm::mat4 Projection = glm::ortho(0.f, _w_width, _w_height, 0.f, -1.f, 1.f);
        glm::mat4 Model = glm::scale(glm::mat4(1.f), glm::vec3(100.f, 70.f, 1.f));

        Resource::ResourceManager::addShader(0, "Shaders/simpleVertexShader.glsl", "Shaders/simpleFragmentShader.glsl");

        Resource::ResourceManager::addTexture(0, "Textures/block.png");
        Resource::ResourceManager::addTexture(1, "Textures/block_solid.png");
        Resource::ResourceManager::addTexture(2, "Textures/background.jpg");

        Resource::Shader* main_shader = Resource::ResourceManager::getShader(0);
        main_shader->setMat4("Projection", Projection);
        main_shader->setMat4("Model", Model);
        main_shader->setVec3("Color", glm::vec3(0.1, 0.8, 0.1));

        glGenVertexArrays(1, &_vao);

        GLfloat vertex_data[] = {
            0.f, 0.f, 0.f, 0.f,
            1.f, 0.f, 1.f, 0.f,
            1.f, 1.f, 1.f, 1.f,

            1.f, 1.f, 1.f, 1.f,
            0.f, 1.f, 0.f, 1.f,
            0.f, 0.f, 0.f, 0.f
        };

        GLuint vbo;
        glGenBuffers(1, &vbo);
        glBindBuffer(GL_ARRAY_BUFFER, vbo);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW);

        glBindVertexArray(_vao);
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glBindVertexArray(0);
    }

    void Game::setKey(sf::Keyboard::Key key, bool isPressed)
    {
        _key_states[key] = isPressed;
    }

    void Game::update()
    {

    }

    void Game::render()
    {
        Resource::Texture* texture = Resource::ResourceManager::getTexture(0);
        Resource::Shader* shader = Resource::ResourceManager::getShader(0);
        texture->bind();
        shader->use();
        glBindVertexArray(_vao);
        glDrawArrays(GL_TRIANGLES, 0, 6);
        glBindVertexArray(0);
    }
};

ResourceManager.hpp

#pragma once

#include <unordered_map>
#include <fstream>
#include <string>
#include <iostream>
#include "Shader.hpp"
#include "Texture.hpp"

namespace Resource {

    class ResourceManager {
    public:
        static void addShader(uint8_t id, std::string vertexShaderSource, std::string fragmentShaderSource);
        static void addTexture(uint8_t id, std::string imageSource);

        static Shader* getShader(uint8_t id);
        static Texture* getTexture(uint8_t id);

        static void __delete__();

    private:
        ResourceManager() {}
        static std::string _loadShaderFromFile(std::string shaderSource);

    public:
        static std::unordered_map<uint8_t, Shader*> _shaders;
        static std::unordered_map<uint8_t, Texture*> _textures;
    };
};

ResourceManager.cpp

#include "ResourceManager.hpp"

namespace Resource {
    std::unordered_map<uint8_t, Shader*> ResourceManager::_shaders;
    std::unordered_map<uint8_t, Texture*> ResourceManager::_textures;

    void ResourceManager::addShader(uint8_t id, std::string vertexShaderSource, std::string fragmentShaderSource)
    {
        _shaders[id] = new Shader(_loadShaderFromFile(vertexShaderSource).c_str(), _loadShaderFromFile(fragmentShaderSource).c_str());
    }

    void ResourceManager::addTexture(uint8_t id, std::string imageSource)
    {
        _textures[id] = new Texture(imageSource);
    }

    Shader* ResourceManager::getShader(uint8_t id)
    {
        return _shaders[id];
    }

    Texture* ResourceManager::getTexture(uint8_t id)
    {
        return _textures[id];
    }

    void ResourceManager::__delete__()
    {
        for (auto iter : _shaders) {
            delete iter.second;
        }
        for (auto iter : _textures) {
            delete iter.second;
        }
    }

    std::string ResourceManager::_loadShaderFromFile(std::string shaderSource)
    {
        std::ifstream file(shaderSource);
        if (!file.is_open()) {
            std::cout << "Failed to load shader: " << shaderSource << "!" << std::endl;
            return "";
        }
        std::string line;
        std::string lines;
        while (std::getline(file, line)) {
            lines += line + '\n';
        }
        return lines;
    }
};

Shader.hpp

#pragma once

#include <GL/glew.h>
#include <glm/glm.hpp>

namespace Resource {

    class Shader {
    public:
        Shader(const char* vertexShaderSource, const char* fragmentShaderSource);
        Shader();
        ~Shader();

        void use();

        void setMat4(const char* location, glm::mat4 matrix);
        void setVec3(const char* location, glm::vec3 vector);
        void setVec4(const char* location, glm::vec4 vector);

        void printID();

        Shader(const Shader&) = delete;
        Shader& operator=(const Shader&) = delete;

    private:
        GLuint _id;
    };
};

Shader.cpp

#include "Shader.hpp"

#include <iostream>

namespace Resource {

    Shader::Shader(const char* vertexShaderSource, const char* fragmentShaderSource)
    {
        GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
        glCompileShader(vertexShader);
        GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
        glCompileShader(fragmentShader);

        _id = glCreateProgram();
        glAttachShader(_id, vertexShader);
        glAttachShader(_id, fragmentShader);
        glLinkProgram(_id);

        glDetachShader(_id, vertexShader);
        glDetachShader(_id, fragmentShader);
        glDeleteShader(vertexShader);
        glDeleteShader(fragmentShader);
    }

    Shader::Shader()
    {
    }

    Shader::~Shader()
    {
        glDeleteProgram(_id);
    }

    void Shader::use()
    {
        glUseProgram(_id);
    }

    void Shader::setMat4(const char* location, glm::mat4 matrix)
    {
        glUniformMatrix4fv(glGetUniformLocation(_id, location), 1, GL_FALSE, &matrix[0][0]);
    }

    void Shader::setVec3(const char* location, glm::vec3 vector)
    {
        glUniform3fv(glGetUniformLocation(_id, location), 1, &vector[0]);
    }

    void Shader::setVec4(const char* location, glm::vec4 vector)
    {
        glUniform4fv(glGetUniformLocation(_id, location), 1, &vector[0]);
    }

    void Shader::printID()
    {
        std::cout << _id << std::endl;
    }
};

Texture.hpp

#pragma once

#include <GL/glew.h>
#include <SFML/Graphics/Image.hpp>

namespace Resource {

    class Texture {
    public:
        Texture(std::string image_src);
        Texture();
        ~Texture();

        void bind();

        Texture(const Texture&) = delete;
        Texture& operator=(const Texture&) = delete;

    private:
        GLuint _id;
    };
}

Texture.cpp

#include "Texture.hpp"

namespace Resource {

    Texture::Texture(std::string image_src)
    {
        glGenTextures(1, &_id);
        bind();
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glGenerateMipmap(GL_TEXTURE_2D);
        sf::Image texture;
        texture.loadFromFile(image_src);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture.getSize().x, texture.getSize().y, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture.getPixelsPtr());
    }

    Texture::Texture()
    {
    }

    Texture::~Texture()
    {
        glDeleteTextures(1, &_id);
    }

    void Texture::bind()
    {
        glBindTexture(GL_TEXTURE_2D, _id);
    }
};

我对c ++还是有些陌生,因此如果可以做出任何明显的改进,请提前抱歉。

1 个答案:

答案 0 :(得分:1)

glUniform*指定当前程序对象的统一变量的值。

这意味着您必须调用glUseProgram来将propper程序对象安装为当前渲染状态的一部分,否则您可以通过glUniform*来设置统一值。

这意味着您必须安装main_shader,因为您必须在方法Game::__init__中设置制服的值才能解决此问题:

main_shader->use();
main_shader->setMat4("Projection", Projection);
main_shader->setMat4("Model", Model);
main_shader->setVec3("Color", glm::vec3(0.1, 0.8, 0.1));

或者,您可以使用glProgramUniform*(自OpenGL 4.1起),它允许您指定应将值设置为的程序对象。


第二个问题是GL_ARRAY_BUFFER不是glDrawArrays的有效枚举常量。如果您通过glGetError检查OpenGL错误,则会得到GL_INVALID_ENUM错误。

您必须使用GL_TRIANGLES

glDrawArrays(GL_TRIANGLES, 0, 6);