Une fois, le designer a appelé le front-end et a demandé de faire une "toile d'araignée" derrière le verre embué. Mais il s'est avéré que ce n'était pas une «toile d'araignée», mais une grille hexagonale, et non derrière la vitre, mais elle est allée au loin, et le front-end n'était pas familier avec WebGL, et j'ai dû apprendre toute l'animation dans le processus de dessin. Ce front-end était
Yuri Artyukh (
akella ).

Yuri est engagé dans la composition depuis longtemps et le dimanche enregistre des flux avec l'analyse de projets réels. Il n'est pas un pro de WebGL, ne fait pas de cartes dessus, n'écrit pas dans Web assembler, mais il aime apprendre quelque chose de nouveau. À
FrontendConf RIT ++, Yuri a expliqué comment effectuer une animation de la mise en page à la livraison au client afin que tout le monde soit satisfait et apprenne WebGL en cours de route. L'histoire vient du point de vue de la première personne et comprend: Three.js, GLSL, Canvas 2D, des graphiques et un peu de mathématiques.
Toile d'araignée derrière un verre brumeux
D'une certaine manière, je me suis assis et j'ai travaillé sur un projet important. Ensuite, le designer du studio, qui aime beaucoup les effets spéciaux, appelle et demande: "Pouvez-vous faire une toile d'araignée, comme si derrière une vitre embuée?"
Ceci, bien sûr, décrit immédiatement tout le problème. Comme il s'est avéré plus tard, la "toile d'araignée" derrière le verre brumeux était la suivante.
Il s'agit d'une grille hexagonale, mais pour le concepteur, pour une raison quelconque, une "toile d'araignée". Verre brumeux - cette grille va au loin. Difficultés de communication. Imaginez à quel point il est difficile d'être introverti et de faire des animations? Mais je suis juste comme ça et c'est ce que je fais.
Ce "web" ne ressemble pas à une animation, après quoi vous pouvez rédiger un rapport sur un cas réussi, ouvrir une startup, obtenir un milliard d'investissements, avoir un tas de fans et lancer une fusée dans l'espace. De quoi s'agit-il? Une ligne brune sur fond blanc-gris, comme tracée par une souris. Plus tard, il s'est avéré qu'elle devrait aller le long des bords, mais plus à ce sujet plus tard. En général, le nom de code est «le Web derrière le verre brumeux».
Sur le site avec cette animation, il y avait plusieurs autres options pour la «toile d'araignée»: sur un fond gris en haut, sur un blanc en dessous. Il fallait le rendre interactif pour qu'il réponde au mouvement de la souris de l'utilisateur.
La première chose que les concepteurs m'ont demandé, c'est à quel point c'est difficile et combien cela coûtera. Quelques pensées m'ont traversé la tête: comment dessiner une ligne et une telle grille, comment la faire ralentir, comment cela devrait fonctionner. Je n'ai jamais rencontré cela auparavant. Mais, en tant que personne engagée dans le développement, il a répondu: "
Ça, c'est facile, nous le ferons ..."J'aime m'impliquer dans des aventures étranges, car quand je fais ça, je souffre généralement.
La souffrance vient de la croissance. Elle est inévitablement associée à la souffrance - vous ne pouvez pas être satisfait de tout, vivre heureux et en même temps vous développer professionnellement.
Three.js
J'ai immédiatement commencé à réfléchir à la manière de résoudre le problème. Comme tout était en 3D, je me suis souvenu de
Three.js . Il s'agit de la bibliothèque la plus populaire mentionnée lors de toutes les conférences. Cette bibliothèque rend WebGL plus simple, plus pratique et plus agréable que le simple WebGL natif.
Three.js a de nombreux objets prêts à l'emploi.
PlaneGeometry est le premier objet qui m'a paru parfait. Ceci est un plan primitif. La bibliothèque possède toutes sortes d'hexagones, de dodécaèdres, d'icosaèdres, de cylindres, mais il existe un plan simple à partir de nombreux triangles.
Il y a beaucoup de triangles, car j'ai besoin d'ondes détaillées - la surface doit s'inquiéter.Si vous regardez à l'intérieur de Three.js, alors en fait ce plan est un simple objet JS avec une liste de toutes les coordonnées des points.
Dans mon cas, j'ai un plan de 50 × 50 carrés, donc j'avais besoin de 2601 sommets. Pourquoi 50 × 50 = 2601? Ce sont les mathématiques de l'école. La coordonnée z = 0, parce que le plan, y = 1, parce que c'est la première rangée de sommets de 50 pièces, et x change.
Mais pourquoi ai-je besoin d'un avion, dois-je le plier d'une manière ou d'une autre? La première chose que vous pouvez faire avec un tableau est d'effectuer des opérations mathématiques sur celui-ci. Par exemple, parcourez la boucle
for each
boucle et affectez la coordonnée z à la valeur sinus de la coordonnée x.
Il s'est avéré quelque chose de similaire à une onde sinusoïdale, car la valeur de la hauteur de chaque sommet sera égale au sinus de cette valeur le long de l'axe x. Pour compliquer cela d'une façon ou d'une autre (maintenant ce sera un moment mathématique difficile, préparez-vous) - J'ajouterai du temps au sinus, et cette toile bougera, simplement parce que les mathématiques fonctionnent de cette façon. Si vous ajoutez du temps à la coordonnée x, le graphique se déplace horizontalement. Si coordonner y - se déplacera verticalement. Je m'intéressais au mouvement horizontal - je voulais que mon océan s'inquiète.
Le concepteur ne s'attendait pas à ce que je possède une sinusoïde qui rampe de gauche à droite. Il voulait que ce soit beau, comme une toile d'araignée, un océan ou tout ce qui était dans sa tête. Par conséquent, l'option avec une sinusoïde ne convenait pas. Il a fallu une sorte de hasard. L'avion n'aurait pas dû être prévisible comme une onde sinusoïdale. Mais si vous appelez au hasard pour chacun de ces pics, nous obtenons alors une imprévisibilité très élevée.
L'essence du caractère aléatoire est qu'il est aléatoire et indépendant. Chaque sommet aléatoire ne dépend en aucune façon de ses voisins. Au hasard, aucun paramètre n'est transmis, il ne se soucie pas des voisins.
Le résultat est une courbe brisée qui ne ressemble à l'océan ou au Web qu'à distance. Plus approprié comme illustration pour un film sur les "hackers" et les cambriolages informatiques.
Si vous regardez au hasard du point de vue de chaque point de l'écran, cela ressemble à du bruit blanc - beaucoup de petits points blancs et noirs. Chaque point est noir et blanc - 0 ou 1.
Mais ce dont j'ai besoin pour créer un océan de vagues devrait ressembler à ceci.
Il ressemble au brouillard, aux nuages et aux montagnes.Il existe des fonctions aléatoires qui renvoient de telles images. Ils sont appelés bruits ou
bruit : bruit simplex, bruit Perlin. Perlin au nom du bruit est le nom du créateur de l'algorithme de bruit de gradient, qui renvoie un beau hasard. Il l'a créé en travaillant sur les effets spéciaux de la première partie du film "Tron". Cet algorithme mathématique existait auparavant, mais maintenant il est activement utilisé dans les films et les jeux.
Lorsque des cartes aléatoires sont générées dans "Heroes of Might and Magic III" (pour ceux de plus de 30 ans) ou dans des stratégies., Vous pouvez généralement voir quelque chose de similaire. C'est toujours la même fonction qui renvoie ces bruits.
Il y a tout un mouvement «Art génératif». Les participants génèrent des illustrations, des paysages, en utilisant la fonction de bruit. Par exemple, dans l'image ci-dessous, un paysage pseudo-naturel de l'un des artistes. On ne sait pas immédiatement s'il s'agit de mathématiques ou de la topographie d'une montagne. La tâche de l'art génératif est précisément de générer mathématiquement un paysage qui ne se distingue pas du présent.
Il s'est avéré que, contrairement au hasard, cette fonction prend des paramètres, car les points doivent dépendre des voisins les plus proches d'eux. Si vous utilisez la fonction de bruit au lieu du caractère aléatoire habituel, vous obtenez quelque chose comme ça.
Le noir et le blanc sont tout simplement la hauteur: 0 sont des vallées noires, 1 sont des pics blancs. Il transforme une surface ondulée.Cette fonction est présente sur tous les PL car elle n'est qu'un algorithme - sinus, cosinus, multiplication.
Je peux faire la distorsion de la même manière en parcourant tous les sommets de mon objet PlaneGeometry, en affectant chaque valeur à la fonction de bruit:
geometry.vertices.forEach(v => { vz = noise(vx, vy, time); });
La fonction ne prend que 30 à 40 lignes, mais est mathématiquement complexe.
Il existe des fonctions de bruit de toutes dimensions: unidimensionnelles, bidimensionnelles, tridimensionnelles. Dans mon cas, il s'agit d'un bruit tridimensionnel, car trois paramètres lui sont transmis. En plus des coordonnées spatiales des plans x et y, je transmets le temps - la surface se tordra constamment, changera de position.
Three.js! == GPU
Quand j'ai commencé l'algorithme, les vagues ont commencé à bouger. Quand je fais quelque chose pour le Web, je regarde toujours dans le profileur, et maintenant je le regarde aussi. Voilà à quoi ressemblaient les vagues.
Sur l'écran, un cadre est dessiné par le navigateur. Les cadres sont indiqués par des lignes verticales en pointillés gris. A l'intérieur du cadre, 2/3 du temps est occupé par l'exécution de la fonction bruit. Lorsque vous animez quelque chose sur le Web, vous utilisez au mieux le cadre d'animation de demande, qui s'exécute toutes les 16 ms. La trame toutes les 16 ms compte la fonction de bruit pour 2600 sommets. Pour chaque sommet, un mouvement de haut en bas et une hauteur sont considérés. Sur chaque trame suivante, les valeurs sont recalculées, car la surface doit vivre dans le temps.
Il s'est avéré que la fonction de bruit, qui a tourné 2600 fois, prend déjà 2/3 du cadre sur mon ordinateur. Et ce n'est pas tout le cadre. Lors du développement d'animations, c'est déjà un drapeau rouge.
Aucune animation ne doit occuper plus de la moitié du cadre.
Si plus, il y a un risque élevé de perdre le cadre lors de toute interaction, tout bouton, tout survol.
Par conséquent, c'était un drapeau rouge difficile. J'ai réalisé que Three.js n'est pas nécessairement WebGL. Malgré le fait que j'ai semblé utiliser Three.js, j'ai tout dessiné en 3D, il a été rendu en WebGL, je n'ai pas obtenu de performances fantastiques de WebGL. Je n'ai que 2 600 sommets - ce n'est pas suffisant pour WebGL. Par exemple, sur chaque carte, il y a des milliers d'objets, chacun composé de dizaines de triangles. Estimez l'échelle: des centaines de milliers sont normales, mais il n'y a que 2600 pics.
Vertex Shader
Après un problème avec les cadres, j'ai découvert qu'il y avait des shaders. Il en existe seulement deux types:
- Vertex Shader;
- Shader de fragment.
Je m'intéressais au vertex shader - Vertex Shader. Si nous réécrivons l'animation dessus, cela ressemble à ceci:
position.z = noise( vec3(position.x, position.y, time) );
Position.z
- coordonnées z du composant de chaque point avec ses propres types de données.
vec3
indique qu'il y aura trois paramètres.
Il n'y a pas de boucle dans le shader.
Avant cela, dans le script, je mettais un
for each
boucle, et pour chaque sommet, les calculs se faisaient en boucle. La différence entre les shaders et les non-shaders est l'absence de cycle.
Shader - c'est le cycle.
Il est exécuté en parallèle pour tous les sommets à la fois. C'est sa signification principale, sa mission, sa puce et sa mission.
Le GPU sur la carte vidéo a un cœur plus grand, contrairement au processeur principal. Sur le processeur, il y en a beaucoup moins, mais il est capable d'effectuer des calculs universels plus rapidement. Des calculs très simples sont disponibles sur la carte vidéo, mais il existe de nombreux cœurs, ce qui vous permet de paralléliser de nombreux calculs. C'est ce qui se produit généralement dans les shaders. La signification du vertex shader est que le bruit sera calculé en parallèle pour 2600 sommets dans le shader sur la carte vidéo.
Si vous regardez le profileur, l'apparence de l'animation ne changera pas, mais elle ressemblera à ceci.
Rien n'est exécuté sur la CPU . Bien sûr, un autre fil sur le GPU a été ajouté ci-dessous. Il existe également des threads sur le GPU, le CPU, les Web-travailleurs, mais ces calculs seront déjà effectués dans un thread séparé sur la carte vidéo.
Bien sûr, ce n'est pas gratuit. La carte vidéo au travail est plus chauffée que le processeur principal. Par conséquent, souvent lorsque vous visitez des sites avec des animations similaires, les fans commencent à travailler pour vous. En effet, lorsque la carte vidéo est allumée, elle nécessite un refroidissement, contrairement au reste du temps. Sur les appareils mobiles, cela est plus important que sur les ordinateurs de bureau - les téléphones portables s'épuisent simplement plus rapidement. Mais en même temps, vous obtenez un gain de performances assez radical.
Le résultat est une telle surface - c'est le bruit perlin habituel. Si vous le démarrez et ne changez que l'heure, des vagues fraîches sont obtenues.Mais ce n'est pas tout. Je devais encore "toiles d'araignée" - une grille hexagonale à la surface. Ayant de l'expérience en mise en page, la manière la plus simple et la plus évidente est de sélectionner un fragment qui peut être répété. Fait intéressant, pour une grille hexagonale, elle n'est pas carrée, mais rectangulaire. Si vous répétez le motif sous forme de rectangle, vous obtenez une grille. La bibliothèque Three.js vous permet de superposer png et de ne pas apprendre tout WebGL avant cela. J'ai découpé png et l'ai mis à la surface, il s'est avéré quelque chose comme ça.
À première vue, ce dont vous avez besoin! Mais seulement au début. Cela ne me convenait pas, car l'animation était requise pour le site de crypto-monnaie - tout devrait être «cher, riche».
Lorsque vous utilisez des textures png et qu'elles sont proches de l'appareil photo, vous pouvez voir que les bords de l'élément le plus proche sont flous. Il n'y a aucun sentiment que l'image est claire. Le png semble s'être étendu dans le navigateur. Le problème est que dans WebGL, il n'y a aucun moyen d'utiliser des textures vectorielles au sens plein du terme. J'ai donc pleuré, puis j'ai lu sur Internet que
GLSL résout ce problème.
GLSL est un langage de type C dans lequel les shaders sont écrits. Tout le monde a peur de l'utiliser, car ce sont des shaders, WebGL - rien n'est clair! Mais j'ai découvert qu'il était possible de faire des images claires dessus et je me suis tourné vers le deuxième type de shader.
Shader de fragment
Ce shader fait la même chose que le vertex. Mais, si le sommet construit une surface polyligne, effectuant des calculs pour chaque sommet, le shader Fragment calcule la couleur pour chaque pixel de la surface.
La fonction de
fragment shader – step(a,b)
plus élémentaire
fragment shader – step(a,b)
. Il ne renvoie que 0 et 1:
- si a> b, alors 0;
- si a <b, alors 1.
J'ai fait une pseudo-implémentation dans JS pour montrer à quel point cette fonction est simple.
function step(a, b) { if (a < b) return 0 else return 1 }
Lorsque vous travaillez dans WebGL, généralement sur n'importe quel objet, il existe un système de coordonnées. S'il s'agit d'un objet carré, le système de coordonnées est primitif: points (0,0), (0,1), (1,0), (1,1).
Un Fragment Shader est exécuté pour chaque pixel. Si Vertex Shader est de 2600 fois par image, alors Fragment Shader est exécuté autant de fois qu'il y a de pixels. Peut-être un million de fois pour chaque image, si la surface est de 1000 × 1000 px. Cela semble effrayant, mais tout simplement parce que peu de gens connaissent les ressources des cartes vidéo à notre époque.
Si vous utilisez la fonction step (a, b) avec les coordonnées de ces pixels, vous pouvez exécuter la fonction step avec le paramètre 0.4 et passer la coordonnée x de chaque pixel à chaque point.
Il s'avère que tout ce qui est inférieur à 0,4 sera 0, tout ce qui est supérieur à 1. Dans WebGL, les nombres et les couleurs sont une seule et même chose. Chaque couleur est un numéro. Blanc - 1, noir - 0. Il y en a trois en RVB, mais c'est toujours 0.0.0 et 1.1.1.

Si nous exécutons cette étape de fonction plus compliquée, nous obtenons du blanc sur la gauche. Cette fonction sera exécutée pour chaque point de l'écran et considérera qu'il s'agit de 0 ou 1. C'est normal, ne vous en faites pas.
Si vous multipliez ces deux expressions, vous obtenez une bande blanche verticale. Si vous faites de même sur un axe différent, vous pouvez dessiner un carré blanc:
Ce devrait être le point culminant - nous avons dessiné un carré blanc!En utilisant des combinaisons d'une seule fonction, vous pouvez dessiner tout ce que vous voulez.
Si vous vous souvenez, les éléments du motif étaient inclinés. Si vous faites une surface inclinée, qui se compose uniquement de couleurs noir et blanc, elle sera nervurée et non lissée. Les côtes attirent votre attention - c'est moche. Pour rendre la surface lisse pour l'œil, non seulement des pixels noirs et blancs sont nécessaires, mais également des demi-teintes grises.
Smoothstep
Les shaders ont une fonction lisse. Il fait la même chose que step, mais interpole entre 0 et 1 pour qu'il y ait un gradient.
De gauche à droite, après compression maximale.Si vous compressez cette fonction autant que possible, vous obtenez une ligne à gradient minimal. C'est exactement ce dont vous avez besoin pour générer une ligne parfaitement lisse à n'importe quel angle dans le shader Fragment.
J'ai donc pu faire un carré blanc avec des bords lisses. S'il y a un carré blanc, vous pouvez faire 3 carrés blancs.
Les carrés peuvent être tournés, utilisez les fonctions sinus et cosinus.Ensuite, j'ai dû utiliser du papier et une feuille pour briser mon modèle.
Capture d'écran de la production.Là, tout est lié à 23 degrés de multiplicité différents, il n'a donc pas été très difficile de calculer les coordonnées de tous ces points. Et puis vous pouvez obtenir un tel modèle.
J'ai dessiné un fragment et je l'ai répété plusieurs fois. Vous pouvez clairement voir le mode de débogage, où le motif se répète. Tous les fragments sont exécutés en utilisant les fonctions paramétriques
step
et
smoothstep
. Cela signifie qu'en créant un motif pour incliner l'avion, vous pouvez générer un nombre infini de ces motifs. Si à l'intérieur du fragment, nous modifions l'épaisseur du trait ou la taille des hexagones, nous obtenons alors de nombreux autres motifs.
J'ai tordu les paramètres et trouvé un nombre infini de motifs. C’est comme «l’art génératif» - ce qui a été fait n’est pas clair, mais joliment.
SDF
Ensuite, j'ai découvert qu'il existe également
des champs de distance signés - générant des images avec une carte de distance . SDF est utilisé dans les cartes ou dans les jeux informatiques pour dessiner des textes et des objets, car il est optimal. Dans WebGL, il est difficile de dessiner du texte d'une manière différente, particulièrement lisse et délimitée.
Il s'agit d'un format mathématique difficile à utiliser en dehors de WebGL. L'idée est simple, mais élégante et donne un bel effet.
Si nous voulons dessiner une étoile claire, alors pour cela, nous devons enregistrer l'image sur la droite - c'est l'image générée de l'image. Il existe déjà un algorithme existant qui transforme toute image claire en une image floue. Après cela, il peut être utilisé pour générer une version claire, mais en même temps, nous n'obtenons pas une image, mais plusieurs. À partir d'une image claire de la même taille, vous pouvez générer la même chose, mais en plus grand. Ce sera avec des erreurs, mais l'approche est mathématiquement intéressante.
Par exemple, si vous prenez une photo d'une taille de 128 × 128 px, alors à partir d'une photo de petite taille, vous pouvez obtenir une image claire plusieurs fois plus grande que la source. C'est l'une des raisons pour lesquelles ils utilisent SDF - une police floue pèse souvent moins que dans un format vectoriel optimisé.
Bien sûr, il y a une limitation. Il est impossible d'augmenter les lettres à 1000 px, même 100 px auront l'air moche. Mais à quelle fréquence des polices de cette taille sont-elles nécessaires?
Fragment shader, dessin de rectangles, propagation - à l'aide de ces perturbations, j'ai finalement réussi à trouver la surface souhaitée.
De nouvelles conditions
Elle était comme il se doit: se tortillant, tous les éléments étaient clairs. Tout était comme je le voulais, mais il s'est avéré qu'il y avait plus:
- Et laissez-le bouger avec la souris et un nouveau chemin est en train d'être tracé. Et les nids d'abeilles sont mis en valeur!Il a été supposé que lorsque l'utilisateur déplace la souris, il ouvre métaphoriquement son chemin épineux le long de la "toile d'araignée" cassée en utilisant le service.
Ce n'est pas difficile de décrire la tâche avec des mots, mais comment la mettre en œuvre? La première chose que j'ai pensé - puisque j'ai une grille hexagonale, elle a probablement déjà été étudiée. Puis je suis tombé sur un article intéressant «
Référence de grille hexagonale et guide d'implémentation ». L'auteur y a collecté des documents pendant 20 ans. Il est cool sans aucun doute, et l'article est divin pour ceux qui aiment les algorithmes et les mathématiques. Il contient de nombreuses données intéressantes sur les maillages hexagonaux.
L'article est long, mais il contient des approches mathématiques intéressantes: comment construire un système de coordonnées sur une grille hexagonale, comment numéroter ces hexagones, puis les référencer là où il est utilisé. Il s'avère que cela a toujours été sous mes yeux, car dans les vieux jeux informatiques, des filets hexagonaux sont utilisés partout.
Si vous êtes déjà réglé sur une case hexagonale - regardez le château. Sur d'autres textures, une grille hexagonale est également devinée.
Dans "Civilisation", tout est généralement évident.Il était également intéressant de savoir que si vous faites une coupe transversale le long de la diagonale d'un cube en trois dimensions, qui se compose de nombreux petits cubes, alors, d'une part, ce sont des cubes, et de l'autre, des hexagones réguliers.
La section transversale d'un cube en trois dimensions donne une grille hexagonale en deux dimensions. C'était amusant d'apprendre que les cubes en trois dimensions sont en quelque sorte connectés avec des hexagones en deux dimensions.
Dans l'article, y compris, il y avait un algorithme pour trouver le chemin le long de la grille hexagonale. J'ai dû chercher un moyen de faire de la hauteur à travers la souris.
Les algorithmes de recherche de chemin sont complexes et simples. Le plus primitif consiste à tracer une ligne entre les points et à voir dans quels hexagones cette ligne tombe. Il s'avère donc que dans le passé, les unités allaient du point A au point B.
J'avais besoin de quelque chose comme ça.Mais ce n'est pas ce dont j'ai besoin. Ici, le chemin est tracé le long des zones d'hexagones, et j'ai besoin le long des bords. J'ai dû résoudre le problème différemment.
Canvas2d
Il y a peut-être de meilleures façons, mais la mienne est plus intéressante. Au début, je viens de dessiner
Canvas2D pour mon débogage - étape # 1.

Avant cela, il y avait WebGL, Three.js, shaders, et c'est juste Canvas2D! J'ai dessiné tous les points de la grille hexagonale dessus. Si vous regardez attentivement, ce sont les mêmes hexagones. Ensuite, je me suis souvenu des graphiques qui stockent des informations sur la façon dont les points sont connectés les uns aux autres, et j'ai connecté chaque point à trois voisins et j'ai obtenu un graphique - étape numéro 2. Pour cela, j'ai utilisé de
beaux graphiques Open Source.
Un graphique est simplement un ensemble de points et d'informations sur la façon dont ils sont connectés. Ils sont bien étudiés, il existe de nombreux bons algorithmes pour tous les goûts pour trouver le chemin du point A au point B à l'intérieur du graphique. Toutes les cartes utilisent ce type d'algorithmes.
Cela ressemble à ceci.
graph = createGraph( ); graph.addNode(..);
Nous construisons un graphique, ajoutons 1000 points et toutes les connexions entre eux, Ensuite, nous passons l'
id
chaque point - le premier est connecté au troisième, le troisième au cinquième. Pas besoin d'inventer quoi que ce soit, il existe un algorithme optimisé avec une fonction toute faite qui vous permettra de trouver ce chemin.
Cet algorithme fonctionne moins qu'une trame. Bien sûr, cela prend une sorte de ressource, mais vous n'avez pas besoin de l'exécuter toutes les 16 ms, mais uniquement lorsque le chemin change.
J'ai donc pu construire cet itinéraire à l'étape 3. Dans Canvas2D, il a commencé à ressembler à ceci: le chemin le plus court du point A au point B - tout, comme dans la vie. À première vue, il semble que ce ne soit pas le chemin le plus court, mais il s'avère qu'il y a beaucoup de chemins les plus courts du point A au point B le long de la grille hexagonale.
Dans WebGL, toutes les images sont des nombres. Là, vous pouvez transférer des textures dans le shader, par exemple, j'ai essayé de transférer des png. Il n'y a aucune différence pour le navigateur - il est passé png ou Canvas2D. Pour le navigateur Canvas2D, c'est la même chose que l'image finale, bitmap. Par conséquent, j'ai d'abord dessiné cette image sous la forme d'un serpent. № 4.
Canvas2D . Canvas2D , — . , . , Canvas2D 3D, , .
, , WebGL — , «, », .
, , Canvas2D, , , . Canvas2D , WebGL, .
: , , ., --. , , «» .
?
: « ? ?» , : «, !». , . .
, WebGL. , . , WebGL. 2 — , .
, , . , .

, . , , , 3D — .
, . , , .
FrontendConf . , Canvas , RxJS JSX React .
30 . , , .