Shaders - light effects
Test whether shaders work
- Martin
- 4 min read
General
Even though I want to give the game a certain retro look, I want to use shaders.
In the video below, you can see me experimenting with light effects. If you look closely, I walk through the light and the player will also be affected by the effect.
This includes moving shaders, e.g. for torches, which we will need in this adventure.
Design
If I had to represent this as a diagram, it would look something like this, but unfortunately it is a lot more complicated to create.

Schader schema
Experience has shown that in love2d it is not possible to render multiple shaders during the same draw loop. This is not possible, and I only discovered this when I had one shader working and wanted to activate a second one.
shader1 = love.graphics.newShader('color-dodge.glsl')
shader2 = love.graphics.newShader('color-dodge.glsl')
function love.draw()
love.graphics.push("all")
love.graphics.setCanvas(src)
love.graphics.clear()
love.graphics.setShader()
love.graphics.draw(scene, 0, 0)
love.graphics.draw(player, playerPos.x, playerPos.y)
love.graphics.setShader(shader1)
love.graphics.setShader(shader2)
love.graphics.setCanvas()
love.graphics.setShader()
love.graphics.draw(src, 0, 0)
end
Fortunately, there is a solution to this problem, namely ‘multi-pass’ rendering. This basically means rewriting the result you draw to the canvas to another canvas and then drawing the shader. You can then repeat this for multiple shaders.
The function below currently works well, although I still need to test it further.
function Engine3:applyIntermediateShader(shader)
if not shader then return end
local src = self.canvas:getCanvas()
local front = self.canvas:getCanvasFront()
-- Store current graphics state
local currentCanvas = love.graphics.getCanvas()
local currentShader = love.graphics.getShader()
-- Apply shader: src -> front
love.graphics.push("all")
love.graphics.origin()
love.graphics.setCanvas(front)
love.graphics.clear()
love.graphics.setShader(shader:getShader())
love.graphics.draw(src, 0, 0)
love.graphics.setShader()
love.graphics.pop()
-- Copy result back to main canvas: front -> src
love.graphics.push("all")
love.graphics.origin()
love.graphics.setCanvas(src)
love.graphics.clear()
love.graphics.draw(front, 0, 0)
love.graphics.pop()
-- Restore graphics state for continued rendering
love.graphics.setCanvas(currentCanvas)
love.graphics.setShader(currentShader)
end
Color Dodge shader
In the Photoshop/Aseprite design, I use the color dodge filter.

Color Dodge Photoshop
So I had to recreate this in the game and the editor somehow.

Editor shader selector
I hardly ever use AI for programming when developing the game. Sometimes, however, I know that it is a small part that I am not yet familiar with and can use as a starting point. For example, I asked for a GLSL shader to be created with the name ‘Color Dodge’ that is similar to what Photoshop does.
Of course, that didn’t work well, because the coordinates I use are different and I also use scaling and other things. The final glsl file is currently.
extern vec2 position;
extern Image image;
extern float scale;
extern float overlayIntensity;
extern vec4 quadRect;
vec3 colorDodge(vec3 base, vec3 blend) {
return vec3(
blend.r >= 0.999 ? 1.0 : min(1.0, base.r / max(0.001, 1.0 - blend.r)),
blend.g >= 0.999 ? 1.0 : min(1.0, base.g / max(0.001, 1.0 - blend.g)),
blend.b >= 0.999 ? 1.0 : min(1.0, base.b / max(0.001, 1.0 - blend.b))
);
}
vec4 effect(vec4 color, Image tex, vec2 tc, vec2 sc)
{
vec4 base = texture(tex, tc);
vec2 texSize = vec2(textureSize(image, 0));
vec2 qpos = quadRect.xy;
vec2 qsize = quadRect.zw;
vec2 uv = (sc - position) / (qsize * scale);
if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) {
return base;
}
uv = (uv * qsize + qpos) / texSize;
vec4 overlay = texture(image, uv);
if (overlay.a > 0.0) {
vec3 enhancedOverlay = min(vec3(1.0), overlay.rgb * overlayIntensity);
vec3 dodged = colorDodge(base.rgb, enhancedOverlay);
float enhancedAlpha = min(1.0, overlay.a * overlayIntensity);
vec3 result = mix(base.rgb, dodged, enhancedAlpha);
return vec4(result, base.a);
}
return base;
}
In the shader class, I pass the Frameset to the shader and the selected effect.
if frameset then
self:setImage(frameset.resource)
self:setScale(nil, true)
self:setPosition(frameset:getFrameX(), frameset:getFrameY(), true)
if frameset:getQuad() then
self:setQuad(frameset:getQuad():getViewport())
end
end
In conclusion
I managed to get Photoshop’s color doge into the game, but I still need to try other things such as shadows and a combination with the particle system for fire in combination with shader effects.
Still, I think it’s necessary to make it all a bit more beautiful. Fortunately, the glsl filters can also be found on various websites, so I don’t have to reinvent them from scratch.


