-
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
#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
#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.
-
Take a look at the end of the OpenGL + Graphics Module tutorial (http://www.sfml-dev.org/tutorials/2.4/window-opengl.php#using-opengl-together-with-the-graphics-module).
tl;dr: You need to use pushGLStates (http://www.sfml-dev.org/documentation/2.4.0/classsf_1_1RenderTarget.php#a8d1998464ccc54e789aaf990242b47f7)/popGLStates (http://www.sfml-dev.org/documentation/2.4.0/classsf_1_1RenderTarget.php#ad5a98401113df931ddcd54c080f7aa8e), or resetGLStates (http://www.sfml-dev.org/documentation/2.4.0/classsf_1_1RenderTarget.php#aac7504990d27dada4bfe3c7866920765).
-
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.
-
http://en.sfml-dev.org/forums/index.php?topic=20968.msg150056#msg150056
-
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.
-
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
-
I do:
while(bRunning)
{
(events)
(make oApp active)
(clear oApp)
(push states)
(reset states)
(draw sprite [SFML])
(pop states)
(opengl things)
(display)
}
-
You still miss a (reset your own OGL 3+ states) line before drawing with SFML.
-
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.
-
Can you show your updated code?
-
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();
}
}
-
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.
-
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?
-
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.
-
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.
-
I'm getting a bit of a problem now with the simplest thing.
If I change the body of my LightEngine::draw function to just
void draw(sf::RenderTarget& rt, sf::RenderStates states) const
{
glBindVertexArray(m_u32VAO);
glBindVertexArray(0);
}
I get this in the console each frame:
An internal OpenGL call failed in Shader.cpp(815).
Expression:
GLEXT_glUseProgramObject(0)
Error description:
GL_INVALID_OPERATION
The specified operation is not allowed in the current state.
Could be worth noting that glGetError is still returning 0, even with this error printing to the console and the sprite is rendering correctly.
-
Let's summon binary1248, because this one is out of my knowledge ;)
-
For reference, this is my main now.
#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
{
private:
GLuint m_u32VAO, m_u32VBO, m_u32EBO;
std::vector<Light> m_aoLights;
public:
LightEngine()
{
glGenVertexArrays(1, &m_u32VAO);
glBindVertexArray(m_u32VAO);
// Data for VBO
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
};
glGenBuffers(1, &m_u32VBO);
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)));
glBindBuffer(GL_ARRAY_BUFFER, 0);
// Data for EBO
const GLuint elements[]
{
0, 1, 2,
2, 3, 0
};
glGenBuffers(1, &m_u32EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_u32EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindVertexArray(0);
// 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 render(sf::RenderTarget& rt)
{
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::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.clear();
oApp.pushGLStates();
oApp.resetGLStates();
oApp.draw(oSprite);
oApp.popGLStates();
sf::Shader::bind(&oShader);
glActiveTexture(GL_TEXTURE2);
sf::Texture::bind(&oRT.getTexture());
glUniform1i(0, 2);
glActiveTexture(GL_TEXTURE3);
sf::Texture::bind(&oRTNormals.getTexture());
glUniform1i(1, 3);
oEngine.render(oApp);
// Cleanup. Reset Texture2 and 3 and unbind any shaders.
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, 0);
sf::Shader::bind(nullptr);
oApp.resetGLStates();
oApp.display();
}
}
-
I changed
sf::Shader::bind(nullptr);
To:
glUseProgram(0);
The above error is replaced with this one:
An internal OpenGL call failed in RenderTarget.cpp(369).
Expression:
GLEXT_glClientActiveTexture(GLEXT_GL_TEXTURE0)
Error description:
GL_INVALID_OPERATION
The specified operation is not allowed in the current state.
-
Ok I finally got it working.
Result: http://i.imgur.com/wnRIzua.gifv
Code:
#include <vector>
#include <SFML/Graphics.hpp>
#include <GL/glew.h>
sf::Vector2i mousepos;
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);
}
};
Light* mouseLight;
class LightEngine
{
private:
GLuint m_u32VAO, m_u32VBO, m_u32EBO;
std::vector<Light> m_aoLights;
public:
LightEngine()
{
glGenVertexArrays(1, &m_u32VAO);
glBindVertexArray(m_u32VAO);
// Data for VBO
const GLfloat verts[]
{
-1.0f, 1.0f, 0.0f, 0.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
};
glGenBuffers(1, &m_u32VBO);
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)));
// Data for EBO
const GLuint elements[]
{
0, 1, 2,
2, 3, 0
};
glGenBuffers(1, &m_u32EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_u32EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
// Create a test light
Light oLight;
oLight.setPosition({512, 384});
oLight.color = sf::Color::White;
oLight.falloff = {0.4f, 3.0f, 20.0f};
m_aoLights.push_back(oLight);
mouseLight = &*m_aoLights.rbegin();
/*
// Multiple lights dont work
Light oLight2;
oLight2.setPosition({100, 100});
oLight2.color = sf::Color::Green;
m_aoLights.push_back(oLight2);
Light oLight3;
oLight3.setPosition({300, 250});
oLight3.color = sf::Color::Red;
m_aoLights.push_back(oLight3);
*/
}
void render(sf::RenderTarget& rt)
{
mouseLight->setPosition(static_cast<sf::Vector2f>(mousepos));
glBindVertexArray(m_u32VAO);
for (const auto& rkoLight : m_aoLights)
{
rkoLight.draw();
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::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);
LightEngine oEngine;
sf::Clock oClock;
bool bRunning = true;
while (bRunning)
{
sf::Event oEvent;
while (oApp.pollEvent(oEvent))
{
switch (oEvent.type)
{
case sf::Event::Closed:
{
bRunning = false;
break;
}
}
}
mousepos = sf::Mouse::getPosition(oApp);
const auto kfSeconds = oClock.getElapsedTime().asSeconds();
mouseLight->color = {static_cast<sf::Uint8>(255 * std::abs(std::sin(kfSeconds))), static_cast<sf::Uint8>(255 * std::abs(std::cos(kfSeconds))), 255, 255};
oApp.clear();
oApp.pushGLStates();
oApp.resetGLStates();
oApp.draw(oSprite);
oApp.popGLStates();
glUseProgram(oShader.getNativeHandle());
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, oRT.getTexture().getNativeHandle());
glUniform1i(0, 2);
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, oRTNormals.getTexture().getNativeHandle());
glUniform1i(1, 3);
oEngine.render(oApp);
// Cleanup. Reset Texture2 and 3 and unbind any shaders.
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgram(0);
glActiveTexture(GL_TEXTURE0);
oApp.display();
}
}
It turns out that NOT setting the active texture to 0 before displaying the RenderWindow makes SFML die.
A few caveats to the result, the final result is actually displayed upside-down and multiple lights dont work and I'm not entirely sure how to fix that(Inverting the y uv coordinate fixed this). I tried adding a RenderTexture member to LightEngine, creating and activating it in the constructor, generating, binding and buffering all my data then making the RenderTexture inactive then each draw call active it again, bind the VAO and do the drawing where all lights are drawn with additive blending then at the end create a sprite from the RenderTexture and render it to the RenderTarget passed to the draw call with multiplicative blending but it didnt work and I'm pretty sure I'm doing something wrong.