Sous le capot de Graveyard Keeper: comment les effets graphiques sont mis en œuvre

Bonjour à tous! Depuis 4 ans, je n'ai pas écrit en Habr. Ma dernière série de messages portait sur divers outils et astuces que nous avons utilisés sur notre dernier jeu (en le développant sur Unity). Depuis lors, nous avons sorti avec succès le jeu et en avons également sorti un nouveau. Alors maintenant, vous pouvez expirer un peu et écrire de nouveaux articles qui peuvent être utiles à quelqu'un.


Aujourd'hui, je veux parler des astuces graphiques et des astuces que nous avons utilisées pour créer l'image que vous voyez dans le GIF ci-dessus.

Nous sommes très sensibles aux visuels de nos jeux et avons donc investi beaucoup de temps et d'efforts dans divers effets et autres goodies qui rendraient notre pixel art aussi attrayant que possible. Peut-être que quelqu'un trouvera quelque chose d'utile pour lui-même.

Pour commencer, je vais brièvement énumérer ce que l'image va être dans notre jeu:

  1. Lumière ambiante variable - un changement banal de l'éclairage en fonction de l'heure de la journée.
  2. Correction des couleurs LUT - est responsable de changer le ton de l'image en fonction de l'heure de la journée (ou du type de zone).
  3. Sources lumineuses dynamiques - torches, poêles, lampes.
  4. Cartes normales - chargées de donner du volume aux objets, en particulier lors du déplacement de sources lumineuses.
  5. Mathématiques de la répartition de la lumière 3D - il est chargé de s'assurer que la source de lumière au centre de l'écran éclaire correctement un objet qui est plus haut, mais n'éclaire pas un objet qui est plus bas (c'est-à-dire tourné vers la caméra avec le côté éteint).
  6. Ombres - faites par des sprites, tournent et répondent à la position des sources de lumière.
  7. Simulation de la hauteur des objets - pour un affichage correct du brouillard.
  8. Autres décorateurs: pluie, vent, animations (y compris animation de shaders de feuillage et d'herbe), etc.

Maintenant plus en détail.

Lumière ambiante variable


Ici, en principe, rien de spécial. La nuit - plus sombre, pendant la journée - plus clair. La couleur de la lumière est définie par le gradient temporel. La nuit, la source de lumière ne devient pas seulement plus sombre, mais acquiert une teinte bleue.

Cela ressemble à ceci:


Correction des couleurs LUT


LUT (Look-up table) - tables d'échange de couleurs. En gros, il s'agit d'un tableau RVB tridimensionnel où, à chaque nœud, il y a une valeur de couleur, qui doit être remplacée par celle correspondante. Autrement dit, si un point rouge est situé aux coordonnées (1, 1, 1), cela signifie que toute la couleur blanche de l'image sera remplacée par du rouge. Si les coordonnées (1, 1, 1) sont blanches (R = 1, G = 1, B = 1), alors il n'y a pas de changement. Par conséquent, la LUT sans changement a une couleur pour chaque coordonnée correspondant à ces mêmes coordonnées. C'est-à-dire au point (0,4, 0,5, 0,8) est la couleur (R = 0,4, G = 0,5, B = 0,8).

Eh bien, il convient de noter que pour plus de commodité, ils présentent une texture 3D en deux dimensions. Par exemple, voici à quoi ressemble la LUT «par défaut» (sans changer le rendu des couleurs):



Il est mis en œuvre de manière élémentaire, il fonctionne rapidement et facilement.

Il est également très facile à configurer - vous donnez à l’artiste n’importe quelle image du jeu et vous dites «tonalité des couleurs pour que ce soit comme le soir». Après cela, appliquez toutes les couches de correction des couleurs à la LUT par défaut et obtenez la LUT du soir.

Dans notre cas, l'artiste est resté un peu coincé et a créé jusqu'à 10 LUT différentes pour différentes heures de la journée (nuit, crépuscule, soirée, etc.). Voici à quoi ressemble leur configuration:


Par conséquent, selon l'heure de la journée, le même emplacement est différent:



Ici, la transparence des sprites lumineux des fenêtres change également en fonction de l'heure de la journée.

Sources lumineuses dynamiques et cartes normales


Les sources lumineuses sont utilisées de manière absolument ordinaire, de Unity. De plus, des cartes normales sont dessinées pour chaque image-objet, ce qui vous permet d'avoir une idée du volume.


De telles normales sont dessinées tout simplement. L'artiste peint grossièrement une lumière avec un pinceau de 4 côtés:


Et puis ce script va à la carte normale:


Si vous cherchez un shader (et un logiciel) qui fait cela, vous pouvez regarder dans la direction de Sprite Lamp.

Simulation de lumière 3D


C'est un peu plus compliqué. Vous ne pouvez pas simplement prendre et mettre en évidence les sprites. Nous devons nous demander si le sprite est "derrière" la source de lumière ou "devant".

Faites attention à cette image:


Les deux arbres sont à la même distance de la source lumineuse, cependant, l'arbre distant est illuminé, et l'arbre le plus proche ne l'est pas (car sa partie non éclairée est tournée vers la caméra).

J'ai résolu ce problème tout simplement. Le shader calcule la distance le long de l'axe vertical y entre la source de lumière et le sprite. Et s'il est positif (la source de lumière avant le sprite), alors nous illuminons le sprite comme d'habitude, mais s'il est négatif (le sprite bloque la source de lumière), mais l'intensité lumineuse décroît beaucoup à distance avec un très grand coefficient. C'est précisément le coefficient qui a été fait, et pas seulement «pas d'éclairage», de sorte que lorsque la source lumineuse se déplace et apparaît soudainement derrière le sprite, le sprite ne devient pas instantanément noir, mais progressivement. Mais encore assez rapidement.


Ombres


Les ombres sont faites par des sprites tournant autour d'un point. J'ai essayé de leur ajouter plus de compression (skew), mais cela s'est avéré inutile.

Au total, chaque objet peut avoir un maximum de 4 ombres. L'une vient du Soleil et trois proviennent de sources lumineuses dynamiques. L'image ci-dessous montre le principe:



La tâche «trouver les 3 prochaines sources de lumière et calculer la distance / l'angle des ombres par rapport à elles» est résolue par un script qui tourne dans Update. Oui, ça ne marche pas très vite, car il faut faire beaucoup de maths. Si j'écrivais maintenant, j'utiliserais les nouveaux systèmes de travaux parallèles dans Unity. Mais ce n'était pas encore le cas, j'ai donc optimisé autant que possible les scripts ordinaires.

La seule chose qui compte, c'est que j'ai fait en sorte que la rotation du sprite ne se transforme pas, mais à l'intérieur du vertex shader. C'est-à-dire la rotation ne bouge pas. C'est juste qu'un paramètre est défini dans le sprite (j'ai utilisé la couleur pour cela, car tout de même, toutes les ombres sont noires), et le shader est déjà responsable de la rotation du sprite. C'est plus rapide car ne pas avoir à tirer la géométrie dans Unity.

Un autre inconvénient de cette approche est que les ombres doivent être configurées (et parfois peintes) individuellement pour chaque objet. Certes, nous avons réussi, probablement, avec une dizaine de sprites différents plus ou moins universels (fins, épais, ovales, etc.).

Le deuxième inconvénient est qu'il est parfois difficile de faire de l'ombre pour un objet dont le point de contact avec la terre est très allongé. Par exemple, regardez l'ombre de la clôture:


Pas parfait. Voici à quoi cela ressemble si vous rendez le sprite de la clôture lui-même translucide:


Ici, cependant, il convient de noter que le sprite est toujours très déformé verticalement (le sprite d'ombre d'origine ressemble presque à un cercle). C'est pourquoi son virage ressemble moins à un virage qu'à une distorsion.

Simulations de brouillard et de hauteur


Il y a du brouillard dans le jeu. Il ressemble à ceci (ci-dessus est la version normale, ci-dessous est un brouillard extrême à 100% pour démontrer l'effet).



Comme vous pouvez le voir, les sommets des maisons et des arbres dépassent du brouillard. En fait, obtenir cet effet était assez simple. Le brouillard se compose de nombreux nuages ​​horizontaux répartis sur toute la profondeur de la scène. Et en conséquence, il s'avère que la partie supérieure de tous les sprites est bloquée par moins de sprites de brouillard:



Le vent


Le vent du pixel art est une autre histoire. Il n'y a pas beaucoup d'options. Soit animer avec vos mains (ce qui est presque impossible avec notre quantité d'art), ou écrire un shader déformant, mais alors vous devez parfois subir de vilaines distorsions. Bien sûr, vous ne pouvez pas animer du tout, mais l'image semble inanimée.

Nous avons choisi l'option de distorsion en utilisant le shader. Cela ressemble à ceci:


Si vous appliquez ce shader à une texture en damier, il devient clair ce qui se passe:


Il convient également de noter que nous n'animons pas toute la couronne, mais uniquement des feuilles individuelles:


Nous secouons également le blé dans le vent, mais tout est simple - le vertex shader déforme les coordonnées x, en tenant compte de la composante y. Plus le point est élevé, plus le décalage est fort. Ceci est fait de sorte que seul le haut chancelle, mais pas la racine. De plus - la phase d'oscillation change des coordonnées x / y de sorte que différents sprites sur l'écran oscillent de manière aléatoire.


Le même shader est également utilisé pour créer l'effet de balancer le blé et l'herbe lorsqu'un joueur les traverse.


C'est probablement tout pour l'instant. Je n'ai pas intentionnellement abordé la question de la construction de la scène et de sa géométrie, car c'est matière pour un article séparé. Pour le reste, il a évoqué les principales solutions utilisées en développement.

PS: Si quelqu'un est intéressé par certains aspects techniques, écrivez dans les commentaires. Je vais peut-être le dire dans un article séparé. À moins, bien sûr, que cela soit nécessaire.

PPS: J'en profite pour dire que maintenant nous voulons trouver plusieurs personnes compétentes dans l'équipe (programmeur, PM, KM, artiste). Les détails sont sur le site Web du studio. J'espère que cette phrase n'a pas violé les règles.

Source: https://habr.com/ru/post/fr425989/


All Articles