2017-09-03 114 views
0

我試圖使用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:解決拉絲問題,請參閱我的答案。

回答

0

我找到了答案!這一切都歸功於「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; 
    } 
} 

現在一切正常。