Unity はさまざまなプラットフォームで動作し、いくつかのケースでは動作に違いが出ます。ほとんどの場合、Unity は違いを隠してくれますが、それでも表面化する場合があります。
垂直方向のテクスチャ座標の表現方法は、Direct3D と OpenGL のプラットフォームで異なります。
ほとんどの場合に影響はありませんが、レンダーテクスチャ に対してレンダリングする場合は影響があります。この場合、Unity は OpenGL 以外でレンダリングするときに意図的にレンダリングを上下逆に反転するので、プラットフォーム間のルールは同じままです。シェーダーで処理する必要のある一般的な例は、イメージエフェクトと、UV空間のレンダリングです。
「非 OpenGL 上で、テクスチャにレンダリングするときに上下逆さまのレンダリング」が起こらないケースは、イメージエフェクト やアンチエイリアスが使用される場合です。この場合、Unty はアンチエイリアスして、後続のイメージエフェクトの処理のために Render Texture に対してレンダリングを“溶かし込みます”。結果となるイメージエフェクトのソーステクスチャは Direct3D/Metal で上下逆には なりません (他の Render Texture と違って)。
もしイメージエフェクトがシンプルである( 1 回にテクスチャを 1 つのみ処理する)場合、Graphics.Blit が処理してくれるため、このことを気にする必要はありません。
しかし、イメージエフェクトで 1 回に Render Texture を複数まとめる場合、多くのケースでそれぞれが異なる垂直方向に出力されます(ただし 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 サンプルプロジェクト でエッジ検出を行っているシーンを確認ください。ここでエッジ検出はシーンのテクスチャとカメラ カメラの深度テクスチャ の両方で使用されています。
特別なエフェクトやツールのためにテクスチャコーディネート (UV) スペースでレンダリングを行うとき、
Direct3D と OpenGL に似たシステムの間でのレンダリングの一貫性、また、スクリーンとテクスチャの間でレンダリングの一貫性を保つために、
シェーダーを調整する必要がある場合があります。
ビルトインのシェーダー変数 _ProjectionParams.x
は、+1 か –1 の値を保持しています。それは、投影が上下さかさまになっているか、いないかを示します。これに基づいて異なることを行いたい場合は、シェーダーでこの値を確認できます。
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 GPU はすべての浮動小数点型 (float
、half
、fixed
) を同様に取り扱い、
すべての計算を完全な32ビット精度を使用して計算しています。
これは、あまり多くのモバイルGPU で行われていません。
そのため、シェーダーをテストして、精度に関する問題を避けることが大切です。
詳細は、 シェーダーのデータタイプと精度 を
参照してください。
同様に、シェーダーコード内のすべてのサンプラーとテクスチャは “low precision” (低精度) に初期設定されています。これは、PC GPU にとっては問題ありませんが、モバイル GPU のなかには sampler2D_half
(半精度テクスチャ) や sampler2D_float
(フル精度テクスチャ) などを使用したほうがよい場合があります。
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 (未定) としてポイントサイズを処理するものもあります。
詳細は shader semantics を参照してください。
Direct3D 9 や OpenGL は NVIDIA の Cg を使用してシェーダーをコンパイルしますが、Direct3D 11 (および Xbox 360)は Microsoft の 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
を追加する必要もあります。現在 Surface Shader の一部のコンパイルパイプラインでは 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