Shaders - licht effecten
Test of shaders werken
- Martin
- 4 min read
Algemeen
Ondanks dat ik het spel een bepaalde retro uitstraling geef wil ik gebruikmaken van shaders.
In de video hieronder zie je dat ik experimenteer met licht effecten. Wanneer je goed kijkt loop ik door het licht heen en zal de speler ook beinvloed worden door het effect.
Inclusief bewegende shaders bijv. voor fakkels die we nodig gaan hebben in dit avontuur.
Ontwerp
Wanneer ik dit als een schema moet uitbeelden is het ongeveer zoals dit, alleen is het helaaas een stuk ingewikkelder om te maken.

Schader schema
De ervaring leert dat het in love2d niet mogelijk is meerdere shaders te renderen tijdens dezelfde draw loop. Dit is niet mogelijk en daar kwam ik pas achter toen ik 1 shader werkend had en een 2de actief wou maken.
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
Gelukkig is er een oplossing voor dit probleem namelijk ‘multi-pass’ rendering. Dit komt neer om het resultaat wat je tekent naar het canvas, opnieuw weg te schrijven naar een ander canvas en daarna te schader te tekenen. Dit kun je vervolgens voor meerdere shaders herhalen.
De onderstaande functie werkt op dit moment goed al moet ik het nog beter testen.
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 het Photoshop/Aseprite ontwerp maak ik gebruik van de filter color dodge.

Color Dodge Photoshop
In het spel en de editor moest ik dit dus op een of andere manier namaken.

Editor shader selector
Ik gebruik bijna geen AI om te programmeren tijdens het ontwikkelen van het spel. Soms echter weet ik dat het een klein onderdeel is waar ik nog geen kennis van heb en kan gebruiken als startpunt. Zo vroeg ik om een glsl shader te maken met de naam ‘Color Dodge’ die gelijk is aan wat Photoshop doet.
Dat werkte natuurlijk niet goed, want de coordinaten die ik gebruik zijn anders en daarnaast gebruik ik ook scaling en andere zaken. De uiteindelijke glsl file is op dit moment.
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 de shader class geef ik de Frameset door aan de shader en het gekozen 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
Tot slot
Het is gelukt om de color doge van photoshop in het spel te krijgen maar ik moet nog andere dingen proberen zoals schaduws en een combinatie met het particle systeem voor vuur in combinatie met shader effecten.
Toch denk ik dat het nodig is om het net allemaal wat mooier te maken. De glsl filters zijn gelukkig ook op diverse website te vinden, dus die hoef ik niet helemaal opnieuw te verzinnen.


