Unity はさまざまなプラットフォームで動作し、いくつかのケースでは動作に違いが出ます。ほとんどの場合、Unity は違いを隠してくれますが、それでも表面化する場合があります。
垂直方向のテクスチャ座標の表現方法は、Direct3D と OpenGL のプラットフォームで異なります。
ほとんどの場合に影響はありませんが、レンダーテクスチャ に対してレンダリングする場合は影響があります。この場合、Unity は OpenGL 以外でレンダリングするときに意図的にレンダリングを上下逆に反転するので、プラットフォーム間のルールは同じままです。シェーダーで処理する必要のある一般的な例は、イメージエフェクトと、UV空間のレンダリングです。
「非 OpenGL 上で、テクスチャにレンダリングするときに上下逆さまのレンダリング」が起こらないケースは、イメージエフェクト やアンチエイリアスが使用される場合です。この場合、Unty はアンチエイリアスして、後続のイメージエフェクトの処理のためにレンダーテクスチャに対してレンダリングを“溶かし込みます”。結果となるイメージエフェクトのソーステクスチャは Direct3D/Metal で上下逆には なりません (他のレンダーテクスチャと違って)。
もしイメージエフェクトがシンプルである( 1 回にテクスチャを 1 つのみ処理する)場合、Graphics.Blit が処理してくれるため、このことを気にする必要はありません。
しかし、イメージエフェクトで 1 回にレンダーテクスチャを複数まとめる場合、多くのケースでそれぞれが異なる垂直方向に出力されます(ただし Direct3D のようなプラットフォームであり、かつアンチエイリアスが使用されている場合のみ)。頂点シェーダーで手動で画面テクスチャを「上下逆さま」にする必要があります。
// 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
Shader Replacement サンプルプロジェクト でエッジ検出を行っているシーンを確認ください。ここでエッジ検出はシーンのテクスチャとカメラの DepthNormals テクスチャ の両方で使用されています。
同様の状況が GrabPass を使用するときに発生します。処理されたレンダーテクスチャは、OpenGL プラットフォーム以外では、実際には上下逆でない場合があります。通常は、GrabPass テクスチャをサンプリングするシェーダーコードは UnityCG の include ファイル から ComputeGrabScreenPos
関数を使用します。
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;
}
テクスチャ座標と同じように、クリップ空間座標 (post-projection 空間座標としても知られています) は、Direct3D のようなプラットフォームと OpenGL のようなプラットフォームで異なります。
シェーダーコード内で、UNITY_NEAR_CLIP_VALUE
マクロ を使用して、プラットフォームに基づいた近平面を取得できます。
スクリプトコード内で GL.GetGPUProjectionMatrix を使用して、 Unity の座標システム (OpenGL に基づく) からプラットフォームに合わせたものに転換できます。
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.
HLSL において、 const
の意味は、宣言された変数はそのスコープ内で読み込みだけ可能で、ただし、いずれにせよ初期化される、などという面で C# や C++ での const とほとんど同様です。
ただし、GLSL では const
は変数は効果的にコンパイル時の定数であり、そのため、それはコンパイル時の定数 - リテラル値か他の定数に関する計算、のいずれかで初期化される必要があります。
GLSL セマンティクスに従い、本当にバリアントでない場合は変数を const
として宣言だけ行い、それを機能内のローカル変数のような変更可能な他の値とともに初期化するのを避けるのが得策です。この方法は、HLSL でもよく機能し、いくつかのプラットフォームでのみ発生する混乱によるエラーを避けるのに有効です。
HLSL において、 const
の意味は、宣言された変数はそのスコープ内で読み込みだけ可能で、ただし、いずれにせよ初期化される、などという面で C# や C++ での const とほとんど同様です。
ただし、GLSL では const
は変数は効果的にコンパイル時の定数であり、そのため、それはコンパイル時の定数 - リテラル値か他の定数に関する計算、のいずれかで初期化される必要があります。
GLSL セマンティクスに従い、本当にバリアントでない場合は変数を const
として宣言だけ行い、それを機能内のローカル変数のような変更可能な他の値とともに初期化するのを避けるのが得策です。この方法は、HLSL でもよく機能し、いくつかのプラットフォームでのみ発生する混乱によるエラーを避けるのに有効です。
シェーダーをすべてのプラットフォーム上で動作させるために、いくつかの特殊なシェーダーの値は以下のようなセマンティクスを使用するべきです。
SV_POSITION
。シェーダーでは ‘POSITION’ セマンティクスを使用することもありますが、これは Sony PS4 上の場合やテッセレーションが使用されている場合は動作しません。SV_Target
。シェーダーでは COLOR
や COLOR0
を使用することもありますが、こちらも PS4 上では動作しません。Point としてメッシュをレンダリングするとき、頂点シェーダーから PSIZE
出力セマンティクスを出力することを確認してください(例えば 1 に設定)。プラットフォーム(例えば OpenGL ES や Metal)によっては、シェーダーから、またはシェーダーへの書き込みでない場合、 undefined (未定) としてポイントサイズを処理するものもあります。
詳細は シェーダーセマンティクス を参照してください。
Direct3D プラットフォームは Microsoft のHLSL シェーダーコンパイラーを使用します。HLSL コンパイラーは他のコンパイラーに比べると、さまざまな微細なシェーダーエラーに関してより厳格です。例えば、HLSL コンパイラーは適切に初期化されていない関数の出力値を受けいれません。
この現象にもっとも頻繁に遭遇するのは以下のような場面です。
void vert (inout appdata_full v, out Input o)
{
**UNITY_INITIALIZE_OUTPUT(Input,o);**
// ...
}
float4
を戻す関数があるが、コードが .xyz 値しか設定しない場合。すべての値を設定できるようにするか、float3
でよいのならばそのように変更します。tex2D
を頂点シェーダーで使用するとき。** 頂点シェーダーでは UV デリバティブが存在しないため無効です。その代わりに明示的にミップレベルのサンプリングを追加する必要があり、例えば tex2Dlod (tex、float4(uv, 0, 0))
を使用します。tex2Dlod
はシェーダーモデル 3.0 機能なので #pragma target 3.0
を追加する必要もあります。現在 サーフェスシェーダー の一部のコンパイルパイプラインでは DirectX 11 特有の HLSL 文法が解釈されません。もし StructuredBuffersや RWTextures、その他の非 DirectX 9 文法の HLSL 機能を使用する場合、DirectX 11 専用のプリプロセッサーマクロにラッピングする必要があります。
#ifdef SHADER_API_D3D11
// DX11-specific code, e.g.
StructuredBuffer<float4> myColors;
RWTexture2D<float4> myRandomWriteTexture;
#endif
一部の GPU(iOS 上で最も顕著な PowerVR ベースのもの) では、フラグメントシェーダーへのインプットとして現在のフラグメントカラーを渡して、プログラマブルブレンディングの形にすることができます。(EXT_shader_framebuffer_fetch を参照してください)。
framebuffer fetch 機能を使うUnityでシェーダーをプログラムすることが可能です。 HLSL/Cg でフラグメントシェーダーをプログラミングするとき、color の引数の inout
を使用するだけです。以下の例を参照してください。
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
デプスバッファの方向は現在一般的に使用されている多くのグラフィックス APIs (DX11/12, PS4, XboxOne, Metal) で反転されます。これらの APIs では、近距離では Z バッファの値は 1.0、遠距離では 0.0 です。これにより、浮動小数点のデプスバッファと組み合わせて、デプスバッファの精度を大幅に上げる効果があります (特に小さな近距離面と大きな遠距離面を使用している場合は、Z fighting が少なくなりシャドウの品質も良くなります)。
実際的には、シェーダーレベルにほとんど変化がないことを意味します。
以下のマクロ/関数は違いを表しています。
Linear01Depth(float z)
LinearEyeDepth(float z)
UNITY_CALC_FOG_FACTOR(coord)
Z バッファの値を手動でフェッチする場合は、以下のようにできます。
float z = tex2D(_CameraDepthTexture, uv);
#if defined(UNITY_REVERSED_Z)
z = 1.0f - z;
#endif
クリップスペースデプスに関しては、以下のマクロを使用できます。
float clipSpaceRange01 = UNITY_Z_0_FAR_FROM_CLIPSPACE(rawClipSpace);
注意 このマクロは、OpenGL/ES プラットフォームではクリップスペースを変えません。つまり、OpenGL/ES プラットフォームでは [-near, far] のままです。
ネイティブコードレンダリングプラグインを使う場合は、マッチングするプラットフォームでもデプスバイアスを無効にする必要があります。