2017-09-03 1 views
0

J'essaie d'utiliser une combinaison de SFML et d'OpenGL pour un projet, mais j'ai des problèmes pour le rendre à sf::RenderTexture. Plus précisément, si j'essaie de dessiner pendant que le RenderTexture est actif, je plante. (Il ressemble à un déréférencement de pointeur nul à l'intérieur de glDrawElements.)Rendu de maillage OpenGL vers SFML RenderTexture

Le rendu directement à la fenêtre fonctionne correctement. Et si je crée moi-même manuellement un framebuffer via OpenGL, cela fonctionne aussi bien. Mais j'aimerais pouvoir utiliser RenderTexture si possible pour simplifier une grande partie du code.

Je fais peut-être quelque chose de stupide, mais je suis encore nouveau sur OpenGL donc je ne suis pas sûr. (Surtout avec le mélange de SFML et d'OpenGL, il semble que beaucoup de choses peuvent casser si vous ne gérez pas correctement le contexte.) Je ne vois aucun avertissement d'OpenGL ou de SFML.

Ce qui suit reproduit la question que je vois (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(); 
    } 
}; 

Quelqu'un at-il des idées?

EDIT: Eh bien, j'ai résolu la partie qui s'écrase. sf::RenderTexture s ont leur propre contexte GL, et je suppose que vous ne pouvez pas réutiliser les données entre les contextes. J'ai donc dû générer la texture en premier et utiliser texture.setActive() avant de générer le shader et le mesh. De cette façon, le contexte a ces objets disponibles.

Maintenant, je reçois un écran noir. Je suis en mesure de dessiner un nouveau sf::RectangleShape au même RenderTexture, mais mon maillage GL ne semble pas dessiner. Toujours enquête ...

Dans le cas où quelqu'un a le même problème, voici des extraits de ce que je devais changer:

// --- 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(); 

EDIT 2: Problèmes de dessin résolus, voir ma réponse.

Répondre

0

J'ai trouvé la réponse! Tout cela grâce à "Groogy" dans ce fil: https://en.sfml-dev.org/forums/index.php?topic=7446.0

J'avais écrémé ce fil plus tôt, ce qui m'a poussé à ajouter texture.setView(texture.getDefaultView()); lors de la création du RenderTexture. Cependant, cela ne suffisait pas, j'ai dû appeler glViewport avec la texture liée. (. Je suppose que est ce que sf::View ferait sous les couvertures, mais apparemment ce n'est pas le cas)

Alors ma RenderTexture création ressemble maintenant à ceci:

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; 
    } 
} 

Et maintenant les choses fonctionnent.