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

Author Topic: [solved] OpenGL with SFML (Assumed context issue)  (Read 16214 times)

0 Members and 1 Guest are viewing this topic.

Gambit

  • Sr. Member
  • ****
  • Posts: 283
    • View Profile
[solved] OpenGL with SFML (Assumed context issue)
« on: October 12, 2016, 08:55:37 am »
Hey all,

I've been struggling with some code with SFML and OpenGL for quite a few hours now and I think I have narrowed it down to an OpenGL context issue.

I'm trying to get lighting working using shaders but I'm trying to get the lights to render on top of everything else. I have made some changes to the code due to testing but I have my problem 100% reproducible.

I have added comments explaining some things  but the run down is that when I DONT draw a sprite, the "lighting" works (For debugging purposes, its a blue square that takes up the whole screen). When I DO draw a sprite, nothing is drawn to the screen at all, not even the sprite.

So without further delay, here is the code with the shaders.

main.cpp
#include <vector>

#include <SFML/Graphics.hpp>
#include <GL/glew.h>

class Light : public sf::Transformable
{
public:
   sf::Color color;
   sf::Vector3f falloff;

   void draw() const
   {
      glUniform2f(2, getPosition().x, getPosition().y);
      glUniform4f(3, color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f);
      glUniform3f(4, falloff.x, falloff.y, falloff.z);
   }
};

class LightEngine : public sf::Drawable
{
private:
   GLuint m_u32VAO, m_u32VBO, m_u32EBO;
   std::vector<Light> m_aoLights;

public:
   LightEngine()
   {
      glGenVertexArrays(1, &m_u32VAO);
      glBindVertexArray(m_u32VAO);

      glGenBuffers(1, &m_u32VBO);

      const GLfloat verts[]
      {
         -1.0f,  1.0f, 0.0f, 1.0f, // Top left
          1.0f,  1.0f, 1.0f, 0.0f, // Top right
          1.0f, -1.0f, 1.0f, 1.0f, // Bottom right
         -1.0f, -1.0f, 0.0f, 1.0f, // Bottom left
      };

      glBindBuffer(GL_ARRAY_BUFFER, m_u32VBO);
      glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
      glEnableVertexAttribArray(0);
      glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), nullptr);
      glEnableVertexAttribArray(1);
      glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), reinterpret_cast<void*>(2 * sizeof(GLfloat)));

      glGenBuffers(1, &m_u32EBO);

      const GLuint elements[]
      {
         0, 1, 2,
         2, 3, 0
      };

      glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_u32EBO);
      glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);

      // Create a test light
      Light oLight;
      oLight.setPosition({512, 384});
      oLight.color = sf::Color::Red;
      oLight.falloff = {0.4f, 3.0f, 20.0f};
      m_aoLights.push_back(oLight);
   }

   void draw(sf::RenderTarget& rt, sf::RenderStates states) const
   {
      glBindVertexArray(m_u32VAO);

      for (const auto& rkoLight : m_aoLights)
      {
         glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
      }

      glBindVertexArray(0);
   }

   ~LightEngine()
   {
      glDeleteBuffers(1, &m_u32EBO);
      glDeleteBuffers(1, &m_u32VBO);
      glDeleteVertexArrays(1, &m_u32VAO);
   }
};

int main()
{
   sf::ContextSettings oContextSettings;
   oContextSettings.antialiasingLevel = 4;
   oContextSettings.depthBits = 24;
   oContextSettings.majorVersion = 3;
   oContextSettings.minorVersion = 3;
   sf::RenderWindow oApp{{1024, 768}, "OpenGL Lighting Test", sf::Style::Default, oContextSettings};

   glewExperimental = 1;
   glewInit();

   sf::Shader oShader;
   oShader.loadFromFile("shaders/Vertex.vert", "shaders/Fragment.frag");
   sf::Shader::bind(&oShader);

   sf::RenderTexture oRT, oRTNormals;
   oRT.create(oApp.getSize().x, oApp.getSize().y);
   oRTNormals.create(oApp.getSize().x, oApp.getSize().y);
   {
      {
         sf::Texture oTex;
         oTex.loadFromFile("bg1.png");
         sf::Sprite oSpr;
         oSpr.setTexture(oTex);
         oSpr.setPosition({5.0f, 5.0f});
         oRT.draw(oSpr);
         oTex.loadFromFile("bg1_n.png");
         oRTNormals.draw(oSpr);
      }
      {
         sf::Texture oTex;
         oTex.loadFromFile("bg2.png");
         sf::Sprite oSpr;
         oSpr.setTexture(oTex);
         oSpr.setPosition({650.0f, 50.0f});
         oRT.draw(oSpr);
         oTex.loadFromFile("bg2_n.png");
         oRTNormals.draw(oSpr);
      }
      {
         sf::Texture oTex;
         oTex.loadFromFile("bg3.png");
         sf::Sprite oSpr;
         oSpr.setTexture(oTex);
         oSpr.setPosition({300.0f, 250.0f});
         oRT.draw(oSpr);
         oTex.loadFromFile("bg3_n.png");
         oRTNormals.draw(oSpr);
      }
   }
   oRT.display();
   oRTNormals.display();

   sf::Sprite oSprite{oRT.getTexture()};

   oApp.setActive(true); // Set the window to active to the LightEngine creates it's VAO, VBO and EBO on the window's context.
                         // This prevents oSprite from drawing anything. Without this call, drawing the light engine causes a crash
   LightEngine oEngine;

   bool bRunning = true;
   while (bRunning)
   {
      sf::Event oEvent;
      while (oApp.pollEvent(oEvent))
      {
         switch (oEvent.type)
         {
         case sf::Event::Closed:
         {
            bRunning = false;
            break;
         }
         }
      }

      oApp.clear();

      oApp.draw(oSprite); // Commenting this line out makes the program draw a blue square on the screen (intended)
                          // Uncommented, nothing is drawn to the screen at all, its just black.

      oApp.setActive(true);
      glActiveTexture(GL_TEXTURE0);
      glBindTexture(GL_TEXTURE_2D, oRT.getTexture().getNativeHandle());
      glActiveTexture(GL_TEXTURE1);
      glBindTexture(GL_TEXTURE_2D, oRTNormals.getTexture().getNativeHandle());
      oApp.draw(oEngine);
      glBindTexture(GL_TEXTURE_2D, 0);

      oApp.display();
   }
}
 

shaders/Vertex.vert
Code: [Select]
#version 450 core

layout(location = 0) in vec2 position;
layout(location = 1) in vec2 uv;

layout(location = 0) out vec2 out_uv;

void main()
{
    out_uv = uv;
    gl_Position = vec4(position, 0.0, 1.0);
};

shaders/Fragment.frag
Code: [Select]
#version 450 core

layout(location = 0) uniform sampler2D u_tex;      // Diffuse map
layout(location = 1) uniform sampler2D u_texNorm;  // Normal map
layout(location = 2) uniform vec2      u_lightPos;
layout(location = 3) uniform vec4      u_lightCol;
layout(location = 4) uniform vec3      u_lightFalloff;

layout(location = 0) in vec2 uv; // UV coords for this fragment

layout(location = 0) out vec4 out_color;

void main()
{
   out_color = vec4(0.0, 0.5, 1.0, 1.0);
}

Requirements:
OpenGL 4.5, SFML (Duh) and GLEW.

Desired output: Given this code, The goal is to have the blue square render on top of oSprite with multiplicative blending.

If anyone can help me figure out why drawing the sprite stops anything from rendering that would be helpful.

Images can be found as a zip here: https://www.dropbox.com/s/lvd8ztlecwvh4w2/lighting.zip?dl=1

It could also be worth noting that glGetError returns 0 in every frame and I grabbed the latest GIT since it has context related changes and it didnt fix the problem I am having.
« Last Edit: October 14, 2016, 08:11:15 pm by Gambit »

dabbertorres

  • Hero Member
  • *****
  • Posts: 505
    • View Profile
    • website/blog
Re: OpenGL with SFML (Assumed context issue)
« Reply #1 on: October 12, 2016, 07:18:04 pm »

Gambit

  • Sr. Member
  • ****
  • Posts: 283
    • View Profile
Re: OpenGL with SFML (Assumed context issue)
« Reply #2 on: October 13, 2016, 09:35:00 am »
Thanks. I wrapped oApp.draw(oSprite); in push/pop and I called oApp.setActive after creating the RenderWindow and set it to active each frame before clearing it and its now drawing properly however its now crashing when it exist LightEngine::draw. I commented out the call to glDrawElements and its no longer crashing.

I checked with glIsVertexArray(m_u32VAO) and it prints 1/true so I'm not entirely sure what the problem is.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Laurent Gomila - SFML developer

Gambit

  • Sr. Member
  • ****
  • Posts: 283
    • View Profile
Re: OpenGL with SFML (Assumed context issue)
« Reply #4 on: October 13, 2016, 12:43:50 pm »
I added the call to oApp.resetGLStates after pushing the states and its still crashing. I'm trying to issue OpenGL 4.5 calls AFTER drawing the sprite.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: OpenGL with SFML (Assumed context issue)
« Reply #5 on: October 13, 2016, 01:03:09 pm »
Since SFML doesn't know about OpenGL 3.0+ states (VBO, VAO), you must:
- reset them before drawing with SFML
- restore them after, for your own drawing
Laurent Gomila - SFML developer

Gambit

  • Sr. Member
  • ****
  • Posts: 283
    • View Profile
Re: OpenGL with SFML (Assumed context issue)
« Reply #6 on: October 13, 2016, 01:10:50 pm »
I do:

while(bRunning)
{
   (events)
   (make oApp active)
   (clear oApp)
   (push states)
   (reset states)
   (draw sprite [SFML])
   (pop states)
   
   (opengl things)
   
   (display)
}
 
« Last Edit: October 13, 2016, 01:14:13 pm by Gambit »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: OpenGL with SFML (Assumed context issue)
« Reply #7 on: October 13, 2016, 01:19:16 pm »
You still miss a (reset your own OGL 3+ states) line before drawing with SFML.
Laurent Gomila - SFML developer

Gambit

  • Sr. Member
  • ****
  • Posts: 283
    • View Profile
Re: OpenGL with SFML (Assumed context issue)
« Reply #8 on: October 13, 2016, 01:31:13 pm »
If you mean when I display the window then thats ok but the crash happens during my own opengl rendering when I call glDrawElements. This crash doesnt happen if I dont draw the sprite.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: OpenGL with SFML (Assumed context issue)
« Reply #9 on: October 13, 2016, 03:00:50 pm »
Can you show your updated code?
Laurent Gomila - SFML developer

Gambit

  • Sr. Member
  • ****
  • Posts: 283
    • View Profile
Re: OpenGL with SFML (Assumed context issue)
« Reply #10 on: October 13, 2016, 03:30:27 pm »
Sure: Here is the while main, shaders remain unchanged from the initial post.

#include <vector>

#include <SFML/Graphics.hpp>
#include <GL/glew.h>

class Light : public sf::Transformable
{
public:
   sf::Color color;
   sf::Vector3f falloff;

   void draw() const
   {
      glUniform2f(2, getPosition().x, getPosition().y);
      glUniform4f(3, color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f);
      glUniform3f(4, falloff.x, falloff.y, falloff.z);
   }
};

class LightEngine : public sf::Drawable
{
private:
   GLuint m_u32VAO, m_u32VBO, m_u32EBO;
   std::vector<Light> m_aoLights;

public:
   LightEngine()
   {
      glGenVertexArrays(1, &m_u32VAO);
      glBindVertexArray(m_u32VAO);

      glGenBuffers(1, &m_u32VBO);

      const GLfloat verts[]
      {
         -1.0f,  1.0f, 0.0f, 1.0f, // Top left
          1.0f,  1.0f, 1.0f, 0.0f, // Top right
          1.0f, -1.0f, 1.0f, 1.0f, // Bottom right
         -1.0f, -1.0f, 0.0f, 1.0f, // Bottom left
      };

      glBindBuffer(GL_ARRAY_BUFFER, m_u32VBO);
      glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
      glEnableVertexAttribArray(0);
      glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), nullptr);
      glEnableVertexAttribArray(1);
      glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), reinterpret_cast<void*>(2 * sizeof(GLfloat)));

      glGenBuffers(1, &m_u32EBO);

      const GLuint elements[]
      {
         0, 1, 2,
         2, 3, 0
      };

      glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_u32EBO);
      glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);

      // Create a test light
      Light oLight;
      oLight.setPosition({512, 384});
      oLight.color = sf::Color::Red;
      oLight.falloff = {0.4f, 3.0f, 20.0f};
      m_aoLights.push_back(oLight);
   }

   void draw(sf::RenderTarget& rt, sf::RenderStates states) const
   {
      glBindVertexArray(m_u32VAO);

      for (const auto& rkoLight : m_aoLights)
      {
         glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
      }

      glBindVertexArray(0);
   }

   ~LightEngine()
   {
      glDeleteBuffers(1, &m_u32EBO);
      glDeleteBuffers(1, &m_u32VBO);
      glDeleteVertexArrays(1, &m_u32VAO);
   }
};

int main()
{
   sf::ContextSettings oContextSettings;
   oContextSettings.antialiasingLevel = 4;
   oContextSettings.depthBits = 24;
   oContextSettings.majorVersion = 3;
   oContextSettings.minorVersion = 3;
   sf::RenderWindow oApp{{1024, 768}, "OpenGL Lighting Test", sf::Style::Default, oContextSettings};
   oApp.setActive(true);

   glewExperimental = 1;
   glewInit();

   sf::Shader oShader;
   oShader.loadFromFile("shaders/Vertex.vert", "shaders/Fragment.frag");
   sf::Shader::bind(&oShader);

   sf::RenderTexture oRT, oRTNormals;
   oRT.create(oApp.getSize().x, oApp.getSize().y);
   oRTNormals.create(oApp.getSize().x, oApp.getSize().y);
   {
      {
         sf::Texture oTex;
         oTex.loadFromFile("bg1.png");
         sf::Sprite oSpr;
         oSpr.setTexture(oTex);
         oSpr.setPosition({5.0f, 5.0f});
         oRT.draw(oSpr);
         oTex.loadFromFile("bg1_n.png");
         oRTNormals.draw(oSpr);
      }
      {
         sf::Texture oTex;
         oTex.loadFromFile("bg2.png");
         sf::Sprite oSpr;
         oSpr.setTexture(oTex);
         oSpr.setPosition({650.0f, 50.0f});
         oRT.draw(oSpr);
         oTex.loadFromFile("bg2_n.png");
         oRTNormals.draw(oSpr);
      }
      {
         sf::Texture oTex;
         oTex.loadFromFile("bg3.png");
         sf::Sprite oSpr;
         oSpr.setTexture(oTex);
         oSpr.setPosition({300.0f, 250.0f});
         oRT.draw(oSpr);
         oTex.loadFromFile("bg3_n.png");
         oRTNormals.draw(oSpr);
      }
   }
   oRT.display();
   oRTNormals.display();

   sf::Sprite oSprite{oRT.getTexture()};

   LightEngine oEngine;

   bool bRunning = true;
   while (bRunning)
   {
      sf::Event oEvent;
      while (oApp.pollEvent(oEvent))
      {
         switch (oEvent.type)
         {
         case sf::Event::Closed:
         {
            bRunning = false;
            break;
         }
         }
      }

      oApp.setActive(true);
      oApp.clear();

      oApp.pushGLStates();
      oApp.resetGLStates();
      oApp.draw(oSprite);
      oApp.popGLStates();

      glActiveTexture(GL_TEXTURE0);
      glBindTexture(GL_TEXTURE_2D, oRT.getTexture().getNativeHandle());
      glActiveTexture(GL_TEXTURE1);
      glBindTexture(GL_TEXTURE_2D, oRTNormals.getTexture().getNativeHandle());

      oApp.draw(oEngine);

      glBindTexture(GL_TEXTURE_2D, 0);

      oApp.display();
   }
}
 

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: OpenGL with SFML (Assumed context issue)
« Reply #11 on: October 13, 2016, 03:51:13 pm »
You're still not doing what we recommend. You must clear all your OpenGL 3+ states before drawing with SFML and you must set all your OpenGL 3+ states back before drawing with OpenGL. You're almost doing nothing, you still have all your states set once in the constructor and left untouched during the whole lifetime of the program.
Laurent Gomila - SFML developer

Gambit

  • Sr. Member
  • ****
  • Posts: 283
    • View Profile
Re: OpenGL with SFML (Assumed context issue)
« Reply #12 on: October 13, 2016, 04:32:01 pm »
I'm resetting the states before I'm drawing the sprite. I would call that clearing it because drawing SFML unless I'm misunderstanding something.

Am I supposed to regenerate all of my object IDs and rebuffer all of my data every frame then?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: OpenGL with SFML (Assumed context issue)
« Reply #13 on: October 13, 2016, 04:55:23 pm »
No, no need to regenerate everything from scratch, your OpenGL objects can continue to exist while drawing with SFML, as long as they are not used. So you must unbind your buffers (VAO, VBO, EBO) before drawing with SFML, and bind them back before drawing with OpenGL.

You may also have to manage texture units > 1, I don't remember.

And finally, I'm not sure if drawing OpenGL inside a sf::Drawable is supposed to work, because you'll get all the SFML OpenGL stuff applied when calling window.draw, before it reaches your LightEngine::draw function and its OpenGL calls.
Laurent Gomila - SFML developer

Gambit

  • Sr. Member
  • ****
  • Posts: 283
    • View Profile
Re: OpenGL with SFML (Assumed context issue)
« Reply #14 on: October 13, 2016, 05:14:36 pm »
Oh ok. I'm still not quite sure myself how I want this to work. The original idea was to render everything in the "game"/application unlit then pass that as an opengl texture to my light engine class where it would apply the lighting to everything but I'm not quite sure how to do that.