Shaders : effets de lumière
Test pour vérifier que les shaders fonctionnent
En général
Même si je donne au jeu un certain look rétro, je veux utiliser des shaders.
Dans la vidéo ci-dessous, vous voyez que je fais des essais avec des effets de lumière. Si vous regardez bien, je traverse la lumière et le joueur est lui aussi touché par l’effet.
Y compris des shaders animés, par exemple pour les torches dont nous aurons besoin dans cette aventure.
Conception
Si je devais le représenter sous forme de schéma, ce serait à peu près comme ça, sauf que c’est malheureusement bien plus compliqué à réaliser.

Schéma des shaders
L’expérience montre qu’avec love2d, il n’est pas possible de rendre plusieurs shaders dans la même boucle de dessin. Ce n’est pas possible, et je ne l’ai découvert qu’une fois un premier shader fonctionnel, en voulant en activer un second.
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
Heureusement, il existe une solution à ce problème : le rendu multi-pass. En gros, on prend le résultat dessiné sur le canvas, on le réécrit sur un autre canvas, puis on dessine le shader. On peut ensuite répéter l’opération pour plusieurs shaders.
La fonction ci-dessous fonctionne bien pour l’instant, même si je dois encore mieux la tester.
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
Shader Color Dodge
Dans le design sous Photoshop et Aseprite, j’utilise le filtre color dodge.

Color Dodge dans Photoshop
Dans le jeu et dans l’éditeur, je devais donc le reproduire d’une manière ou d’une autre.

Sélecteur de shaders de l’éditeur
Je n’utilise presque pas d’IA pour programmer pendant le développement du jeu. Mais parfois, je sais qu’il s’agit d’une petite partie que je ne maîtrise pas encore et que je peux utiliser comme point de départ. J’ai donc demandé de créer un shader glsl appelé ‘Color Dodge’ qui fasse la même chose que Photoshop.
Évidemment, ça n’a pas bien fonctionné, car les coordonnées que j’utilise sont différentes et j’utilise aussi le scaling et d’autres choses. Voici le fichier glsl final pour l’instant.
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;
}
Dans la classe du shader, je transmets au shader le Frameset et l’effet choisi.
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
Pour finir
J’ai réussi à reproduire le color dodge de Photoshop dans le jeu, mais je dois encore essayer d’autres choses, comme les ombres et une combinaison du système de particules pour le feu avec des effets de shader.
Je pense quand même que c’est nécessaire pour rendre le tout un peu plus beau. Heureusement, on trouve aussi des filtres glsl sur différents sites, donc je n’ai pas à tout réinventer.