Hi, I've been trying to implement lights and shadows into a small game I'm working on. I have three RenderTexture's (or maps) - color, shadow and light. I create three sprites, add the textures onto them and draw them to the window. This works fine and setting the sprite's position to the active view makes them behave like expected.
The problem I'm facing is that as soon as I throw shaders into the mix, this breaks. The lights, which are made with a shader, will "stick" to the view once applied. So instead of them staying where they should, they move with the view.
Here's two images showing what I mean:When the lights are on-top of where they should be
https://i.imgur.com/4WNwkBB.pngAfter moving the view a bit to the left
https://i.imgur.com/bZGdgWX.pngAnd a video with the color and shadow map removed (flickering is unrelated, - just kwin and ffmpeg not behaving)
https://horobox.co.uk/u/udY6bf.mp4And finally, some code.
The main draw function that puts it all together:
void Renderer::draw(sf::RenderWindow &window)
// draw everything!
{
window.clear();
window.setView(*view);
createRenderTextures();
createColorMap();
createLightMap();
sf::Sprite colorOutput {colorMap.getTexture()};
colorOutput.setPosition(view->getCenter() - view->getSize() / 2.f);
sf::Sprite lightOutput {lightMap.getTexture()};
lightOutput.setPosition(view->getCenter() - view->getSize() / 2.f);
sf::Sprite shadowOutput {shadowMap.getTexture()};
shadowOutput.setPosition(view->getCenter() - view->getSize() / 2.f);
window.draw(colorOutput);
window.draw(shadowOutput, sf::BlendMultiply);
window.draw(lightOutput, sf::BlendMultiply);
tileManager->draw(window);
window.setView(window.getDefaultView());
uiManager->draw(window);
window.display();
}
Function responsible for drawing shadows and lights
void Renderer::createLightMap()
// create the light map of the world
{
shadowMap.clear();
lightMap.clear();
for (Light& light : objManager->getLights())
light.getPointLight().render(shadowMap, lightMap, *objManager->getAllObjects(), *view);
shadowMap.display();
lightMap.display();
}
The code that draws the lights themselves in render()
// this is in the constructor of light
lightFrag = assets->getShader("light.frag");
lightFrag->setUniform("color", sf::Glsl::Vec4(color));
lightStates.shader = lightFrag;
// render function
sf::RectangleShape lightShape;
lightShape.setSize(sf::Vector2f(radius * 2, radius * 2));
lightShape.setPosition(position - lightShape.getSize() / 2.f); // 'position' is the position of the object that emits the light, like a lamp
lightFrag->setUniform("viewportRes", lightShape.getSize() / 2.f);
lightFrag->setUniform("origin", sf::Vector2f(position.x + lightShape.getSize().x / 2, invertCoordinateY(position.y + lightShape.getSize().y / 2, view.getSize().
lightMap.draw(lightShape, lightStates);
And finally, the shader that creates the light effect
uniform vec2 viewportRes;
uniform vec2 origin;
uniform vec4 color;
void main( out vec4 fragColor, in vec2 fragCoord )
{
// pixel coords
vec2 xy = vec2(gl_FragCoord.x, gl_FragCoord.y);
// pixel distance from center
vec2 vecDistance = vec2((origin.x - xy.x) * (origin.x - xy.x),
(origin.y - xy.y) * (origin.y - xy.y));
float distanceToCenter = sqrt(vecDistance.x + vecDistance.y);
float distancePercent = distanceToCenter / viewportRes.x;
// output to screen
color = vec4(color.r - distancePercent, color.g - distancePercent, color.b - distancePercent, 1.0);
fragColor = vec4(color);
}
Any ideas? Since it works fine without the shader, I assume there's something off with the logic of the shader or the coordinates I give the shader. It also happens with a blur shader I use for the shadow map but I decided to not include it since it seems like the blur and light shader share the same problem.
Bonus question:
Since I'm here, is there a straight forward way of making sure a light doesn't "interfere" with another light? Not sure how to explain it, you most likely saw it in the images I linked. I'll link another image that shows it more clearly below.
https://i.imgur.com/EXMD6Ka.png