将OpenGL网格渲染为SFML RenderTexture

时间:2017-09-03 03:44:09

标签: c++ opengl sfml

我正在尝试为项目使用SFML和OpenGL的组合,但是我无法呈现给sf::RenderTexture。具体来说,如果我在RenderTexture处于活动状态时尝试绘制,我会崩溃。 (看起来像glDrawElements中的空指针取消引用。)

直接渲染到窗口工作正常。如果我自己通过OpenGL手动创建一个帧缓冲区,那也可以正常工作。但是如果可能的话,我希望能够使用RenderTexture来简化很多代码。

我可能做了些蠢事,但我还是OpenGL的新手,所以我不确定。 (特别是在SFML和OpenGL混合的情况下,如果你不正确地管理上下文切换,似乎很多东西都会破坏。)我没有看到来自OpenGL或SFML的任何警告。

以下再现了我所看到的问题(Windows 10,Visual Studio 2017,OpenGL 4.5,GLEW 2.1.0,SFML 2.4.0):

#include <iostream>
#include <string>
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#define GL_GLEXT_PROTOTYPES
#include <GL/glew.h>
#include <SFML/OpenGL.hpp>

GLenum glCheckError_(const char *file, int line)
{
    GLenum errorCode;
    while ((errorCode = glGetError()) != GL_NO_ERROR)
    {
        std::string error;
        switch (errorCode)
        {
        case GL_INVALID_ENUM:                  error = "INVALID_ENUM"; break;
        case GL_INVALID_VALUE:                 error = "INVALID_VALUE"; break;
        case GL_INVALID_OPERATION:             error = "INVALID_OPERATION"; break;
        case GL_STACK_OVERFLOW:                error = "STACK_OVERFLOW"; break;
        case GL_STACK_UNDERFLOW:               error = "STACK_UNDERFLOW"; break;
        case GL_OUT_OF_MEMORY:                 error = "OUT_OF_MEMORY"; break;
        case GL_INVALID_FRAMEBUFFER_OPERATION: error = "INVALID_FRAMEBUFFER_OPERATION"; break;
        }
        std::cerr << error << " | " << file << " (" << line << ")" << std::endl;
    }
    return errorCode;
}
#define glCheckError() glCheckError_(__FILE__, __LINE__)

int main()
{
    sf::RenderWindow window(sf::VideoMode(800, 600, 32), "test");
    glewInit();
    std::cout << "Using OpenGL " << window.getSettings().majorVersion << "." << window.getSettings().minorVersion << std::endl;
    //std::cout << "Available GL extensions: " << glGetString(GL_EXTENSIONS) << std::endl;

    sf::Shader shader;
    { // Shader
        const char* vs = R"(
            #version 330 core
            layout (location = 0) in vec3 pos;

            void main()
            {
                gl_Position = vec4(pos, 1.0);
            }
        )";
        const char* fs = R"(
            #version 330 core
            out vec4 color;

            void main()
            {
                color = vec4(0.3, 0.8, 0.2, 1.0);
            }
        )";
        shader.loadFromMemory(vs, fs);
    }

    unsigned int vao;
    { // Mesh
        float vertices[] = {
             0.3f,  0.5f,  1.0f,  // top right
             0.5f, -0.5f, -0.5f,  // bottom right
            -0.5f, -0.5f, -1.0f,  // bottom left
            -0.3f,  0.5f,  0.5f,  // top left
        };
        unsigned int indices[] = {
            0, 3, 1,  // first triangle
            1, 3, 2,  // second triangle
        };
        unsigned int vbo, ebo;
        glGenVertexArrays(1, &vao);
        glCheckError();
        glGenBuffers(1, &vbo);
        glCheckError();
        glGenBuffers(1, &ebo);
        glCheckError();
        glBindVertexArray(vao);
        glCheckError();
        glBindBuffer(GL_ARRAY_BUFFER, vbo);
        glCheckError();

        glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
        glCheckError();

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
        glCheckError();
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
        glCheckError();

        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
        glCheckError();

        glEnableVertexAttribArray(0);
        glCheckError();
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glCheckError();
        glBindVertexArray(0);
        glCheckError();
    }

    sf::RenderTexture texture;
    sf::Sprite sprite;
    { // Render Texture
        if (!texture.create(800, 600, true)) {
            std::cerr << "Failed to create RenderTexture" << std::endl;
        }
        sprite.setTexture(texture.getTexture());
    }

    int frame = 0;
    while (window.isOpen())
    {
        ++frame;
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
            {
                window.close();
            }
        }

        window.clear();
        if (frame > 1)
        {
            window.popGLStates();
        }

        { // Render to screen
            sf::Shader::bind(&shader);
            glBindVertexArray(vao);
            glCheckError();

            glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
            glCheckError();

            glBindVertexArray(0);
            glCheckError();
            sf::Shader::bind(nullptr);
        }

        window.pushGLStates();
        window.display();

        // Press space to continue...
        bool waiting = true;
        while (waiting) {
            while (window.pollEvent(event))
            {
                if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Space)
                {
                    waiting = false;
                    break;
                }
            }
        }

        window.clear();
        if (frame > 1)
        {
            window.popGLStates();
        }

        { // Render to texture
            sf::Shader::bind(&shader);
            glBindVertexArray(vao);
            glCheckError();

            texture.pushGLStates();
            if (!texture.setActive(true)) { // TODO Setting the texture as active is causing me to segfault, messing up my state somehow
                std::cerr << "Failed to activate RenderTexture" << std::endl;
            }
            texture.clear();
            texture.popGLStates();

            glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); // <-- Crashes here!
            glCheckError();

            texture.pushGLStates();
            texture.display();
            if (!texture.setActive(false)) {
                std::cerr << "Failed to deactivate RenderTexture" << std::endl;
            }
            texture.popGLStates();

            glBindVertexArray(0);
            glCheckError();
            sf::Shader::bind(nullptr);
        }

        window.pushGLStates();
        window.draw(sprite);
        window.display();
    }
};

有没有人有任何想法?

编辑:我已经解决了崩溃的部分。 sf::RenderTexture有自己的GL上下文,我想你不能在上下文之间重用数据。所以我必须先生成纹理,然后在生成着色器和网格之前使用texture.setActive()。这样,上下文就有了这些对象。

现在我只是一个黑屏。我可以将新的sf::RectangleShape绘制到同一RenderTexture,但我的GL网格似乎没有绘制。还在调查......

如果有人遇到同样的问题,以下是我必须改变的内容:

// --- initialization ---

// Generate the texture first so its context is available
sf::RenderTexture texture;
sf::Sprite sprite;
{ // Render Texture
    if (!texture.create(800, 600, true)) {
        std::cerr << "Failed to create RenderTexture" << std::endl;
    }
    sprite.setTexture(texture.getTexture());
}

// Generate the rest of the data within the texture's context
sf::Shader shader;
{ // Shader
    if (!texture.setActive(true)) {
        std::cerr << "Failed to activate RenderTexture" << std::endl;
    }
    shader.loadFromMemory(vs, fs);
    if (!texture.setActive(false)) {
        std::cerr << "Failed to deactivate RenderTexture" << std::endl;
    }
}

unsigned int vao;
{ // Mesh
    if (!texture.setActive(true)) {
        std::cerr << "Failed to activate RenderTexture" << std::endl;
    }
    unsigned int vbo, ebo;
    glGenVertexArrays(1, &vao);
    glCheckError();
    glGenBuffers(1, &vbo);
    glCheckError();

    // ...

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glCheckError();
    glBindVertexArray(0);
    glCheckError();
    if (!texture.setActive(false)) {
        std::cerr << "Failed to deactivate RenderTexture" << std::endl;
    }
}

// --- drawing ---

{ // Render to texture
    // Make sure we use the appropriate context for all drawing to texture
    if (!texture.setActive(true)) {
        std::cerr << "Failed to activate RenderTexture" << std::endl;
    }
    texture.clear();
    sf::Shader::bind(&shader);

    glBindVertexArray(vao);
    glCheckError();

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); // TODO rendering nothing here...
    glCheckError();

    glBindVertexArray(0);
    glCheckError();
    sf::Shader::bind(nullptr);

    // Drawing to the texture through SFML works fine
    texture.pushGLStates();
    sf::RectangleShape rect(sf::Vector2f(20, 20));
    rect.setFillColor(sf::Color::Cyan);
    texture.draw(rect);
    texture.popGLStates();

    texture.display();
    if (!texture.setActive(false)) {
        std::cerr << "Failed to deactivate RenderTexture" << std::endl;
    }
}

if (!window.setActive(true)) {
    std::cerr << "Failed to activate window" << std::endl;
}
window.pushGLStates();
window.draw(sprite);
window.display();

编辑2:绘图问题已解决,请参阅我的回答。

1 个答案:

答案 0 :(得分:0)

我找到了答案!感谢&#34; Groogy&#34;在这个帖子中:https://en.sfml-dev.org/forums/index.php?topic=7446.0

我之前已经浏览过该帖子,这促使我在创建texture.setView(texture.getDefaultView());时添加RenderTexture。但是这还不够,我不得不用纹理绑定调用glViewport。 (我认为这是sf::View在封面下会做的事情,但显然事实并非如此。)

所以我的RenderTexture创建现在看起来像这样:

sf::RenderTexture texture;
sf::Sprite sprite;
{ // Render Texture
    if (!texture.create(800, 600, true)) {
        std::cerr << "Failed to create RenderTexture" << std::endl;
    }
    if (!texture.setActive(true)) {
        std::cerr << "Failed to activate texture" << std::endl;
    }
    sprite.setTexture(texture.getTexture());

    glViewport(0, 0, 800, 600); // <-- Required
    glCheckError();

    if (!texture.setActive(false)) {
        std::cerr << "Failed to deactivate texture" << std::endl;
    }
}

现在事情正在发挥作用。