Exploración del juego de sombreador de arena Journey

Comienzo de una serie de artículos aquí.

imagen

Parte 4: Imagen espejo


En esta parte, nos centraremos en los reflejos de los espejos, gracias a los cuales las dunas se asemejan a un océano de arena.

Uno de los efectos más intrigantes de la reproducción de arena de Journey es cómo las dunas brillan en los rayos de luz. Esta reflexión se llama especular . El nombre proviene de la palabra latina speculum , que significa "espejo" . La reflexión especular es un concepto de "paraguas" que combina todo tipo de interacciones en las que la luz se refleja fuertemente en una dirección en lugar de dispersarse o absorberse. Gracias a los reflejos especulares, tanto el agua como las superficies pulidas en cierto ángulo se ven brillantes.

En viajeHay tres tipos de reflejos de espejo: iluminación de borde , reflejo especular oceánico y brillo , que se muestran en el siguiente diagrama. En esta parte veremos los dos primeros tipos.




Antes y después de aplicar reflejos de espejo.

Iluminación de la llanta


Puede notar que en cada nivel de Viaje se presenta un conjunto limitado de colores. Y si bien esto crea una estética fuerte y limpia, este enfoque complica la renderización de arena. Las dunas se representan solo con un número limitado de sombras, por lo que es difícil para el jugador comprender dónde termina una a una distancia larga y comienza la otra.

Para compensar esto, el borde de cada duna tiene un ligero efecto de brillo, destacando sus contornos. Esto permite que las dunas no se escondan en el horizonte y crea la ilusión de un entorno mucho más amplio y complejo.

Antes de comenzar a descubrir cómo implementar este efecto, ampliemos la función de iluminación agregándole un color difuso (considerado por nosotros en la parte anterior del artículo) y un nuevo componente generalizado de reflexión especular.

float4 LightingJourney (SurfaceOutput s, fixed3 viewDir, UnityGI gi)
{
    // Lighting properties
    float3 L = gi.light.dir;
    float3 N = s.Normal;

    // Lighting calculation
    float3 diffuseColor	= DiffuseColor (N, L);
    float3 rimColor     = RimLighting  (N, V);

    // Combining
    float3 color = diffuseColor + rimColor;

    // Final color
    return float4(color * s.Albedo, 1);
}

En el fragmento de código que se muestra arriba, vemos que el componente espejo de la iluminación de la llanta, que se llama rimColor, simplemente se agrega al color difuso original.

Alto rango dinámico y efectos de floración
Tanto el componente difuso como el brillo de los bordes son colores RGB en el rango de 0antes de 1. El color final está determinado por su suma. Esto significa que podría ser potencialmente más grande.1.

Si no es nuevo en la codificación de sombreadores, puede saber que los colores deben limitarse a un rango de0 antes de 1. Sin embargo, hay momentos en que necesitamos colores para exceder1. High Dynamic Range, 1«» . bloom, . .

Reflexiones de Fresnel


El brillo de los bordes se puede realizar de muchas maneras diferentes. La codificación de sombreadores más popular utiliza el conocido modelo de reflexión Fresnel .

Para comprender la ecuación subyacente a la reflexión de Fresnel, es útil visualizar dónde ocurre. El siguiente diagrama muestra cómo la duna es visible a través de la cámara (en azul). La flecha roja indica la superficie normal de la parte superior de la duna, donde debería ser una imagen especular. Es fácil ver que todos los bordes de la duna tienen una propiedad común: su normalidad (N, rojo) es perpendicular a la dirección de la vista (V, de color azul).


Similar a lo que hicimos en la parte sobre Color difuso, puede usar el producto escalar Ny Vpara obtener una medida de su paralelismo. En este casoNV es igual 0, porque dos vectores unitarios son perpendiculares; en su lugar podemos usar1NVpara obtener medidas de su no paralelismo.

Uso directo1NVno nos dará buenos resultados, porque la reflexión será demasiado fuerte. Si queremos que la reflexión sea más nítida , podemos tomar la expresión en grado. Grado de magnitud desde0 antes de 1permanece limitado a un intervalo, pero la transición entre la oscuridad y la luz se agudiza.

El modelo de reflexión de Fresnel afirma que el brillo de la luzI se establece de la siguiente manera:

I=(1NV)powerstrength(1)


Dónde powery strength- Estos son dos parámetros que se pueden usar para controlar el contraste y la fuerza del efecto. Parámetrospower y strengtha veces se llama especular y brillo , pero los nombres pueden variar.

La ecuación (1) es muy fácil de convertir a código:

float _TerrainRimPower;
float _TerrainRimStrength;
float3 _TerrainRimColor;

float3 RimLighting(float3 N, float3 V)
{
    float rim = 1.0 - saturate(dot(N, V));
    rim = saturate(pow(rim, _TerrainRimPower) * _TerrainRimStrength);
    rim = max(rim, 0); // Never negative
    return rim * _TerrainRimColor;
}

Su resultado se muestra en la animación a continuación.


Océano especular


Uno de los aspectos más originales de la jugabilidad de Journey es que a veces un jugador puede literalmente "surfear" las dunas. El ingeniero líder John Edwards explicó que esa empresa del juego buscaba hacer que la arena se sintiera más sólida, no líquida.

Y esto no está del todo mal, porque la arena puede percibirse como una aproximación muy aproximada de un líquido. Y bajo ciertas condiciones, por ejemplo, en un reloj de arena, incluso se comporta como un líquido.

Para reforzar la idea de que la arena puede tener un componente líquido, Journey ha agregado un segundo efecto de reflexión, que a menudo se encuentra en los cuerpos líquidos. John Edwards lo llama océano especular: La idea es obtener el mismo tipo de reflejos que son visibles en la superficie del océano o lago al atardecer (ver más abajo).


Como antes, haremos cambios en la función de iluminación LightingJourneypara agregarle un nuevo tipo de reflexión especular.

float4 LightingJourney (SurfaceOutput s, fixed3 viewDir, UnityGI gi)
{
    // Lighting properties
    float3 L = gi.light.dir;
    float3 N = s.Normal;
    float3 V = viewDir;

    // Lighting calculation
    float3 diffuseColor	= DiffuseColor  (N, L);
    float3 rimColor     = RimLighting   (N, V);
    float3 oceanColor   = OceanSpecular (N, L, V);

    // Combining
    float3 specularColor = saturate(max(rimColor, oceanColor));
    float3 color = diffuseColor + specularColor;

    // Final color
    return float4(color * s.Albedo, 1);
}

¿Por qué tomamos un máximo de dos componentes de reflexión?
, rim lighting ocean specular. , , -. , .

, .

Los reflejos de espejo en el agua a menudo se realizan utilizando el reflejo Blinn-Fong , que es una solución de bajo costo para materiales brillantes. Fue descrito por primera vez por James F. Blinn en 1977 (artículo: " Modelos de reflexión de luz para imágenes sintetizadas por computadora ") como una aproximación de una técnica de sombreado anterior desarrollada por Bui Tyong Fong en 1973 (artículo: " Iluminación para imágenes generadas por computadora ") .

Cuando se usa el sombreado Blinnu-Phong, luminosidadI superficies viene dada por la siguiente ecuación:

I=(NH)powerstrength(2)


Dónde

H=V+LV+L(3)


El denominador de la ecuación (3) divide el vector. V+Len su longitud Esto asegura queH tiene una longitud 1. La función de sombreador equivalente para realizar esta operación es esta normalize. Desde un punto de vista geométrico,H representa el vector entre Vy L, y por lo tanto se llama medio vector .



¿Por qué está H entre V y L?
, , HVL.

, . VLLVVL.

, , V+LL+V, . , :


, , . , ( V+L) . , ( ).


, V+LVL, 1. , 1, ( ).

Se puede encontrar una descripción más detallada de la reflexión de Blinn-Fong en el tutorial de Modelos de iluminación y renderizado basados en la física . A continuación se muestra una implementación simple en el código del sombreador.

float _OceanSpecularPower;
float _OceanSpecularStrength;
float3 _OceanSpecularColor;

float3 OceanSpecular (float3 N, float3 L, float3 V)
{
    // Blinn-Phong
    float3 H = normalize(V + L); // Half direction
    float NdotH = max(0, dot(N, H));
    float specular = pow(NdotH, _OceanSpecularPower) * _OceanSpecularStrength;
    return specular * _OceanSpecularColor;
}

La animación presenta una comparación del sombreado difuso tradicional según Lambert y el espejo según Blinn-Fong:


Parte 5: reflejo brillante


En esta parte, recrearemos los brillantes reflejos que generalmente son visibles en las dunas de arena.

Poco después de publicar mi serie de artículos, Julian Oberbek y Paul Nadelek hicieron su propio intento de recrear una escena inspirada en el juego Journey en Unity. El siguiente tweet muestra cómo perfeccionaron los reflejos brillantes para proporcionar una mayor integridad temporal. Lea más sobre su implementación en un artículo sobre IndieBurg Mip Map Folding .


En la parte anterior del curso, revelamos la implementación de dos funciones de espejo en el renderizado de arena Journey : iluminación de borde y especular oceánico . En esta parte, explicaré cómo implementar la última versión de la reflexión especular: brillo .


Si alguna vez has estado en el desierto, probablemente hayas notado cómo la arena es realmente brillante. Como se discutió en la parte de arena normal, cada grano de arena puede reflejar la luz en una dirección aleatoria. Debido a la naturaleza de los números aleatorios, parte de estos rayos reflejados caerán en la cámara. Debido a esto, los puntos aleatorios de arena aparecerán muy brillantes. Este brillo es muy sensible al movimiento, ya que el menor cambio evitará que los rayos reflejados entren en la cámara.

En otros juegos, como Astroneer y Slime Rancher, se usaron reflejos brillantes para arena y cuevas.



Brillo: antes y después de aplicar el efecto.

Es más fácil evaluar estas características de brillo en una imagen más grande:



Sin lugar a dudas, el efecto de brillo en las dunas reales depende completamente del hecho de que algunos granos de arena reflejan la luz al azar en nuestros ojos. Estrictamente hablando, esto es exactamente lo que ya hemos modelado en la segunda parte del curso dedicado a las normales de arena, cuando modelamos una distribución aleatoria de las normales. Entonces, ¿por qué necesitamos otro efecto para esto?

La respuesta puede no ser demasiado obvia. Imaginemos que estamos tratando de recrear el efecto de brillo solo con la ayuda de los normales. Incluso si todas las normales se dirigen a la cámara, la arena aún no brillará, porque las normales solo pueden reflejar la cantidad de luz disponible en la escena. Es decir, en el mejor de los casos, reflejaremos solo el 100% de la luz (si la arena es completamente blanca).

Pero necesitamos algo más. Si queremos que el píxel se vea tan brillante que la luz se extienda sobre los píxeles adyacentes, entonces el color debería ser más grande1. Esto sucedió porque en Unity, cuando el filtro de floración se aplica a la cámara utilizando el efecto de procesamiento posterior , los colores son más brillantes.1se extiende a los píxeles vecinos y produce un halo que crea la sensación de que algunos píxeles brillan intensamente. Esta es la base de la representación HDR .

Por lo tanto, no, las normales superpuestas no se pueden usar de una manera simple para crear superficies brillantes. Por lo tanto, este efecto es más conveniente de implementar como un proceso separado.

Teoría de las microfaces


Para abordar la situación de manera más formal, necesitamos percibir las dunas como si fueran espejos microscópicos, cada uno de los cuales tiene una dirección aleatoria. Este enfoque se llama la teoría de microfacet , donde cada uno de estos pequeños espejos se llama microfacet . La base matemática de la mayoría de los modelos de sombreado modernos se basa en la teoría de las micro caras, incluido el modelo de sombreador estándar de Unity .

El primer paso es dividir la superficie de la duna en micro caras y determinar la orientación de cada una de ellas. Como ya se mencionó, hicimos algo similar en parte del tutorial sobre las normales de arena, donde se usó la posición UV del modelo 3D de la duna para muestrear una textura aleatoria. Aquí se puede usar el mismo enfoque para adjuntar una orientación aleatoria a cada micro faceta. El tamaño de cada microface dependerá de la escala de la textura y de su nivel de texturas mip . Nuestra tarea es recrear una cierta estética, y no el deseo de fotorrealismo; Este enfoque será lo suficientemente bueno para nosotros.

Después de muestrear una textura aleatoria, podemos asociar una dirección aleatoria con cada grano de arena / micro faceta de la duna. Vamos a llamarloG. Indica la dirección del brillo , es decir, la dirección de la normalidad de los granos de arena que estamos viendo. Se reflejará un rayo de luz que cae sobre un grano de arena, teniendo en cuenta el hecho de que la micro faceta es un espejo ideal orientado en la direcciónG. El rayo de luz reflejado resultante debe ingresar a la cámara.R (vea abajo).


Aquí podemos usar nuevamente el producto escalar Ry Vpara obtener medidas de su paralelismo.

Un enfoque es la exponenciaciónRV, como se explica en la parte anterior (cuarta) del artículo. Si intenta hacerlo, veremos que el resultado es muy diferente de lo que está en Journey . Los reflejos brillantes deben ser raros y muy brillantes. La forma más fácil será considerar solo aquellas reflexiones brillantes para las cualesRV está por debajo de un cierto valor umbral.

Implementación


Podemos implementar fácilmente el efecto de brillo descrito anteriormente usando una función reflecten Cg, lo que hace que sea muy fácil de calcularR.

sampler2D_float _GlitterTex;
float _GlitterThreshold;
float3 _GlitterColor;

float3 GlitterSpecular (float2 uv, float3 N, float3 L, float3 V)
{
    // Random glitter direction
    float3 G = normalize(tex2D(_GlitterTex, uv).rgb * 2 - 1); // [0,1]->[-1,+1]

    // Light that reflects on the glitter and hits the eye
    float3 R = reflect(L, G);
    float RdotV = max(0, dot(R, V));
	
    // Only the strong ones (= small RdotV)
    if (RdotV > _GlitterThreshold)
        return 0;
	
    return (1 - RdotV) * _GlitterColor;
}

Estrictamente hablando, si Gcompletamente por casualidad entonces RTambién será completamente al azar. Puede parecer que el uso es reflectopcional. Y aunque esto es cierto para un marco estático, pero ¿qué sucede si la fuente de luz se mueve? Esto puede deberse al movimiento del sol o a una fuente puntual de luz vinculada al jugador. En ambos casos, la arena perderá integridad temporal entre los cuadros actuales y posteriores, por lo que el efecto de brillo aparecerá en lugares aleatorios. Sin embargo, el uso de la función reflectproporciona una representación mucho más estable.

Los resultados se muestran a continuación:


Como recordamos de la primera parte del tutorial, el componente de brillo se agrega al color final.

#pragma surface surf Journey fullforwardshadows

float4 LightingJourney (SurfaceOutput s, fixed3 viewDir, UnityGI gi)
{
    float3 diffuseColor = DiffuseColor    ();
    float3 rimColor     = RimLighting     ();
    float3 oceanColor   = OceanSpecular   ();
    float3 glitterColor = GlitterSpecular ();

    float3 specularColor = saturate(max(rimColor, oceanColor));
    float3 color = diffuseColor + specularColor + glitterColor;
	
    return float4(color * s.Albedo, 1);
}

Existe una alta probabilidad de que algunos píxeles obtengan más color. 1, lo que conducirá al efecto de floración. Éso es lo que necesitamos. El efecto también se agrega sobre una reflexión especular existente (discutida en la parte anterior del artículo), por lo que incluso se pueden encontrar granos brillantes de arena donde las dunas están bien iluminadas.

Hay muchas formas de mejorar esta técnica. Todo depende del resultado que desea lograr. En Astroneer y Slime Rancher , por ejemplo, este efecto solo se usa de noche. Esto se puede lograr reduciendo la intensidad del efecto de brillo según la dirección de la luz solar.

Por ejemplo, el valor max(dot(L, fixed3(0,1,0),0))es1cuando el sol cae desde arriba, y es igual a cero cuando está más allá del horizonte. Pero puede crear su propio sistema, cuya apariencia depende de sus preferencias.

¿Por qué no se usa la reflexión en la reflexión de Blinn-Fong?
ocean specular, , -.

, 3D- , , . , reflect . - RVNH, .

Parte 6: olas


En la última parte del artículo, recrearemos las típicas olas de arena resultantes de la interacción de las dunas y el viento.




Olas en la superficie de las dunas: antes y después.

Teóricamente, sería lógico poner esta parte después de la parte sobre las normales de la arena. Lo dejé al final porque es el más difícil de los efectos del tutorial. Parte de esta complejidad se debe a la forma en que los mapas normales son almacenados y procesados ​​por un sombreador de superficie que realiza muchos pasos adicionales.

Mapas normales


En la parte anterior (quinta), exploramos un método para producir arena heterogénea. En la parte dedicada a las normales de arena, se utilizó una técnica de mapeo normal muy popular para cambiar la forma en que la luz interactúa con la superficie de la geometría . A menudo se usa en gráficos 3D para crear la ilusión de que el objeto tiene una geometría más compleja, y generalmente se usa para suavizar las superficies curvas (ver más abajo).


Para lograr este efecto, cada píxel se asigna a la dirección de lo normal , lo que indica su orientación. Y se utiliza para calcular la iluminación en lugar de la orientación real de la malla.

Al leer las direcciones de las normales a partir de una textura aparentemente aleatoria, pudimos simular el granulado. A pesar de la inexactitud física, ella todavía se ve creíble.

Olas en la arena


Sin embargo, las dunas de arena muestran otra característica que no se puede ignorar: las olas. En cada duna hay dunas más pequeñas, que aparecen debido a la influencia del viento y se mantienen unidas por la fricción de los granos individuales de arena.

Estas olas son muy visibles y visibles en la mayoría de las dunas. En la fotografía que se muestra a continuación, tomada en Omán, se ve que la parte cercana de la duna tiene un patrón ondulado pronunciado.


Estas olas varían significativamente dependiendo no solo de la forma de la duna, sino también de la composición, dirección y velocidad del viento. La mayoría de las dunas con un pico agudo tienen olas en un solo lado (ver más abajo).


El efecto presentado en el tutorial está diseñado para dunas más suaves con olas en ambos lados. Esto no siempre es físicamente preciso, pero lo suficientemente realista como para ser creíble, y es un buen primer paso hacia implementaciones más complejas.

Realización de las olas.


Hay muchas formas de implementar olas. Lo menos costoso es simplemente dibujarlos en una textura, pero en el tutorial queremos lograr algo más. La razón es simple: las ondas no son "planas" y deben interactuar correctamente con la luz. Si simplemente los dibuja, será imposible lograr un efecto realista cuando la cámara (o el sol) se mueva.

Otra forma de agregar ondas es cambiar la geometría del modelo de duna. Pero no se recomienda aumentar la complejidad del modelo, ya que afecta en gran medida el rendimiento general.

Como vimos en la parte sobre las normales de arena, puede solucionar este problema utilizando mapas normales . De hecho, se dibujan en la superficie como texturas tradicionales, pero se usan en cálculos de iluminación para simular geometrías más complejas.

Es decir, la tarea se ha convertido en otra: cómo crear estos mapas normales. La representación manual llevará demasiado tiempo. Además, cada vez que cambie las dunas, deberá volver a dibujar las olas nuevamente. Esto ralentizará significativamente el proceso de creación de recursos, que muchos artistas técnicos intentan evitar.

Una solución mucho más efectiva y óptima sería agregar ondas de manera procesal . Esto significa que las direcciones normales de las dunas cambian según la geometría local para tener en cuenta no solo los granos de arena, sino también las olas.

Dado que las ondas deben simularse en una superficie 3D, será más lógico implementar un cambio en la dirección de las normales para cada píxel. Es más fácil usar un mapa normal sin costuras con un patrón de onda. Este mapa se combinará con el mapa normal existente previamente utilizado para la arena.

Mapas normales


Hasta este momento, nos encontramos con tres normales diferentes :

  • Geometría Normal : la orientación de cada cara del modelo 3D, que se almacena directamente en los vértices;
  • Arena normal : calculada usando textura de ruido;
  • Wave Normal : El nuevo efecto discutido en esta parte.

El siguiente ejemplo, tomado de la página de ejemplos de Unity Surface Shader , muestra una forma estándar de reescribir la normalidad de un modelo 3D. Esto requiere cambiar el valor o.Normal, lo que generalmente se hace después de muestrear la textura (más a menudo llamado el mapa normal ).

  Shader "Example/Diffuse Bump" {
    Properties {
      _MainTex ("Texture", 2D) = "white" {}
      _BumpMap ("Bumpmap", 2D) = "bump" {}
    }
    SubShader {
      Tags { "RenderType" = "Opaque" }
      CGPROGRAM
      #pragma surface surf Lambert
      struct Input {
        float2 uv_MainTex;
        float2 uv_BumpMap;
      };
      sampler2D _MainTex;
      sampler2D _BumpMap;
      void surf (Input IN, inout SurfaceOutput o) {
        o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
        o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
      }
      ENDCG
    } 
    Fallback "Diffuse"
  }

Utilizamos exactamente la misma técnica para reemplazar la normalidad de la geometría por la normal de la arena.

¿Qué es UnpackNormal?
. ( 1), , . X, Y Z R, G B .

1+1. 01. , «» «» . (normal packing) (normal unpacking). :

R=X2+12G=Y2+12B=Z2+12(1)


:

X=2R1Y=2G1Z=2B1(2)


Unity (2), UnpackNormal. .

Normal Map Technical Details polycount.

Empinada de las dunas


Sin embargo, la primera dificultad está relacionada con el hecho de que la forma de onda cambia dependiendo de la nitidez de las dunas. Las dunas bajas y planas tienen olas pequeñas; En las dunas empinadas, los patrones de onda son más distintos. Esto significa que debe considerar la inclinación de la duna.

La forma más fácil de solucionar este problema es crear dos mapas normales diferentes, respectivamente, para dunas planas y empinadas. La idea básica es mezclar entre los dos mapas normales en función de la inclinación de la duna.


Mapa normal de la duna escarpada


Mapa normal para una duna plana

Mapas normales y canal azul
Muchos mapas normales procesan el canal azul un poco diferente a los otros dos.

Esto sucedió porque la información almacenada en él es esencialmente redundante. Los vectores unitarios deben tener una longitud unitaria, es decir, después de determinar dos dimensiones (X e Y), el valor del tercero (Z) está completamente determinado.

length(N)=1X2+Y2+Z2=1(3)


y eso significa:

X2+Y2+Z2=1X2+Y2+Z2=12Z2=1X2Y2Z=1X2Y2(4)


UnpackNormal Unity. Shader API .

inline fixed3 UnpackNormal(fixed4 packednormal)
{
#if defined(SHADER_API_GLES)  defined(SHADER_API_MOBILE)
    return packednormal.xyz * 2 - 1;
#else
    fixed3 normal;
    normal.xy = packednormal.wy * 2 - 1;
    normal.z = sqrt(1 - normal.x*normal.x - normal.y * normal.y);
    return normal;
#endif
}

Unity DXT5nm, (packednormal.xy) - (packednormal.wy).

Normal Map Compression polycount.

¿Por qué los mapas normales parecen lilas?
, , .

, . , [0,0,1].

[0,0,1][0.5,0.5,1], RGB.

; , [0,0,1].

, «». , B 1+1, 0+1. .

La inclinación puede calcularse usando un producto escalar , que a menudo se usa en la codificación de sombreadores para calcular el grado de "paralelismo" de dos direcciones. En este caso, tomamos la dirección de la normalidad de la geometría (que se muestra a continuación en azul) y la comparamos con un vector que apunta al cielo (que se muestra a continuación en amarillo). El producto escalar de estos dos vectores devuelve valores cercanos a1cuando los vectores son casi paralelos (dunas planas) y cerca de 0cuando el ángulo entre ellos es de 90 grados (dunas empinadas).


Sin embargo, nos enfrentamos al primer problema: dos vectores están involucrados en esta operación. El vector normal obtenido de la función surfusando o.Normalse expresa en espacio tangente . Esto significa que el sistema de coordenadas utilizado para codificar la dirección normal es relativo a la geometría de la superficie local (ver más abajo). Tocamos brevemente este tema en la parte sobre la arena normal.


Un vector que apunta al cielo se expresa en el espacio mundial . Para obtener el producto escalar correcto, ambos vectores deben expresarse en el mismo sistema de coordenadas. Esto significa que necesitamos transformar uno de ellos para que ambos se expresen en un espacio.

Afortunadamente, la unidad viene al rescate con una función WorldNormalVectorque nos permite transformar el vector normal del espacio tangente al espacio mundial . Para usar esta función, necesitamos cambiar la estructura Input, para que se incluya float3 worldNormaly INTERNAL_DATA:

struct Input
{
    ...

    float3 worldNormal;
    INTERNAL_DATA
};

Esto se explica en un artículo de la documentación de Unity Writing Surface Shaders que dice:

INTERNAL_DATA — , o.Normal.

, WorldNormalVector (IN, o.Normal).

A menudo, esto se convierte en la principal fuente de problemas al escribir sombreadores de superficie. De hecho, el valor o.Normalque está disponible en la función surf, varía en función de cómo se está utilizando. Si solo lo está leyendo, o.Normalcontiene el vector normal del píxel actual en el espacio mundial . Si cambia su valor, o.Normalestá en espacio tangente .

Si graba o.Normal, pero aún necesita acceso a lo normal en el espacio mundial (como en nuestro caso), puede usarlo WorldNormalVector (IN, o.Normal). Sin embargo, para esto necesita hacer un pequeño cambio en la estructura que se muestra arriba Input.

¿Qué es INTERNAL_DATA?
INTERNAL_DATA Unity.

, , WorldNormalVector. , , . ( ).

, 3D- 3×3. , , (tangent to world matrix), Unity TtoW.

INTERNAL_DATA TtoW Input. , «Show generated code» :


, INTERNAL_DATA — , TtoW:

#define INTERNAL_DATA
    half3 internalSurfaceTtoW0;
    half3 internalSurfaceTtoW1;
    half3 internalSurfaceTtoW2;

half3x3, half3.

WorldNormalVector, , ( ) TtoW:

#define WorldNormalVector(data,normal)
    fixed3
    (
        dot(data.internalSurfaceTtoW0, normal),
        dot(data.internalSurfaceTtoW1, normal),
        dot(data.internalSurfaceTtoW2, normal)
    )

mul, TtoW , .

, :

[ToW1,1ToW1,2ToW1,3ToW2,1ToW2,2ToW2,3ToW3,1ToW3,2ToW3,3][N1N2N3]=[[ToW1,1ToW1,2ToW1,3][N1N2N3][ToW2,1ToW2,2ToW2,3][N1N2N3][ToW3,1ToW3,2ToW3,3][N1N2N3]]


LearnOpenGL.

Implementación


Un fragmento del siguiente código convierte la normalidad de la tangente al espacio mundial y calcula la pendiente en relación con la dirección hacia arriba .

// Calculates normal in world space
float3 N_WORLD = WorldNormalVector(IN, o.Normal);
float3 UP_WORLD = float3(0, 1, 0);

// Calculates "steepness"
// => 0: steep (90 degrees surface)
//  => 1: shallow (flat surface)
float steepness = saturate(dot(N_WORLD, UP_WORLD));

Ahora que hemos calculado la inclinación de la duna, podemos usarla para mezclar los dos mapas normales. Ambos mapas normales se muestrean, tanto planos como geniales (en el código a continuación se llaman _ShallowTexy _SteepTex). Luego se mezclan en función del valor steepness:

float2 uv = W.xz;

// [0,1]->[-1,+1]
float3 shallow = UnpackNormal(tex2D(_ShallowTex, TRANSFORM_TEX(uv, _ShallowTex)));
float3 steep   = UnpackNormal(tex2D(_SteepTex,   TRANSFORM_TEX(uv, _SteepTex  )));

// Steepness normal
float3 S = normalerp(steep, shallow, steepness);

Como se indicó en la parte sobre las normales de arena, es bastante difícil combinar los mapas normales correctamente, y esto no se puede hacer lerp. En este caso slerp, es más correcto de usar , pero en su lugar, se llama una función menos costosa normalerp.

Mezcla de onda


Si utilizamos el código que se muestra arriba, los resultados pueden decepcionarnos. Esto se debe a que las dunas tienen muy poca inclinación, lo que conduce a una mezcla excesiva de las dos texturas normales. Para solucionar esto, podemos aplicar una transformación no lineal a la inclinación, lo que aumentará la nitidez de la mezcla:

// Steepness to blending
steepness = pow(steepness, _SteepnessSharpnessPower);

Cuando se mezclan dos texturas, a menudo se usa para controlar su nitidez y contraste pow. Aprendimos cómo y por qué funciona en mi tutorial de renderizado basado en la física .

A continuación vemos dos gradientes. La parte superior muestra los colores de negro a blanco, interpolados linealmente a lo largo del eje X con c = uv.x. En la parte inferior, el mismo gradiente se representa con c = pow(uv.x*1.5)*3.0:



Es fácil de notar, lo que le powpermite crear una transición mucho más nítida entre blanco y negro. Cuando trabajamos con texturas, esto reduce su superposición, creando bordes más nítidos.

Dirección de las dunas


Todo lo que hicimos anteriormente funciona perfectamente. Pero necesitamos resolver otro último problema. Las olas varían con la inclinación , pero no con la dirección . Como se mencionó anteriormente, las olas generalmente no son simétricas debido al hecho de que el viento sopla principalmente en una dirección.

Para que las olas sean aún más realistas, necesitamos agregar dos mapas normales más (ver la tabla a continuación). Se pueden mezclar según el paralelismo de la duna del eje X o el eje Z.

FrioPlano
Xgenial xplano x
Zgenial zplano z

Aquí necesitamos implementar el cálculo del paralelismo de la duna en relación con el eje Z. Esto se puede hacer de manera similar al cálculo de la inclinación, pero float3 UP_WORLD = float3(0, 1, 0);se puede usar en su lugar float3 Z_WORLD = float3(0, 0, 1);.

Este último paso te lo dejo a ti. Si tiene algún problema, al final de este tutorial hay un enlace para descargar el paquete completo de Unity.

Conclusión


Esta es la última parte de una serie de tutoriales sobre renderizado de arena de Journey.

A continuación se muestra hasta dónde hemos podido avanzar en esta serie:



Antes y después

quiero agradecerle por leer este tutorial bastante largo hasta el final. Espero que hayas disfrutado explorando y recreando este sombreador.

Expresiones de gratitud


El videojuego Journey fue desarrollado por Thatgamecompany y publicado por Sony Computer Entertainment . Está disponible para PC ( Epic Store ) y PS4 ( PS Store ). Jiadi Deng

creó los modelos de dunas 3D, los fondos y las opciones de iluminación . Se encontró un modelo 3D del personaje Journey en el foro FacePunch (ahora cerrado).



Paquete de unidad


Si desea recrear este efecto, el paquete completo de Unity está disponible para descargar en Patreon . Tiene todo lo que necesitas, desde sombreadores hasta modelos 3D.

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


All Articles