Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Issues mixing SFML and OpenGL  (Read 2537 times)

0 Members and 1 Guest are viewing this topic.

Gollum999

  • Newbie
  • *
  • Posts: 4
    • View Profile
Issues mixing SFML and OpenGL
« on: September 04, 2017, 08:54:04 am »
As the title says, I've been having some issues mixing OpenGL and SFML.

Ultimately, I want to be able to draw to a framebuffer, and then sample from that framebuffer in shaders that most of my sprites will use.  The object drawn to the framebuffer will be 3D, but everything else is 2D, so ideally most of this can be done with SFML rather than raw OpenGL.

I already have a 100% OpenGL solution that works, however obviously it would be nice if I could clean things up with SFML instead.  Specifically, I already use sf::Sprite in a lot of places, so I want to get things working with that so I don't need to re-write all of my drawing code.

In order to get this working, I think I need one of two things to work:

1. Binding an OpenGL texture to an sf::Sprite
I have not been able to get this to work; nothing is drawn to the screen.  I have also tried this with an sf::RectangleShape, which does get drawn to the screen, but is missing the texture.

The relevant bits of my code for this part look like this:
// --- initialization ---

{ // Shaders
    const char* vs = R"(
        #version 330 core
        layout (location = 0) in vec2 pos;
        layout (location = 1) in vec2 texCoords;

        out vec2 TexCoords;

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

        in vec2 TexCoords;

        uniform sampler2D screenTexture;

        void main()
        {
            color = vec4(texture(screenTexture, TexCoords).rrr, 1.0);
        }
    )"
;
    makeShader(_spriteShader, vs, fs);
}

{ // FrameBuffer
    glGenFramebuffers(1, &_frameBuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
    glGenTextures(1, &_sampleTexture);
    glBindTexture(GL_TEXTURE_2D, _sampleTexture);
    // glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, CONST.WIN.WIDTH, CONST.WIN.HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, CONST.WIN.WIDTH, CONST.WIN.HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); // Only using the depth component for right now
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _sampleTexture, 0);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, _sampleTexture, 0);
    glBindTexture(GL_TEXTURE_2D, 0);
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

sprite.setScale(sf::Vector2f(1.0 / 3.0, 1.0 / 3.0));
sprite.setPosition(0, WINDOW_HEIGHT * 2.0 / 3.0);

// --- drawing ---

// ...
// draw to framebuffer...
// ...

// draw framebuffer to screen
_window.pushGLStates();
_window.setView(_window.getDefaultView());

glUseProgram(_spriteShader);
glBindTexture(GL_TEXTURE_2D, _texture);

_window.draw(sprite);
rect.setPosition(0,0);
_window.draw(rect);

glBindTexture(GL_TEXTURE_2D, 0);
glUseProgram(0);
_window.popGLStates();
 

I'm not sure how the implementation of sf::Sprite handles vertex and texture coordinates, so there definitely could be an issue there compared to what my shader is expecting.

Alternatively, is there a way to create an sf::Texture from an OpenGL texture (preferably without making a copy?)  I'm not sure if the shader or the texture is to blame here, but if it's the texture, that might be an easier solution.

2. Using an sf::RenderTexture instead of an OpenGL framebuffer + texture
I was having some other issues with this one earlier, so you can see my code for this on StackOverflow.  I was able to solve some of the problems, but as of right now I can draw SFML objects to my RenderTexture, but my OpenGL draw calls to the same RenderTexture don't seem to do anything.  I am at a loss for this one because I was able to draw directly to the window with minimal modifications, and it works fine.

Anyway, does anyone know if either of these solutions is possible, or if I am doing something wrong?  I am new to OpenGL, so there is a high likelihood that both of these issues are just stupid mistakes on my part.  Or maybe there is an even simpler solution that I don't know about yet.

Any insight is appreciated.  Please let me know if I should provide more information or code.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: Issues mixing SFML and OpenGL
« Reply #1 on: September 04, 2017, 10:34:27 am »
Quote
1. Binding an OpenGL texture to an sf::Sprite
You're not supposed to do this, but with the proper shader I guess it will work. Please have a look at this:
https://www.sfml-dev.org/tutorials/2.4/graphics-shader.php#minimal-shaders

But why don't you simply create a textured quad yourself, instead of using sf::Sprite?

Quote
2. Using an sf::RenderTexture instead of an OpenGL framebuffer + texture
That should definitely work. Maybe a problem with your shaders?
Laurent Gomila - SFML developer

Gollum999

  • Newbie
  • *
  • Posts: 4
    • View Profile
Re: Issues mixing SFML and OpenGL
« Reply #2 on: September 04, 2017, 07:21:48 pm »
Thanks for the fast reply.

Quote
But why don't you simply create a textured quad yourself, instead of using sf::Sprite?

If I did that, I would have to handle things like projections and transformations myself, right?  It's definitely possible, but it seems like it would be a pretty big pain.

Quote
That should definitely work. Maybe a problem with your shaders?

Maybe.  I've thrown together an example that demonstrates my issue.  If I comment out the #define DRAW_TO_TEXTURE, everything draws to the window correctly.  But when drawing to the RenderTexture, my SFML draw calls work, but my OpenGL draw calls do not.  The only difference between these two (besides the render target) is the bit at the end that draws the RenderTexture sprite to the screen.

#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__)

#define DRAW_TO_TEXTURE
#ifdef DRAW_TO_TEXTURE
#  define TARGET texture
#else
#  define TARGET window
#endif

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::RenderTexture texture;
    sf::Sprite sprite;
    { // Render Texture
        if (!texture.create(800, 600, true)) {
            std::cerr << "Failed to create RenderTexture" << std::endl;
        }
        texture.setView(texture.getDefaultView());
        sprite.setTexture(texture.getTexture());
    }

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

    sf::Shader shader;
    { // Shader
        if (!TARGET.setActive(true)) {
            std::cerr << "Failed to activate RenderTarget" << std::endl;
        }
        shader.loadFromMemory(vs, fs);
        if (!TARGET.setActive(false)) {
            std::cerr << "Failed to deactivate RenderTarget" << std::endl;
        }
    }

    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 vao;
    { // Mesh
        if (!TARGET.setActive(true)) {
            std::cerr << "Failed to activate RenderTarget" << std::endl;
        }
        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();

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

        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glCheckError();
        glBindVertexArray(0);
        glCheckError();
        if (!TARGET.setActive(true)) {
            std::cerr << "Failed to activate RenderTarget" << std::endl;
        }
    }

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

        if (!window.setActive(true)) {
            std::cerr << "Failed to activate window" << std::endl;
        }
        window.clear(sf::Color::Red);

        { // Draw
            if (!TARGET.setActive(true)) {
                std::cerr << "Failed to activate RenderTarget" << std::endl;
            }
            TARGET.clear(sf::Color::Magenta);

            TARGET.pushGLStates();
            sf::RectangleShape rect(sf::Vector2f(20, 20));
            rect.setFillColor(sf::Color::Cyan);
            TARGET.draw(rect);
            TARGET.popGLStates();

            sf::Shader::bind(&shader);
            glBindVertexArray(vao);
            glCheckError();

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

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

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

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

Gollum999

  • Newbie
  • *
  • Posts: 4
    • View Profile
Re: Issues mixing SFML and OpenGL
« Reply #3 on: September 06, 2017, 03:32:24 am »
Just for sanity's sake, I decided to build my test app on my Linux laptop, and I got the same result:

g++ -lGLEW -lGLU -lGL -lsfml-window -lsfml-graphics -lsfml-system test_render_texture.cpp



g++ -lGLEW -lGLU -lGL -lsfml-window -lsfml-graphics -lsfml-system -DDRAW_TO_TEXTURE test_render_texture.cpp


Gollum999

  • Newbie
  • *
  • Posts: 4
    • View Profile
Re: Issues mixing SFML and OpenGL
« Reply #4 on: September 09, 2017, 06:45:17 am »
I found the answer!  All thanks to "Groogy" in this thread: https://en.sfml-dev.org/forums/index.php?topic=7446.0

I had skimmed that thread earlier, which prompted me to add texture.setView(texture.getDefaultView()); when creating the RenderTexture.  However that was not enough, I instead had to call glViewport with the texture bound.  (I assumed that is what sf::View would do under the covers, but apparently that is not the case.)

    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); // SECRET SAUCE
        glCheckError();
        if (!texture.setActive(false)) {
            std::cerr << "Failed to deactivate texture" << std::endl;
        }
    }