La Depth Texture (Textura de Profundidad) de la cámara
Shader Level of Detail (Nivel de detalle)

Diferencias especificas de rendering por plataforma

Unity funciona en varias plataformas, y en algunos casos hay diferencias en cómo se comportan las cosas. La mayor parte del tiempo Unity oculta las diferencias, pero a veces pueden surgir problemas.

Render Texture coordinates

Las convenciones de coordenadas verticales de textura difieren entre las plataformas similar a Direct3D y OpenGL:

  • En Direct3D, Metal y consolas, la coordenada es 0 en la parte superior, y aumenta hacia abajo.
  • En OpenGL y OpenGL ES, la coordenada es 0 en la parte inferior, y aumenta hacia arriba.

La mayoría de las veces esto realmente no importa, excepto cuando se renderiza en una Render Texture. En este caso, Unity internamente vuelca el renderizado a convertirlo en una Textura en OpenGL no, de modo que las convenciones coincidan entre las plataformas. Dos casos comunes donde esto se necesita ser manejado en los Shaders son efectos de imagen y rendering en el espacio UV.

Image Effects, RenderTextures al revés y MSAA

Un caso en el que el rendering al revés en una Textura no ocurre es cuando se utilizan Image Effects y anti-aliasing. En este caso, Unity renderiza a la pantalla para obtener anti-aliasing y, a continuación, resuelve el rendering en una RenderTexture para su posterior procesamiento con un efecto de imagen. La fuente resultante de Textura para un efecto de imagen no se *voltea al revés en Direct3D/Metal (a diferencia de todas las demás Render Textures).

Si tu Image Effect es uno simple (procesando una textura a la vez) entonces esto realmente no importa, porque Graphics.Blit se encarga de eso.

Sin embargo, si está procesando más de una RenderTexture junta en su Image Effect, es probable que salgan a diferentes orientaciones verticales (sólo en las plataformas de tipo Direct3D y sólo cuando se utiliza anti-aliasing). Usted necesita “voltear” manualmente la textura de la pantalla al revés en su Vertex Shader, así:

// On non-GL when AA is used, the main Texture and scene depth Texture
// will come out in different vertical orientations.
// So flip sampling of the Texture when that is the case (main Texture
// texel size will have negative Y).

#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0)
        uv.y = 1-uv.y;
#endif

Consulte la Escena de Edge Detection (detección de bordes) en el proyecto de muestras Shader Replacement para obtener un ejemplo de esto. La Edge Detection (detección de bordes) utiliza la textura de la pantalla y la textura de la cámara Depth+Normals texture.

Una situación similar ocurre cuando se utiliza GrabPass. La Textura de rendering resultante no se puede invertir realmente en plataformas no OpenGL. Normalmente, el código Shader que muestra Texturas GrabPass debe utilizar la función ComputeGrabScreenPos desde UnityCG include file.

Rendering en espacio UV

When rendering in Texture coordinate (UV) space for special effects or tools, you might need to adjust your Shaders so that the rendering is consistent between D3D-like and OpenGL-like systems, and between rendering into the screen vs. rendering into a Texture. The built-in variable _ProjectionParams.x contains a +1 or –1 value which indicates whether projection has been flipped upside down or not. You can check this value in your Shaders if you need to do different things based on this.

float4 vert(float2 uv : TEXCOORD0) : SV_POSITION
{
    float4 pos;
    pos.xy = uv;
    // we're rendering with upside-down flipped projection,
    // so flip the vertical UV coordinate too
    if (_ProjectionParams.x < 0)
        pos.y = 1 - pos.y;
    pos.z = 0;
    pos.w = 1;
    return pos;
}

Diferencias de coordenadas en el clip space

Al igual que con las coordenadas de textura, las coordenadas del espacio de clip (también conocidas como coordenadas de espacio de post-proyección) difieren entre las plataformas similar a Direct3D y OpenGL:

  • En Direct3D, Metal y consolas, la profundidad del espacio de clip va de 0.0 en el plano cercano, a +1.0 en el plano lejano.
  • En OpenGL y OpenGL ES, la profundidad del espacio de clip va de –1.0 en el plano cercano, a +1.0 en el plano lejano.

Dentro del código Shader, puede usar UNITY_NEAR_CLIP_VALUE macro para obtener el valor del plano cercano basado en la plataforma.

Dentro del código script, se puede utilizar GL.GetGPUProjectionMatrix para convertir del sistema de coordenadas de Unity (que sigue las convenciones de OpenGL) a lo que la plataforma espera.

Precisión de cálculos Shader

PC GPUs treat all floating point types (float, half and fixed) as the same, and do all calculations using full 32 bit precision. Many mobile GPUs do not do this, so make sure to test your Shaders on the target platform to avoid precision issues. See the data types and precision page for details.

Declaraciones Const en Shaders

En HLSL, const tiene mucho el mismo significado que en C # y C ++, ya que la variable declarada es de sólo lectura dentro de su ámbito, pero puede ser inicializada de cualquier manera.

En GLSL, sin embargo, const significa que la variable es efectivamente una constante de tiempo de compilación, por lo que debe ser inicializada con constantes de tiempo de compilación (ya sea valores literales o cálculos en otras constantes).

Lo mejor es seguir la semántica GLSL y sólo declarar una variable como const cuando es verdaderamente invariante, y evitar la inicialización de una variableconst con algunos otros valores mutables e.g Como una variable local en una función. Esto funcionará perfectamente en HLSL y evitará errores confusos en sólo algunas plataformas.

Declaraciones Const en Shaders

En HLSL, const tiene bastante el mismo significado que en C # y C ++, de modo que la variable declarada es de sólo lectura dentro de su ámbito, pero puede ser inicializada de cualquier manera.

En GLSL, sin embargo, const significa que la variable es efectivamente una constante de tiempo de compilación, por lo que debe ser inicializada con constantes de tiempo de compilación - ya sea valores literales, o cálculos en otras constantes.

Lo mejor es seguir la semántica GLSL y sólo declarar una variable como const cuando es verdaderamente invariante, y evitar la inicialización de una variableconst con algunos otros valores mutables e.g Como una variable local en una función. Esto funcionará perfectamente en HLSL y evitará errores confusos en sólo algunas plataformas.

Semánticas utilizada por Shaders

Para que Shaders funcione en todas las plataformas, algunos valores especiales Shader deberían usar estas semántica:

  • Posición output (espacio clip) del Vertex Shader: SV_POSITION. A veces el Shader utiliza la semántica POSITION para eso, pero esto no funciona en Sony PS4 o cuando se utiliza tessellation.
  • Color output del Fragment shader: SV_Target. A veces el Shader utiliza COLOR oCOLOR0 para eso, pero nuevamente esto no funciona en PS4.

Al renderizar meshes como Puntos, asegúrese de emitir la semántica PSIZE desde el vertex shader (por ejemplo, configure en 1). Algunas plataformas, como OpenGL ES o Metal, tratan el tamaño del punto como “indefinido” cuando no se escribe en Shader.

Mire la página de semánticas Shader para más detalles.

El compilador shader Direct3D 9 / 11 es más estricto acerca de la sintaxis

Las plataformas Direct3D utilizan el compilador HLSL Shader de Microsoft. El compilador HLSL es más estricto que otros compiladores sobre varios errores sutiles de Shader. Por ejemplo, no acepta valores de salida de función que no se inicializan correctamente.

Los lugares más comunes que podría encontrarse con esto son:

  • Un modificador de vértice Surface Shader que tiene un parámetro “out”. Asegúrese de inicializar la salida de esta manera:
      void vert (inout appdata_full v, out Input o) 
      {
        **UNITY_INITIALIZE_OUTPUT(Input,o);**
        // ...
      }
  • **Valores parcialmente inicializados. ** Por ejemplo, una función devuelve float4, pero el código sólo configura los valores .xyz de la misma. Asegúrese de establecer todos los valores o cambie a float3 si sólo necesita tres valores.
  • **Usando tex2D en el Vertex Shader. ** Esto no es válido, ya que los derivados UV no existen en el Vertex Shader. Necesita probar un nivel mip explícito en su lugar; Por ejemplo, use tex2Dlod (tex, float4(uv,0,0)). También debe agregar #pragma target 3.0, ya quetex2Dlod es una característica de Shader 3.0.

Sintaxis DirectX 11 HLSL y Surface Shaders

Algunas partes del pipeline de compilación Surface Shader no entienden la sintaxis HLSL específica de DX11. Si está utilizando funciones HLSL como StructuredBuffers, RWTextures y otra sintaxis no DX9, envuélvalas en una macro de preprocesador sólo con DX11:

#ifdef SHADER_API_D3D11
// DX11-specific code, e.g.
StructuredBuffer<float4> myColors;
RWTexture2D<float4> myRandomWriteTexture;
#endif

Utilizando Shader framebuffer fetch

Algunas GPUs (sobre todo las basadas en PowerVR en iOS) le permiten hacer una forma de mezcla programable proporcionando el color de fragmento actual como entrada al Fragment Shader (mire EXT_shader_framebuffer_fetch).

Es posible escribir Shaders en Unity que utilizan la funcionalidad framebuffer fetch. Cuando escriba HLSL/Cg Fragment Shader, simplemente utilice el argumento de color inout en este. Por ejemplo:

CGPROGRAM
// only compile Shader for platforms that can potentially
// do it (currently gles,gles3,metal)
#pragma only_renderers framebufferfetch

void frag (v2f i, inout half4 ocol : SV_Target)
{
    // ocol can be read (current framebuffer color)
    // and written into (will change color to that one)
    // ...
}   
ENDCG

Revertir-float Z buffer

La dirección del buffer de profundidad se ha invertido en las API gráficas modernas (DX11 / 12, PS4, XboxOne, Metal). En estas APIs el buffer Z contiene 1,0 en el plano cercano y 0,0 en el plano lejano. Esto, combinado con el buffer de profundidad de punto flotante, tiene el efecto de aumentar masivamente la precisión del buffer de profundidad (menos combate Z y mejores sombras, especialmente cuando se usan planos cercanos pequeños y grandes planos lejanos).

Prácticamente, esto significa pocos cambios en el nivel Shader:

  • UNITY_REVERSED_Z se define
  • El rango del Clip space es [near, 0]
  • El rango de textura _CameraDepthTexture es [1,0]
  • El bias Z se niega antes de ser aplicado

Las siguientes macros/funciones abstraen la diferencia:

Linear01Depth(float z)
LinearEyeDepth(float z)
UNITY_CALC_FOG_FACTOR(coord)

Si está recuperando manualmente el valor del búfer Z, es posible que desee hacer algo como:

float z = tex2D(_CameraDepthTexture, uv);
#if defined(UNITY_REVERSED_Z)
    z = 1.0f - z;
#endif

Para la profundidad en el espacio clip (clip space) usted puede utilizar la siguiente macro:

    float clipSpaceRange01 = UNITY_Z_0_FAR_FROM_CLIPSPACE(rawClipSpace);

Note: Por favor tenga en cuenta que esta macro no altera el clip space en plataformas OpenGL/ES; se mantiene aquí [-near (cerca), far (lejos)].

Finalmente, si usted está utilizando el plugin de código nativo de rendering, usted necesita negar el bias de profundidad en plataformas que coincidan también.

La Depth Texture (Textura de Profundidad) de la cámara
Shader Level of Detail (Nivel de detalle)