The standard Shader language in Unity is HLSL, and general HLSL data types are supported. However, Unity handles some data types differently from HLSL, particularly to provide better support on mobile platforms.
Shaders carry out the majority of calculations using floating point numbers (which are float
in regular programming languages like C#). In Unity’s implementation of HLSL, the scalar floating point data types are float
, half
, and fixed
. These data types differ in precision and, consequently, performance or power usage. There are also several related data types for vectors and matrices such as half3
and float4x4
.
float
This is the highest precision floating point data type. On most platforms, float
values are 32 bits like in regular programming languages.
Full float
precision is generally useful for world space positions, texture coordinates, or scalar calculations that involve complex functions such as trigonometry or power/exponentiation. If you use lower precision floating point data types for these purposes, it can cause precision-related artifacts. For example with texture coordinates, a half
doesn’t have enough precision to accurately represent 1-texel offsets of larger textures.
half
This is a medium precision floating point data type. On platforms that support half
values, they are generally 16 bits. On other platforms, this becomes float
.
half
values have a smaller range and precision than float
values.
Half precision is useful to get better shader performance for values that don’t require high precision such as short vectors, directions, object space positions, and high dynamic range colors.
fixed
This is only supported by the OpenGL ES 2.0 Graphics API. On other APIs it becomes the lowest supported precision (half
or float
).
This is the lowest precision fixed point value and is generally 11 bits. fixed
values range from –2.0 to +2.0 and have a precision of 1/256.
fixed 精度は、標準カラー (一般的に標準テクスチャに保管されるので) とそれらの単純な制御に使用されます。
Unity’s shader compiler ignores floating point number suffixes from HLSL. Floating point numbers with a suffix therefore all become float
.
This code shows a possible negative impact of numbers with the h
suffix in Unity:
half3 packedNormal = ...;
half3 normal = packedNormal * 2.0h - 1.0h;
Since the h
suffix is ignored, the shader compiler generates code that executes these steps:
1. Calculate an intermediary normal
value in high precision (float3
)
2. Convert the intermediary value to half3
.
This reduces your shader’s performance.
This code is more efficient because it only uses half
values in its calculations:
half3 packedNormal = ...;
half3 normal = packedNormal * half(2.0) - half(1.0);
整数 (int
データ型) はしばしばループカウンターや配列のインデックスとして使用されます。そのため、通常は、さまざまなプラットフォームで問題なく使用できます。
プラットフォームによっては、整数型がGPU にサポートされていないことがあります。例えば、Direct3D 9 と OpenGL ES 2.0 GPU は、浮動小数点データ上でのみ作動します。そのため、シンプルに見える整数表現 (ビットやロジカルオペレーションに関する) は、ある程度複雑な浮動小数点の数式を使用してエミュレーションされていることがあります。
Direct3D 11、OpenGL ES 3、Metal やその他の現段階で使用されている多くのプラットフォームでは、整数のデータ型は適切にサポートされています。そのため、ビットシフトやビットマスクを使用しても、想定通り作動します。
HLSL には、基本の型から作成されたビルトインのベクトル型と行列型があります。例えば、float3
は .x、.y、.z コンポーネントを含む 3D ベクトルです。また、half4
は、中精度の 4D ベクトル (.x、.y、.z、.w コンポーネントを含む) です。あるいは、カラーを使用するときに有用な .r、.g、.b、.a コンポーネントを使用して、ベクトルをインデックス化することができます。
行列型も同様に作成されます。例えば、float4x4
は 4x4 変換行列です。プラットフォームによっては、OpenGL ES 2.0 でよく知られているように正方行列だけしかサポートしないものもあるので注意してください。
多くの場合、HLSL コードではテクスチャを以下のように宣言します。
sampler2D _MainTex;
samplerCUBE _Cubemap;
モバイルプラットフォームでは、これらは “低精度サンプラー” に変換されます。つまり、テクスチャには低精度のデータが含まれることが期待されます。 Unity プロジェクト全体のデフォルトのサンプラー精度は、Player Settings の Shader precision model ドロップダウンで変更することができます。 テクスチャに HDR カラーが含まれることが分かっている場合は、半精度サンプラーを使用するとよいでしょう。
sampler2D_half _MainTex;
samplerCUBE_half _Cubemap;
または、テクスチャがフルの float 精度のデータを含む場合 (例えば 深度テクスチャ)、最高精度を使用します。
sampler2D_float _MainTex;
samplerCUBE_float _Cubemap;
float
/half
/fixed
データ型を使用する上での厄介な問題の 1 つは、PC の GPU は 常に 高精度であるということです。つまり、すべての PC の (Windows/Mac/Linux) GPU にとって、シェーダーで float
、half
、fixed
の中のどのデータ型を使用するかは重要ではありません。PC の GPU は常にフルの 32 ビット浮動小数点精度ですべてを計算します。
half
と fixed
型は、モバイルの GPU をターゲットとする場合にのみ意味があります。これらのタイプは主に電力 (および、場合によってはパフォーマンス) の制約のために存在します。そのため、精度/数値の問題があるかどうか確認するためには、モバイルでシェーダをテストする必要があります。
モバイルの GPU であっても、GPU ファミリーによって精度のサポートにさまざまな違いがあります。以下は、各モバイル GPU ファミリーが各浮動小数点型を処理する方法の概要です (消費されるビット数で表示)。
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 (頂点)、24 (フラグメント) | ||
ARM Mali T6xx/7xx | 32 | 16 | |
ARM Mali 400/450 | 32 (頂点)、16 (フラグメント) | ||
NVIDIA X1 | 32 | 16 | |
NVIDIA K1 | 32 | ||
NVIDIA Tegra 3/4 | 32 | 16 |
最近良く使われるモバイルのほとんどの GPU は、実際には 32 ビット数 (float
型に使用) または 16 ビット数 (half
と fixed
型の両方に使用) のいずれかしかサポートしていません。一部の古い GPU では、頂点シェーダーとフラグメントシェーダーの計算で精度が異なります。
低精度を使用すると高速になることがよくあります。それは、GPU レジスターの割り当てが改善されたため、または特定の低精度数学演算用の特別な “高速パス” 実行ユニットによるためです。直接のパフォーマンス上の利点がない場合でも、低い精度を使用すると GPU の消費電力が少なくなるため、バッテリー寿命が長くなります。
一般的に推奨される使用法は、位置とテクスチャ座標を除くすべてに half (中精度) を使用します。計算で精度にいくらか不十分な個所がある場合にのみ、精度を上げます。
計算に使用される精度が異なる GPU グループ (ほとんどモバイル) によって異なるように、特別な浮動小数点値のサポートも異なります。
Direct3D 10 をサポートするすべての PC の GPU は、非常にうまく指定された IEEE 754 浮動小数点規格をサポートします。つまり、浮動小数点数は、CPU 上の通常のプログラミング言語の場合とまったく同じように動作します。
モバイル GPU のサポートレベルは、わずかに異なります。例えば、ゼロをゼロで除算すると NaN (“数字ではない”) になるモバイルがありますが、無限大、ゼロ、または、その他の特定できない値になるものもあります。ターゲットデバイスでシェーダーをテストして、それらのサポートを確認してください。
GPU には、そのパフォーマンスや能力に関してベンダーによる詳しいガイドがあります。詳細は、それらを参照してください。