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=1It 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.