Necrown - Devlog #16 [Game Dev] : Effet de zoom et Shaders
Splix Il y a 9 mois Premium Pro - Adhésion à vie0

Salut les gamecodeurs!!!

On continue nos devlogs avec la programmation de quelques effets visuels. Pour commencer, nous allons coder rapidement un effet de zoom de caméra. Pour cela, nous allons revenir dans le fichier où nous gérions la caméra et ajouter quelques lignes dans la fonction camera.update. Dans l’input handler nous avions un objet mouse qui gérait la souris, et qui utilisait love.wheelmoved pour savoir si la molette était tournée, en utilisant la variable mouse.wheel. Nous allons ajouter à la caméra une variable s qui gérera l’échelle de l’affichage, et que l’on modifiera en fonction de mouse.wheel:

camera.s = 0.5

function camera.update()
  if mouse.wheel > 0 and camera.s < 1 then
    camera.s = camera.s + 0.05
  elseif mouse.wheel < 0 and camera.s > 0.5 then
    camera.s = camera.s - 0.05
  end
end

Maintenant, dès que nous auront des positions d’affichage dans notre code, il faudra les multiplier par camera.s, et il faudra également appliquer ce scaling dans les appels à love.graphics.draw… Voici ce que cela donne en jeu avec un zoom au minimum et un zoom au maximum:

Cet effet de zoom est plutôt simple à implémenter. Nous allons maintenant nous attaquer à des effets un peut plus compliqués, puisqu’ils vont faire intervenir des shaders… Le premier effet est un effet de flou inspiré de l’effet tilt-shift en photographie. Pour cela, il va nous falloir flouter le haut et le bas de la scène, et il faut donc commencer par créer une image de la scène. C’est pourquoi dorénavant toutes nos images seront affichées dans un canvas, et c’est sur ce canvas que nous allons appliquer le shader que voici:

local xblur = love.graphics.newShader [[
    uniform number windowHeight;
    
    vec4 effect(vec4 p_color, Image p_texture, vec2 p_textureCoords, vec2 p_screenCoords) {
      number blurHeight = windowHeight / 3;
      number blurSize = 0;
      if (p_screenCoords[1] < blurHeight)
        blurSize = 0.0025 * ((blurHeight - p_screenCoords[1]) / blurHeight);
      else if (p_screenCoords[1] > windowHeight - blurHeight)
        blurSize = 0.0025 * ((p_screenCoords[1] - (windowHeight - blurHeight)) / blurHeight);
    
      vec4 sum = vec4(0.0);
      sum += Texel(p_texture, vec2(p_textureCoords.x - 4.0 * blurSize, p_textureCoords.y)) * 0.05;
      sum += Texel(p_texture, vec2(p_textureCoords.x - 3.0 * blurSize, p_textureCoords.y)) * 0.09;
      sum += Texel(p_texture, vec2(p_textureCoords.x - 2.0 * blurSize, p_textureCoords.y)) * 0.12;
      sum += Texel(p_texture, vec2(p_textureCoords.x - blurSize, p_textureCoords.y)) * 0.15;
      sum += Texel(p_texture, vec2(p_textureCoords.x, p_textureCoords.y)) * 0.16;
      sum += Texel(p_texture, vec2(p_textureCoords.x + blurSize, p_textureCoords.y)) * 0.15;
      sum += Texel(p_texture, vec2(p_textureCoords.x + 2.0 * blurSize, p_textureCoords.y)) * 0.12;
      sum += Texel(p_texture, vec2(p_textureCoords.x + 3.0 * blurSize, p_textureCoords.y)) * 0.09;
      sum += Texel(p_texture, vec2(p_textureCoords.x + 4.0 * blurSize, p_textureCoords.y)) * 0.05;
      
      return sum * 0.5;
    }
  ]]
  
xblur:send("windowHeight", (display.h * display.s))

Pour chaque pixel de l’image, ce shader va produire une couleur en récupérant un échantillon des couleurs des pixels alentours, qu’il va additionner, avec une légère modification en fonction de la distance. Cet effet s’atténue vers le centre de l’écran, grâce à la variable blurSize, qui prend en compte la taille de la fenêtre (qu’on enregistre dans la variable windowHeight). Comme vous l’aurez surement constaté, ce shader s’appelle xblur, car il s’agit d’un premier passage horizontal. Un shader yblur effectuera exactement la même action en y, pour réaliser le second passage vertical. Voici le résultat une fois l’effet appliqué à notre canevas:

Cet effet n’est pas spectaculaire, mais il produit une sensation de profondeur plutôt agréable, et même si il floute une partie de l’image, cela ne gêne en rien le gameplay. Le deuxième effet que nous allons implémenter est un peu plus marquant visuellement: Il va générer une ondulation sur les sprites des arbres, ce qui donnera l’impression qu’ils bougent avec le vent… Voici le shader qui produit cet effet:

local wind = love.graphics.newShader [[
    uniform float time;
    
    vec4 effect(vec4 p_color, Image p_texture, vec2 p_textureCoords, vec2 p_screenCoords) {
      vec2 size = vec2(148, 244); // taille de la texture
      vec2 wave = vec2(768, 2); // [1]: hauteur de la vague, [2]: amplitude de la vague
    
      vec2 coord = p_textureCoords + vec2(cos((p_screenCoords.y / wave.x + time/ 4) 
        * 6.2831) * wave.y, 0) / size * (1.0 - p_textureCoords.y);
      
      return p_color * texture2D(p_texture, coord);
    }
  ]]

Ce shader à besoin qu’on lui envoi une valeur de temps (en secondes), puisqu’elle produit une animation. On ajoutera donc à l’objet world un timer, auquel on ajoutera le delta time à chaque update. Pour un visuel varié, on va modifier la valeur time du shader avec ce timer, mais en lui ajoutant une petite variation qui sera différente pour chaque arbre. Voici le résultat que l’on obtient avec tous nos effets activés:

Si vous comparez cette image avec celle du début de ce devlog où l’on présentait le zoom de caméra, vous constaterez à quel point ces deux shaders modifient l’aspect visuel de la scène. Je vous laisse là-dessus, et en attendant le prochain devlog…

Bon code à tous !!!

Laisser un commentaire

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.