Unity Terrane et Mesh Blending

Bonjour à tous, en juin, OTUS lancera à nouveau le cours Unity Games Developer . En prévision du début du cours, nous avons préparé une traduction de matériel intéressant sur le sujet.



Aujourd'hui, nous allons parler de la façon de mélanger un maillage de terrane (ou autre maillage) dans Unity. Ce guide est assez avancé, mais j'ai essayé de le décomposer en étapes distinctes. On suppose que vous avez des compétences générales sur Unity et des connaissances de base en C #. Le guide est conçu pour amplifier, mais je pense qu'il peut également être appliqué au Shader Graph. Ceci est mon premier guide, j'espère donc qu'il sera suffisamment clair. Si vous souhaitez ajouter quelque chose, faites-le moi savoir. J'ai inclus un kit de démarrage ici, qui a quelques shaders supplémentaires pour ce projet, ainsi qu'un kit de base pour commencer. Tous les fichiers de projet du manuel d'aujourd'hui sont disponibles pour mes clients pour 5 $, mais dans un proche avenir, ils seront disponibles pour tout le monde.

Kit de démarrage
L'ensemble du projet

: , , . . , , , . ( ) unity . , , .

Commençons donc. Pour commencer, je vais identifier le problème et approfondir la théorie sur laquelle la solution sera basée. Pour faire un mélange entre le terrane et d'autres maillages, vous devez en quelque sorte dire au shader où se produit l'intersection avec le terrane. Dire est plus facile que faire, mais il existe un moyen. Et en quoi cela consiste-t-il? Render Texture nous aidera !

Qu'est-ce qu'une texture de rendu?


La texture de rendu est, en fait, la position de la caméra, qui est enregistrée dans le fichier d'actif. La texture de rendu dans les jeux est le plus souvent utilisée pour des choses comme la création d'écrans de surveillance vidéo. Nous pouvons utiliser la texture de rendu pour enregistrer la vue de la caméra dans l'éditeur afin que cette texture puisse ensuite être utilisée comme shader. Ceci est utile car nous pouvons créer des informations sur notre terrain, telles que la hauteur, les normales et les couleurs, dans une texture qui peut ensuite être utilisée en runtime pour se fondre entre les mailles et le terran.


Rendu de texture utilisé pour afficher des images d'oie sur les téléviseurs dans Untitled Goose Game

Personnalisation


Pour commencer, créons une nouvelle scène et terrane. Définissez une taille pratique pour le travail, par exemple 200x200. Vous pouvez maintenant organiser la terrane comme vous le souhaitez. Créez ensuite un nouveau calque et nommez-le «Terrain» et attribuez un terrain à ce calque. Cela est nécessaire pour que le masque de la caméra puisse enregistrer le terrane dans la texture de rendu .


Mon chef-d'œuvre terrane

Les fichiers source du projet ont un préfabriqué appelé "BlendBakingCamera" - faites-le glisser sur la scène. Vous obtiendrez une simple caméra orthographique. À la caméra, vous devez mettre un masque d'abattage sur une nouvelle couche de terrane. Positionnez la caméra au centre de la terrane légèrement au-dessus du point le plus haut du terrain. Ajustez ensuite le plan du clip éloigné pour que la caméra voit le sol de la terrane. En fin de compte, la scène devrait ressembler à ceci:


Shader de remplacement


Maintenant que la caméra est configurée, vous devez trouver un moyen d'enregistrer les données de terrane. Pour cela, nous avons besoin de Shaders de remplacement. Le Replacement Shader semble être une perspective douteuse; je n'ai pas compris moi-même comment cela fonctionne depuis longtemps. Mais en fait, tout est très simple et efficace. L’utilisation du Shader de remplacement signifie essentiellement le rendu de chaque objet dans le champ de vision de la caméra avec un seul shader, quel que soit le shader superposé aux objets. En conséquence, tous les objets seront rendus à l'aide du Shader de remplacement sélectionné , qui n'est en fait qu'un shader ordinaire.

Le shader dont nous avons besoin pour le mélange est le shader de profondeur. Il rend la profondeur de la scène et est un élément clé dans la création de notre effet de fusion, car il écrit les valeurs de profondeur de notre appareil photo dans la texture afin que nous puissions les lire plus tard. Pour en savoir plus sur ce shader et le Shader de remplacement en général, je vous recommande de lire ce manuel de Making Stuff Look Good in Unity .


Exemple de shader de profondeur

Commençons la cuisson


Créons une nouvelle classe et appelons-la «TerrainBlendingBaker» . Commençons par implémenter un masque de profondeur pour le terrane de base. Plus tard, nous reviendrons sur ce script pour ajouter des couleurs et des normales.

Définissez plusieurs variables.

//Shader that renders object based on distance to camera
public Shader depthShader;
//The render texture which will store the depth of our terrain
public RenderTexture depthTexture;
//The camera this script is attached to
private Camera cam;

Créons maintenant une nouvelle méthode et appelons-la «UpdateBakingCamera» . Dans cette méthode, nous déterminerons les données de caméra dont le shader peut avoir besoin pour rendre le mélange dans les variables globales.

private void UpdateBakingCamera()
    {
        //if the camera hasn't been assigned then assign it
        if (cam == null)
        {
            cam = GetComponent<Camera>();
        }
 
        //the total width of the bounding box of our cameras view
        Shader.SetGlobalFloat("TB_SCALE", GetComponent<Camera>().orthographicSize * 2);
        //find the bottom corner of the texture in world scale by subtracting the size of the camera from its x and z position
        Shader.SetGlobalFloat("TB_OFFSET_X", cam.transform.position.x - cam.orthographicSize);
        Shader.SetGlobalFloat("TB_OFFSET_Z", cam.transform.position.z - cam.orthographicSize);
        //we'll also need the relative y position of the camera, lets get this by subtracting the far clip plane from the camera y position
        Shader.SetGlobalFloat("TB_OFFSET_Y", cam.transform.position.y - cam.farClipPlane);
        //we'll also need the far clip plane itself to know the range of y values in the depth texture
        Shader.SetGlobalFloat("TB_FARCLIP", cam.farClipPlane);
 
        //NOTE: some of the arithmatic here could be moved to the shader but keeping it here makes the shader cleaner so ¯\_(ツ)_/¯
    }

Maintenant, faisons cuire la profondeur de la terrane dans la texture.

// The context menu tag allows us to run methods from the inspector (https://docs.unity3d.com/ScriptReference/ContextMenu.html)
[ContextMenu("Bake Depth Texture")]
public void BakeTerrainDepth()
{
    //call our update camera method 
    UpdateBakingCamera();
 
    //Make sure the shader and texture are assigned in the inspector
    if (depthShader != null && depthTexture != null)
    {
        //Set the camera replacment shader to the depth shader that we will assign in the inspector 
        cam.SetReplacementShader(depthShader, "RenderType");
        //set the target render texture of the camera to the depth texture 
        cam.targetTexture = depthTexture;
        //set the render texture we just created as a global shader texture variable
        Shader.SetGlobalTexture("TB_DEPTH", depthTexture);
    }
    else
    {
        Debug.Log("You need to assign the depth shader and depth texture in the inspector");
    }
}

Les valeurs deviendront plus claires lorsque nous commencerons à travailler avec le shader. Voici une petite image qui peut nous éclairer:


Nous allons transférer la terrane à la texture de profondeur afin de la lire plus tard et de comprendre où faire le mélange.Nous

avons maintenant tout ce dont nous avons besoin pour créer l'effet de mélange de base dans le shader. À ce stade, le script ressemble à ceci: pastebin.com/xNusLJfh

D'accord, maintenant que nous avons le script, nous pouvons l'ajouter à la caméra de cuisson que nous avons ajoutée plus tôt. Les actifs initiaux ont un shader appelé 'DepthShader' (Inresin / Shaders / DepthShader) et une texture de rendu appelée 'DepthTerrainRT' (Inresin / RenderTextures / DepthTextureRT) , vous devez les mettre dans les champs appropriés dans l'inspecteur.

Après cela, exécutez simplement la méthode dans le menu contextuel pour cuire la profondeur de votre terrain dans la texture de rendu .


Shader


Créons enfin un shader pour le mélange. Créez un nouveau shader d'amplification standard et ouvrez-le, nommez-le 'TerrainBlending' ou ainsi.

Nous devons maintenant créer un UV pour la texture de rendu . Ce sera la différence entre le point en cours de rendu et la position de la caméra de cuisson mise à l'échelle par rapport à la zone totale. Les trois variables globales ici sont celles que nous venons de déclarer dans le code. Nous définissons également worldY comme une variable locale, nous en aurons besoin plus tard.



Prenons la texture de profondeur que nous avons affectée en tant que variable globale (pour cela, ajoutez un nœud d'échantillon de texture , rendez-le global et nommez-le 'TB_DEPTH'), si nous mettons la sortie dans le champ debug amplify shader , nous pouvons voir ce qui se passe. Créez un plan avec le matériau auquel notre nouveau shader sera appliqué.


Donc, dans le shader, nous avons des informations sur la profondeur, maintenant nous devons ajouter un décalage en y pour obtenir le mélange.



Ce bloc met à l'échelle la position y du masque du plan de délimitation éloigné , soustrait cette valeur de la position mondiale le long de l'axe y du point en cours de rendu, puis la déplace finalement vers le côté inférieur du cadre de délimitation de la caméra (position y de la caméra moins le plan farclip ).

Déjà quelque chose! Nous voyons comment les bords de l'avion fusionnent avec la terrane.



Ok, rendons le mélange plus contrôlé.



Nous pouvons maintenant contrôler l'épaisseur et la surface du terrane de mélange.



Je pense qu'on peut même ajouter un peu de bruit. Utilisons la position mondiale pour générer du bruit à partir de la texture.



Les textures de bruit se trouvent dans le dossier de texture du projet de démarrage, elles peuvent être affectées dans l'inspecteur ou en tant que constante dans le shader lui-même.

Il est enfin temps d'ajouter quelques textures! Pour commencer, utilisons des textures simples d'une seule couleur, j'ai ajouté deux textures au dossier avec des actifs de texture. Faites de la texture «SingleColorGrass» une texture de terrane. Ensuite, dans le shader, vous devez créer une texture de terrane et un nœud de texture d'objet . Nous basculerons entre eux sur le canal rouge du masque que nous venons de créer.




Et voici le shader complet.



Ajout personnalisée toon éclairage ou éclairage non éclairés modèles seront les meilleurs résultats pour ce shader. Je me suis éteintshader terrane et version non éclairée du shader dans le package complet disponible pour les sponsors.


Terrane non éclairé et mailles

Je recommande également d'ajouter un shader triplanaire à la terrane et éventuellement à d'autres mailles. Je peux considérer cette question dans le prochain guide.

Eh bien, nous avons presque fini avec le thème principal du guide d'aujourd'hui. J'ai ajouté quelques sections qui peuvent aider avec l'extension du shader.

Extension de shader - Normal


J'ai ajouté un shader régulier aux fichiers, vous pouvez l'utiliser pour écrire des normales sur la surface de la terrane et également utiliser le mélange. Pour mon jeu, je n'ai pas besoin d'un mélange normal, donc j'ai juste expérimenté un peu et il semble que l'expérience ait été un succès, et mon idée fonctionne bien pour le terrain sans un haut degré de changement de hauteur. J'ai inclus le shader de mappage normal dans le répertoire avec les shaders de l'ensemble de départ. Ici, vous pouvez voir mon implémentation de base de la carte normale:



Le code est presque le même qu'avec la carte de profondeur, mais cette fois, nous utiliserons la carte normale comme shader de remplacement . J'ai également ajouté un ensemble de texture de rendu pour l'enregistrement normal (ici, vous aurez peut-être besoin d'une configuration supplémentaire).

Pour que les normales fonctionnent bien, cela peut prendre un peu plus d'efforts liés au réglage, et il peut également y avoir une restriction associée aux terranes de bas niveau (mais je n'ai pas effectué suffisamment de tests pour le confirmer).

Extension Shader - Toutes les couleurs


Je ne vais pas entrer dans les détails de ce sujet, car il va au-delà de ce qui est requis pour mon jeu, mais j'y ai pensé en écrivant ce guide. Pour ajouter un mélange de nombreuses couleurs, nous pouvons sélectionner des couleurs de terrane non éclairées et les enregistrer en tant que textures. Dans ce cas, nous sommes limités par une résolution assez faible, mais cette méthode fonctionne bien lorsque vous utilisez des textures de terrain monochromes et des textures basse résolution, ou lorsque vous utilisez un fond perdu faible . Avec des ajustements mineurs, ils peuvent également être appliqués aux mailles de terrane.

Voici le code de l'option multicolore:

[Header("The following settings are only if using the multi-color terrain shader")]
//Shader that renders the unlit terraom of an object
public Shader unlitTerrainShader;
//The render texture which will store the normals of our terrain
public RenderTexture surfaceTexture;
//An unlit terrain material used to capture the texture of our terrain without any lighting
public Material unlitTerrainMaterial;
//The terrain you want to capture the textures of
public Terrain yourTerrain;
 
[ContextMenu("Bake Surface Texture")]
public void BakeTerrainSurface()
{
    UpdateBakingCamera();
 
    //return if there is no terrain assigned
    if (yourTerrain == null)
    {
        Debug.Log("You need to assign a terrain to capture surface texture");
        return;
    }
 
    StartCoroutine(BakeColors());
}
 
IEnumerator BakeColors()
{
    Material tempTerrainMaterial = yourTerrain.materialTemplate;
 
    yourTerrain.materialTemplate = unlitTerrainMaterial;
 
    yield return 0;
 
    cam.SetReplacementShader(unlitTerrainShader, "RenderType");
    cam.targetTexture = surfaceTexture;
    Shader.SetGlobalTexture("TB_SURFACE", surfaceTexture);
 
    yield return 0;
 
    cam.targetTexture = null;
    yourTerrain.materialTemplate = tempTerrainMaterial;
 
    yield return null;
 
}

Le seul changement dans le shader est qu'au lieu d'utiliser une texture prédéfinie, nous utilisons une texture de surface de terrain définie globalement qui utilisera la position relative comme UV. Une extension supplémentaire vous permet d'obtenir un mélange de textures plus agréable.


Mélange de textures multiples

Voici un graphique multicolore complet avec un shader de mélange normal:




Conclusion


Félicitations si vous avez lu jusqu'ici! Comme je l'ai dit, c'est mon premier guide, donc j'apprécierais vraiment les commentaires. Si vous souhaitez m'aider à créer des jeux et des tutoriels, jetez un œil ici ou abonnez-vous à mon Twitter .



En savoir plus sur le cours

All Articles