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.
Las convenciones de coordenadas verticales de textura difieren entre las plataformas similar a Direct3D y OpenGL:
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.
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.
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;
}
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:
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.
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.
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.
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.
Para que Shaders funcione en todas las plataformas, algunos valores especiales Shader deberían usar estas semántica:
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.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.
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:
void vert (inout appdata_full v, out Input o)
{
**UNITY_INITIALIZE_OUTPUT(Input,o);**
// ...
}
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.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.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
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
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:
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.