Unity Terrane y Mesh Blending

Hola a todos, en junio OTUS lanzará nuevamente el curso Unity Games Developer . En previsión del comienzo del curso, hemos preparado una traducción de material interesante sobre el tema.



Hoy hablaremos sobre cómo combinar una malla de terreno (u otra malla) en Unity. Esta guía es bastante avanzada, pero intenté dividirla en pasos separados. Se supone que tiene habilidades generales de Unity y conocimientos básicos de C #. La guía está diseñada para amplificar, pero creo que también se puede aplicar al Shader Graph. Esta es mi primera guía, así que espero que sea lo suficientemente clara. Si quieres agregar algo, avísame. He incluido un kit de inicio aquí, que tiene algunos sombreadores adicionales para este proyecto, así como un kit básico para comenzar. Todos los archivos del proyecto del manual de hoy están disponibles para mis clientes por $ 5, pero en un futuro cercano, estarán disponibles para todos.

Kit de inicio
Todo el proyecto

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

Vamos a empezar. Para comenzar, señalaré el problema y profundizaré en la teoría en la que se basará la solución. Para hacer una mezcla entre el terreno y otras mallas, debe decirle al sombreador de alguna manera dónde se produce la intersección con el terreno. Decir es más fácil que hacerlo, pero hay una manera. ¿Y en qué consiste? ¡Render Texture nos ayudará !

¿Qué es una textura de renderizado?


Render Texture es, de hecho, la posición de la cámara, que se guarda en el archivo de activos. Render Texture en los juegos se usa con mayor frecuencia para cosas como crear pantallas de video vigilancia. Podemos usar la textura de renderizado para guardar la vista de la cámara en el editor para que esta textura pueda usarse como sombreador. Esto es útil porque podemos hornear información sobre nuestro terreno, como la altura, las normales y los colores, en una textura que luego se puede utilizar en tiempo de ejecución para combinar mallas y terran.


El renderizado de texturas se usa para mostrar imágenes de ganso en televisores en Untitled Goose Game

Personalización


Para comenzar, creemos una nueva escena y terreno. Establezca un tamaño conveniente para el trabajo, por ejemplo, 200x200. Ahora puedes organizar el terreno como quieras. Luego cree una nueva capa y asígnele el nombre "Terreno" y asigne un terreno a esta capa. Esto es necesario para que la máscara de la cámara pueda grabar el terreno en la Textura de renderizado .


Mi obra maestra terrane

Los archivos fuente del proyecto tienen un prefabricado llamado "BlendBakingCamera" : arrástrelo al escenario. Obtendrá una cámara ortográfica simple. En la cámara, debe colocar una máscara de eliminación en una nueva capa de terreno. Coloque la cámara en el centro del terreno ligeramente por encima del punto más alto del terreno. Luego ajuste el plano de recorte lejano para que la cámara vea el suelo del terreno. Al final, la escena debería verse así:


Shader de repuesto


Ahora que la cámara está configurada, debe encontrar una manera de registrar datos de terreno. Para esto necesitamos sombreadores de repuesto. The Replacement Shader parece una perspectiva dudosa; yo mismo no he entendido cómo funciona durante mucho tiempo. Pero, de hecho, todo es muy simple y efectivo. Usar el Sombreador de reemplazo significa esencialmente representar cada objeto en el campo de visión de la cámara con un solo sombreador, independientemente del sombreador que se superponga a los objetos. Como resultado, todos los objetos se renderizarán utilizando el sombreador de reemplazo seleccionado , que en realidad es solo un sombreador normal.

El sombreador que necesitamos para mezclar es el sombreador de profundidad. Representa la profundidad de la escena y es un componente clave en la creación de nuestro efecto de fusión, ya que escribe los valores de profundidad de nuestra cámara en la textura para que podamos leerlos más tarde. Para obtener más información sobre este shader y el Shader de reemplazo en general, le recomiendo que lea este manual de Making Stuff Look Good in Unity .


Ejemplo de sombreador de profundidad

Empecemos a hornear


Creemos una nueva clase y llamémosla "TerrainBlendingBaker" . Comencemos implementando una máscara de profundidad para el terreno base. Más tarde volveremos a este script para agregar colores y normales.

Definir varias 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;

Ahora creemos un nuevo método y llamémoslo "UpdateBakingCamera" . En este método, determinaremos los datos de la cámara que el sombreador puede necesitar para mezclar en 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 ¯\_(ツ)_/¯
    }

Ahora horneemos la profundidad del terreno en la textura.

// 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");
    }
}

Los valores serán más claros cuando comencemos a trabajar con el sombreador. Aquí hay una pequeña imagen que puede arrojar algo de luz:


Vamos a transferir el terreno a la textura de profundidad para leerlo más tarde y comprender dónde hacer la fusión.

Ahora tenemos todo lo que necesitamos para crear el efecto de fusión básico en el sombreador. En este punto, el script se ve más o menos así: pastebin.com/xNusLJfh

Bien, ahora que tenemos el script, podemos agregarlo a la cámara de horneado que agregamos anteriormente. Los activos iniciales tienen un sombreador llamado 'DepthShader' (Inresin / Shaders / DepthShader) y una Textura de renderizado llamada 'Depth FieldRT' (Inresin / RenderTextures / DepthTextureRT) , debe colocarlos en los campos apropiados en el inspector.

Después de eso, simplemente ejecute el método a través del menú contextual para hornear la profundidad del terreno en la Textura de renderizado .


Shader


Finalmente creemos un sombreador para mezclar. Cree un nuevo sombreador de amplificación estándar y ábralo, asígnele el nombre 'TerrainBlending' más o menos.

Ahora necesitamos crear un UV para la textura de renderizado . Esta será la diferencia entre el punto que se renderiza y la posición de la cámara de cocción escalada en relación con el área total. Las tres variables globales aquí son las que acabamos de declarar en el código. También establecemos worldY como una variable local, la necesitaremos más adelante.



Tomemos la textura de profundidad que asignamos como variable global (para esto, agregue un nodo de muestra de textura , hágalo global y asígnele el nombre 'TB_DEPTH'), si ponemos la salida en el sombreador de amplificación de depuración 'un campo , podemos ver qué sucede. Cree un plano con el material al que se aplicará nuestro nuevo sombreador.


Entonces, en el sombreador, tenemos información sobre la profundidad, ahora necesitamos agregar un desplazamiento en y para obtener la fusión.



Este bloque escala la posición y de la máscara del plano de recorte lejano , resta este valor de la posición mundial a lo largo del eje y del punto que se está representando, y finalmente lo desplaza hacia el lado inferior del cuadro delimitador de la cámara (posición y de la cámara menos el plano del clip lejano ).

Ya algo! Vemos cómo los bordes del avión se fusionan con el terreno.



Ok, hagamos que la mezcla sea más controlada.



Ahora podemos controlar el grosor y el área del terreno de mezcla.



Creo que incluso podemos agregar un poco de ruido. Usemos la posición mundial para generar ruido a partir de la textura.



Las texturas de ruido están en la carpeta de texturas en el proyecto de inicio, se pueden asignar en el inspector o como una constante en el sombreador.

¡Finalmente es hora de agregar algunas texturas! Para comenzar, usemos algunas texturas simples de un solo color, agregué dos texturas a la carpeta con activos de textura. Convierta la textura 'SingleColorGrass' en una textura de terreno. Luego, en el sombreador, debe crear una textura de terreno y un nodo de textura de objeto . Cambiaremos entre ellos en el canal rojo de la máscara que acabamos de crear.




Y aquí está el sombreador completo:



agregar iluminación personalizada de toon o modelos de iluminación apagada proporcionará los mejores resultados para este sombreador. Encendí apagadosombreador de terreno y versión apagada del sombreador en el paquete completo disponible para patrocinadores.


Terrane y mallas apagadas

También recomiendo agregar un sombreador triplanar al terreno y posiblemente otras mallas. Puedo considerar este problema en la próxima guía.

Bueno, casi hemos terminado con el tema principal de la guía de hoy. He agregado algunas secciones que pueden ayudar con la extensión del sombreador.

Extensión del sombreador: normal


Agregué un sombreador regular a los archivos, puede usarlo para escribir normales en la superficie del terreno y también usar mezclas. Para mi juego, no necesito una mezcla normal, así que solo experimenté un poco, y parece que el experimento fue un éxito, y mi idea funciona bien para el terreno sin un alto grado de cambio de altura. Incluí el sombreador de mapeo normal en el directorio con los sombreadores de inicio. Aquí puede ver mi implementación básica del mapa normal:



el código es casi el mismo que con el mapa de profundidad, pero esta vez usaremos el mapa normal como sombreador de reemplazo . También agregué un conjunto de texturas de renderizado para grabar normal (aquí es posible que necesite una configuración adicional).

Para que las normales funcionen bien, puede tomar un poco más de esfuerzo relacionado con el ajuste, y también puede haber una restricción asociada con los terrenos de bajo nivel (pero no he realizado suficientes pruebas para confirmar esto).

Extensión del sombreador: todos los colores


No voy a profundizar en los detalles de este tema, ya que va más allá de lo que se requiere para mi juego, pero lo pensé mientras escribía esta guía. Para agregar mezclas de muchos colores, podemos seleccionar colores de terreno no iluminados y guardarlos como texturas. En este caso, estamos limitados por una resolución bastante baja, pero este método funciona bien cuando se usan texturas de terreno de un solo color y texturas de baja resolución, o cuando se usa un sangrado débil . Con ajustes menores, también se pueden aplicar a mallas de terreno.

Aquí está el código para la opción multicolor:

[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;
 
}

El único cambio en el sombreador es que, en lugar de usar una textura predefinida, usamos una textura de superficie del terreno definida globalmente que usará la posición relativa como UV. Una extensión adicional le permite obtener una combinación más agradable de texturas.


Mezcla de texturas múltiples

Aquí hay un gráfico multicolor completo con un sombreador de mezcla normal:




Conclusión


¡Felicitaciones si has leído hasta este punto! Como dije, esta es mi primera guía, por lo que agradecería mucho sus comentarios. Si quieres apoyarme en la creación de juegos y tutoriales, échale un vistazo aquí o suscríbete a mi Twitter .



Aprende más sobre el curso

All Articles