Comment transférer un shader d'un moteur de jeu vers Substance Painter

Je m'appelle Taras Uleisky, je suis un artiste technique au Plarium Kharkiv. Pour optimiser les graphismes de notre Survival RPG sur les appareils mobiles, nous avons utilisé nos shaders personnalisés. Ils impliquent l'utilisation de textures et de cartes uniques qui ne sont pas similaires aux textures et aux cartes d'autres méthodes d'ombrage populaires. Par conséquent, les artistes 3D ne savent pas très bien comment créer ces textures pour les éléments du jeu. Pour voir immédiatement à quoi ressemblera le modèle 3D dans le moteur de jeu au stade de la texturation, j'ai déplacé le shader vers Substance Painter. Il n'y a pratiquement pas de matériel API dans Substance Painter pour le moment, j'ai étudié ce sujet moi-même, j'ai donc décidé de partager mes propres idées.



Shader Unity


Le jeu utilise un ombrage matcap. En plus de la texture diff habituelle, deux textures Matcap pré-créées sont également transférées vers le shader. Ils sont interpolés et flous à l'aide de deux masques, respectivement. En conséquence, la texture Matcap est multipliée par des reflets diffus et faux et des reflets peuvent être vus sur le matériau.



L'exemple ci-dessous montre comment Matcap est implémenté dans un graphique shader. Dans ce cas, deux textures Matcap sont regroupées en une seule et divisées en canaux. C'est-à-dire, métal et non-métal dans les canaux R et G, respectivement.



Deux matcaps sont interpolés pour un exemple par checker.



Le résultat est une certaine analogie avec le métal et le non-métal comme dans l'ombrage PBR.

Nous voulions ajouter de la rugosité et de la saleté aux matériaux, pour créer une sorte d'analogue de rugosité dans l'ombrage PBR. Pour cela, nous avons utilisé la méthode de texturation.mip-mapping . Une séquence de textures crée la soi-disant pyramide MIP avec une résolution maximale de 1x1. Par exemple: 1 × 1, 2 × 2, 4 × 4, 8 × 8, 16 × 16, 32 × 32, 64 × 64, 128 × 128. Chacune de ces textures est appelée niveau MIP. Pour implémenter les rayures dans le shader pixel par pixel, en fonction du masque, vous devez sélectionner le niveau MIP requis. Cela se passe ainsi: là où le pixel sur le masque est noir, le niveau MIP maximum est sélectionné sur Matcap, et là où la couleur du pixel est blanc, le niveau MIP est 0.





En conséquence, le shader permet de simuler des reflets et des reflets, d'ajouter de la rugosité légère et des éraflures. Et tout cela sans l'utilisation de Cubemap, sans calculs d'éclairage complexes et autres techniques qui réduisent considérablement les performances des appareils mobiles.



Configuration de Substance Painter pour créer un shader


Tous les shaders disponibles dans Substance Painter sont écrits en GLSL.
Plus précisément, pour écrire un shader pour Substance Painter, j'utilise le code VS gratuit. Pour la coloration syntaxique, il est préférable d'utiliser la prise en charge des langages Shader pour l'extension VS Code.



Il y a très peu de matériel sur l'API dans Substance Painter, donc la documentation standard trouvée dans l' API Aide / Documentation / Shader est inestimable.



La deuxième chose qui aidera à écrire le shader est les shaders standard de Substance Painter. Pour les trouver, allez à ... / Allegorithmic / SubstancePainter / resources / clayette / allegorithmic / shaders.

Essayons d'écrire le shader non éclairé le plus simple qui affichera la couleur de base. Tout d'abord, créez un fichier texte avec l'extension .glsl et écrivez un shader si simple. Peut-être, alors que rien n'est clair, je parlerai plus en détail de la structure du shader dans Substance Painter.



Créez un nouveau projet et faites glisser le shader sur votre coque. Dans la liste déroulante Importer vos ressources vers , sélectionnez le projet 'nom_projet' .



Cela est nécessaire pour que toutes les modifications puissent être mises à jour.

Maintenant, allez dans Window / Views / Shader Settings et sélectionnez votre nouveau shader dans la fenêtre qui apparaît. Vous pouvez utiliser la recherche.



Si vous voyez que tout le modèle est blanc et que vous pouvez dessiner la couleur de base, vous avez tout fait correctement. Vous pouvez maintenant enregistrer le projet et passer à la section suivante.



Si le modèle est rose, il y a très probablement une erreur dans le shader - une notification à ce sujet sera dans la console.

Création d'un shader dans Substance Painter


Considérez la structure d'un shader en utilisant le shader non éclairé décrit précédemment comme exemple.



La méthode de teinte est la partie de base du shader; elle ne fonctionnera pas sans elle. Tout ce qui sera décrit à l'intérieur peut être affiché sur un modèle 3D. Tous les calculs finaux sont générés via la fonction diffuseShadingOutput () .

Les lignes 3 et 4 créent respectivement un paramètre et une variable. Le paramètre associe le canal de couleur de base à la variable dans laquelle la texture peinte sera stockée. Tous les paramètres sont énoncés dans l' aide , dans le cas de la couleur de base, tout doit être énoncé comme dans l'exemple. La ligne 8 présente la texture dans les coordonnées uv du modèle 3D. Je note que pour la texture avec la couleur de base, le système Sparse Virtual Textures est utilisé , car la bibliothèque est connectée à la première lignelib-sparce.glsl .

Vous pouvez trouver de nombreuses implémentations de Matcap, mais son principal point est que les normales du modèle sont dirigées vers la caméra et la texture est tournée le long des axes x et y. Pour faire pivoter la normale vers la caméra, nous avons besoin d'une matrice de vue ou du type de matrice . Vous pouvez en trouver un dans le certificat mentionné ci-dessus.



Ce sont donc les mêmes noms déclarés que dans le cas de la couleur de base. Maintenant, nous devons obtenir les normales du modèle 3D.


Zéro comme quatrième élément du vecteur est requis.

La multiplication d'une matrice de vue avec un vecteur normal étendra la normale à la caméra.



N'oubliez pas que lors de la multiplication des matrices, l'ordre des facteurs est important. Si vous modifiez l'ordre de multiplication, les résultats seront différents.

Vous pouvez maintenant créer des coordonnées uv à partir de viewNormal .



Il est temps de brancher la texture du matcap.



Dans ce cas, le paramètre créera un champ de texture dans l'interface du shader, et si le projet a une texture avec le nom "Matcap_mip", Substance Painter le tirera automatiquement vers le haut.



Vérifions ce qui s'est passé.



Ici, la texture de Matcap est développée dans de nouvelles coordonnées et multipliée par la couleur de base à la sortie. Je veux faire attention au fait que la texture Matcap est développée par la fonction texture () et la couleur de base - par la fonction textureSparse () . En effet, les textures spécifiées via l'interface du shader ne peuvent pas être de type SamplerSparse .

Le résultat devrait ressembler à ceci:



Ajoutez maintenant un masque qui mélangera deux Matcap. Pour plus de commodité, ajoutez deux Matcap'a dans une texture, en les divisant en canaux. En conséquence, deux textures Matcap seront dans les canaux R et G, respectivement.

Il en résultera quelque chose comme ceci:



Commençons par ajouter un masque au shader. Le principe est similaire à l'ajout de la couleur de base.



Il suffit de remplacer la valeur de la couleur de base par user0 dans le paramètre.

Maintenant, obtenez la valeur du masque dans le pixel shader et mélangez les textures du matcap.



Ici, seul le canal R est utilisé dans le masque, car il sera en noir et blanc. Les deux canaux matcap sont mixés à l'aide de la fonction mix () , un analogue de lerp dans Unity.

Mettons à jour le shader et ajoutons des canaux personnalisés dans l'interface. Pour ce faire, allez dans Paramètres de fenêtre / vues / jeu de textures, dans la fenêtre près de l'en-tête Channels, cliquez sur le plus et sélectionnez user0 dans la grande liste.



Le canal peut être appelé comme bon vous semble.

Maintenant, en dessinant sur ce canal, vous pouvez voir comment les deux textures Matcap sont mélangées.



Le shader pour Unity a également utilisé des cartes normales pour Matcap, qui ont été cuites à partir d'un modèle haute poly. Essayons de faire de même dans Substance Painter.

Pour utiliser toutes les opérations sur les normales, vous devez connecter la bibliothèque appropriée :



Maintenant, nous connectons les cartes normales. Il y en a deux dans Substance Painter: l'un est obtenu par cuisson et le second peut être dessiné.



Selon les paramètres, vous pouvez deviner que channel_normal est une carte normale, selon laquelle vous pouvez dessiner, et texture_normal- carte normale cuite. Je note également que le nom de variable texture_normal est intégré dans l'API et vous ne pouvez pas le nommer à votre discrétion.

Ensuite, déballez les cartes dans le pixel shader:



Ensuite, nous mélangeons les cartes normales et les normales qui se trouvent sur les sommets du modèle. Pour ce faire, dans la bibliothèque connectée ci-dessus, il existe une fonction normalBlend () .



Nous mélangeons d'abord les deux cartes normales, puis les normales normales. Mais peu importe dans quel ordre les mélanger.

La rotation des normales dans la direction du regard de la caméra ressemblera à ceci:



alors vous ne pouvez rien changer, tout restera le même. Cela devrait être quelque chose comme ceci:



Le mip-mapping, comme mentionné ci-dessus, dans ce cas est nécessaire pour simuler les éraflures, quelque chose comme une carte de rugosité dans l'ombrage PBR. Mais le principal problème est que la pyramide des cartes MIP n'est pas générée pour la texture, qui est transmise depuis l'interface du shader, et en conséquence la méthode textureLod () de glsl ne fonctionnera pas. On pourrait aller dans l'autre sens et charger la texture Matcap via le canal utilisateur, comme cela a été fait pour le mixage de Matcap. Mais alors la qualité de la texture diminuera considérablement et des artefacts étranges apparaîtront.

Une solution alternative consiste à créer une pyramide de cartes MIPmanuellement, dans Adobe Photoshop ou un autre éditeur similaire, puis sélectionnez le niveau MIP. La pyramide est construite tout simplement. Il faut partir de la taille de la texture d'origine - dans mon cas, c'est 256x256. Nous créons un fichier avec une taille de 384x256 (384, car 256 + 256/2) et réduisons maintenant la texture d'origine de moitié jusqu'à ce qu'elle soit d'un pixel. Toutes les versions de textures réduites sont placées à droite de la texture d'origine dans l'ordre croissant. Cela devrait ressembler à ceci:



vous pouvez maintenant commencer à écrire une fonction qui trouvera les coordonnées de chaque texture dans la pyramide en fonction de la couleur de chaque pixel sur le masque.

Le moyen le plus simple consiste à stocker les coordonnées uv qui seront calculées pour chaque texture dans un tableau. La taille du tableau sera déterminée comme log2 (hauteur). Nous avons besoin du uv d' origine , nous les ajoutons donc à l'argument de la fonction. Pour déterminer quel élément de tableau utiliser sur un pixel particulier, ajoutez un niveau à l'argument de fonction.



Maintenant, calculez uv pour la texture d'origine, c'est-à-dire recadrez ces 128 pixels de largeur supplémentaires. Pour ce faire, multipliez la coordonnée x par ⅔.



Pour utiliser le reste de la texture de la pyramide, vous devez trouver des motifs. Lorsque nous avons créé la pyramide à partir des textures, nous avons pu remarquer qu'à chaque fois la texture est réduite de moitié par rapport à la taille précédente. Autrement dit, combien de fois la taille de la texture diminue, vous pouvez déterminer en augmentant 2 à la puissance du niveau MIP .



Il s'avère que si vous sélectionnez le niveau, par exemple 4, la texture diminuera de 16 fois. Commeles coordonnées uv sont déterminées de 0 à 1, puis la taille doit être normalisée, c'est-à-dire 1 divisé par le nombre de fois que la texture a diminué, par exemple 1 divisé par 16.

En utilisant la valeur obtenue de la variable de taille, vous pouvez calculer les coordonnées pour un niveau MIP spécifique .



La taille des uv est réduite de la même manière que la taille de la texture. À la coordonnée x, la texture se déplace toujours de ⅔. Le décalage des coordonnées y peut être défini comme la somme de toutes les valeurs de la variable de taille pour chaque valeur de niveau . Autrement dit, si la valeur est level = 1 , alors uv dans la coordonnée y se déplacera de 0 pixels, et si level = 2 , alors le décalage sera la moitié de la hauteur de la texture - 128 pixels. Si niveau = 3, le décalage se traduira par 128 + 64 pixels et ainsi de suite. La somme de tous les décalages peut être obtenue en utilisant le cycle.



Maintenant, à chaque itération, la variable de décalage s'additionne et décale la texture le long de l'axe y du nombre de pixels souhaité. L'algorithme étape par étape ressemble à ceci:



La dernière étape consiste à afficher un canal qui sélectionnera le niveau souhaité à chaque pixel. Nous l'avons déjà fait, rien de nouveau.





Pour sélectionner le niveau MIP en tant que texture, multipliez simplement la longueur du tableau par la texture. Vous pouvez maintenant connecter de nouvelles coordonnées uv grâce à la méthode qui vient d'être écrite.



N'oubliez pas de traduire la texture en type int, car il s'agit désormais d'un index pour le tableau.
Ensuite, vous devez ajouter un canal personnalisé dans Substance Painter, comme nous l'avons fait auparavant. Cela devrait ressembler à ceci:





la seule chose qui manque au shader est la source de lumière et la possibilité de le faire tourner en appuyant sur shift. Tout d'abord, pour cela, nous avons besoin d'un paramètre qui produira l'angle de rotation en appuyant sur Maj et sur la matrice de rotation .





Nous plaçons au hasard la source de lumière et multiplions la position par la matrice de rotation.



Maintenant, la source lumineuse tournera autour de l'axe y en appuyant sur Maj, mais jusqu'à présent, ce n'est qu'un vecteur dans lequel la position de la source lumineuse est stockée. Il y a de bonnes chosescomment mettre en œuvre la lumière directionnelle dans un shader. Nous allons nous concentrer sur lui. Il nous reste à déterminer la direction de la lumière et l'illumination de notre modèle.



La couleur de l'ombre et la couleur de la source lumineuse seront définies par les paramètres:



Les paramètres de couleur sont interpolés en fonction de l'éclairage calculé ci-dessus.



Le résultat sera le suivant:



À l'aide de ces paramètres, vous pouvez ajuster la couleur de l'ombre et la couleur de la source de lumière via l'interface Substance Painter.



Créer et configurer un préréglage


Lorsque le shader est prêt, vous devez importer la texture Matcap et le shader avec le paramètre d'étagère.



Nous supprimons tous les canaux inutilisés et ajoutons des canaux utilisateur: le



préréglage pour l'exportation de textures ressemblera à n'importe quel autre, sauf qu'il utilisera nos canaux personnalisés.



Nous allons créer un modèle pour tous les paramètres afin que, lorsque vous créez le projet, le shader souhaité soit immédiatement attribué et tous les canaux de texture soient configurés. Pour ce faire, accédez à Fichier / SaveAsTemplate et enregistrez le modèle.



Désormais, lors de la création d'un nouveau projet, vous n'avez rien à configurer. Sélectionnez simplement le modèle souhaité.



Qu'est-ce que vous obtenez


Un artiste technique peut créer des effets spéciaux, personnaliser des scènes et optimiser les processus de rendu. Je voulais aussi que les modèles d'armures et d'armes de Stormfall: Saga of Survival soient exactement ce que les artistes 3D voulaient. Par conséquent, le modèle 3D de Substance Painter est identique à celui du moteur de jeu.


Modèle 3D dans Substance Painter avec ombrage personnalisé.


Modèle 3D dans Unity avec ombrage personnalisé.

J'espère que l'article vous a été utile et vous a inspiré de nouvelles réalisations!

All Articles