The standard Shader language in Unity is HLSL, and general HLSL data types are supported. However, Unity has some additions to the HLSL types, particularly for better support on mobile platforms.
La mayoría de los cálculos en shaders se llevan a cabo en números de punto flotante (que sería float
en lenguajes de programación regulares como C#). Existen varias variantes de tipos de punto flotante: float
,half
y fixed
(así como las variantes de vector/matriz de ellas, comohalf3
y float4x4
). Estos tipos difieren en precisión (y, en consecuencia, en rendimiento o consumo de energía):
float
Valor de punto flotante de precisión más alta; Generalmente 32 bits (como float
desde lenguajes de programación regulares).
Full float
precision is generally used for world space positions, texture coordinates, or scalar computations involving complex functions such as trigonometry or power/exponentiation.
half
Valor de punto flotante de precisión media; Generalmente 16 bits (rango de –60000 a +60000, con unos 3 dígitos decimales de precisión).
La media precisión es útil para vectores cortos, direcciones, posiciones de espacio de objeto, colores de alto rango dinámico.
fixed
Valor de punto fijo de precisión más baja. Generalmente 11 bits, con un rango de –2.0 a +2.0 y precisión 1 / 256th.
La precisión fija es útil para los colores regulares (normalmente almacenados en texturas regulares) y para realizar operaciones sencillas sobre ellos.
Los enteros (tipo de datos int
) se utilizan a menudo como contadores de bucle o índices de matriz. Para este propósito, generalmente funcionan bien en varias plataformas.
Dependiendo de la plataforma, los tipos enteros podrían no ser compatibles con la GPU. Por ejemplo, las GPU Direct3D 9 y OpenGL ES 2.0 sólo funcionan con datos de punto flotante, y las expresiones enteras de apariencia simple (que implican bits o operaciones lógicas) pueden emularse utilizando instrucciones matemáticas de punto flotante bastante complicadas.
Direct3D 11, OpenGL ES 3, Metal y otras plataformas modernas tienen soporte adecuado para tipos de datos enteros, por lo que el uso de los desplazamientos de bit y el bit masking funciona como se esperaba.
HLSL incorpora tipos vectoriales y matriciales que se crean a partir de los tipos básicos. Por ejemplo, float3
es un vector 3D con componentes .x, .y, .z, yhalf4
es un vector 4D de precisión media con componentes .x, .y, .z, .w. Alternativamente, los vectores pueden indexarse utilizando los componentes .r, .g, .b, .a, que es útil cuando se trabaja en colores.
Los tipos de matriz se construyen de manera similar; Por ejemplo float4x4
es una matriz de transformación 4x4. Tenga en cuenta que algunas plataformas sólo admiten matrices cuadradas, especialmente OpenGL ES 2.0.
Typically you declare textures in your HLSL code as follows:
sampler2D _MainTex;
samplerCUBE _Cubemap;
For mobile platforms, these translate into “low precision samplers”, i.e. the textures are expected to have low precision data in them. If you know your texture contains HDR colors, you might want to use half precision sampler:
sampler2D_half _MainTex;
samplerCUBE_half _Cubemap;
Or if your texture contains full float precision data (e.g. depth texture), use a full precision sampler:
sampler2D_float _MainTex;
samplerCUBE_float _Cubemap;
One complication of float
/half
/fixed
data type usage is that PC GPUs are always high precision. That is, for all the PC (Windows/Mac/Linux) GPUs, it does not matter whether you write float
, half
or fixed
data types in your shaders. They always compute everything in full 32-bit floating point precision.
The half
and fixed
types only become relevant when targeting mobile GPUs, where these types primarily exist for power (and sometimes performance) constraints. Keep in mind that you need to test your shaders on mobile to see whether or not you are running into precision/numerical issues.
Even on mobile GPUs, the different precision support varies between GPU families. Here’s an overview of how each mobile GPU family treats each floating point type (indicated by the number of bits used for it):
Familia GPU | float | half | fixed |
---|---|---|---|
PowerVR Series 6/7 | 32 | 16 | |
PowerVR SGX 5xx | 32 | 16 | 11 |
Qualcomm Adreno 4xx/3xx | 32 | 16 | |
Qualcomm Adreno 2xx | 32 vertex 24 fragment | ||
ARM Mali T6xx/7xx | 32 | 16 | |
ARM Mali 400/450 | 32 vertex 16 fragment | ||
NVIDIA X1 | 32 | 16 | |
NVIDIA K1 | 32 | ||
NVIDIA Tegra 3/4 | 32 | 16 |
Most modern mobile GPUs actually only support either 32-bit numbers (used for float
type) or 16-bit numbers (used for both half
and fixed
types). Some older GPUs have different precisions for vertex shader and fragment shader computations.
Using lower precision can often be faster, either due to improved GPU register allocation, or due to special “fast path” execution units for certain lower-precision math operations. Even when there’s no raw performance advantage, using lower precision often uses less power on the GPU, leading to better battery life.
Una regla general es comenzar con la mitad de precisión para todo, excepto las posiciones y las coordenadas de textura. Sólo aumentar la precisión si la media precisión no es suficiente para algunas partes del cálculo.
El soporte para valores de punto flotante especiales puede ser diferente dependiendo de la familia de GPU (principalmente móvil) que esté ejecutando.
All PC GPUs that support Direct3D 10 support very well-specified IEEE 754 floating point standard. This means that float numbers behave exactly like they do in regular programming languages on the CPU.
Mobile GPUs can have slightly different levels of support. On some, dividing zero by zero might result in a NaN (“not a number”); on others it might result in infinity, zero or any other unspecified value. Make sure to test your shaders on the target device to check they are supported.
GPU vendors have in-depth guides about the performance and capabilities of their GPUs. See these for details: