渲染OpenGL的网格来SFML渲染纹理

问题描述:

我试图使用SFML和OpenGL的组合为一个项目,但我有麻烦渲染到sf::RenderTexture。具体来说,如果我试图在RenderTexture处于活动状态时进行绘制,则会崩溃。 (貌似内glDrawElements空指针。)渲染OpenGL的网格来SFML渲染纹理

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

我可能会做一些愚蠢的事,但我还是新OpenGL的,所以我也不太清楚。 (特别是随着SFML和OpenGL的混合,好像很多东西可以打破,如果你不管理上下文切换正确的),我没有看到从OpenGL或SFML任何警告。

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

#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:解决拉丝问题,请参阅我的答案。

我找到了答案!这一切都归功于“Groogy”在这个线程:https://en.sfml-dev.org/forums/index.php?topic=7446.0

我脱脂线程早些时候,这促使我创建RenderTexture时添加texture.setView(texture.getDefaultView());。然而,这还不够,我不得不打电话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; 
    } 
} 

现在一切正常。