ユーザ用ツール

サイト用ツール


opengl:lefthand

文書の過去の版を表示しています。


OpenGL で Direct3D 座標系を使う (またはその逆)

GPU が Shader によりプログラマブルになったことで、 レンダリングに関する多くのルールをプログラマが決められるようになりました。

ライティングの演算とマテリアルのバリエーションから、アニメーションや変形といったジオメトリまで、 ShaderModel が進むにつれて固定機能の割合が減少しています。 特に最新の ComputeShader では Rendering Pipeline からも開放されて、 使い方は完全にプログラマに委ねられています。

従来は上位 API で定義されていた座標系も、Shader 世代の GPU ではあまりい意味を持っていません。 OpenGL で Direct3D 座標系を使うこともできますし、その逆も可能です。

昔から Windows を使用しており、Direct3D のコードやデータを溜め込んでいる場合は D3D 座標系を使いたいかもしれません。 逆に DCC ツールとの互換性や、Mobile Device での動作を考えると OpenGL に合わせた方が何かと都合が良くなります。

  • 座標系を自由に決められるメリット
    • API に依存しないで幅広いプラットフォームへの移植性を高めることができる
    • 開発したライブラリを共有することができる
    • データの互換性、リソース資産の蓄積
    • DCC ツールの座標系に合わせて開発できる (GL座標系が多いし Z-Up のものもある)
  • 必要なこと
    • 数値演算系のライブラリやシェーダーコードは独自に用意する必要がある。
      • サンプルのコードをそのまま流用できない。

OpenGL と Direct3D の座標系の違い

Direct3D OpenGL Programmable
3D 座標系 LH 左手系 RH 右手系 API 依存なし Cライブラリと Shader が自由に決定可能
Clip (device) 座標系 1.0 >= Z >= 0.0 1.0 >= Z >= -1.0 API 依存あり Driver 内部に組み込まれているため Shader で変換が必要
Screen 座標系 左上原点 左下原点 API 依存あり API で決められているため、呼び出し時に変換が必要。Viewport/Scissor 等
UV 座標系 左上原点 左下原点 API 依存なし 自由に決定可能。データは上下が反転しているが API 上は違いがない。
Tangent Space 座標系 未定義(右手系) 未定義(右手系) API 依存なし テクスチャデータと Shader で好きな様に定義できるが、RH のデータが多い

3D 座標系

D3D (LH) と OpenGL (RH) では、Y-Up+, X-Right+ のとき、Z 軸の向きが反対になります。

  • OpenGL (RH) : 奥に向かって Z値が小さくなる (カメラが原点 0 なら遠くは負の座標を持つ)
  • Direct3D (LH) : 奥に向かって Z値が大きくなる (カメラが原点 0 なら遠くは正の座標を持つ)

Maya などの DCC ツールは OpenGL で作られているため、OpenGL と同じ RH 座標系が用いられており馴染みがあります。

ハードウエア的には最終的に depth buffer には 0~1.0 の値が格納されるため、符号の向きは D3D の LH 座標系に一致します。 OpenGL は Rendering Pipeline の内部で Z の符号反転が行われていますが、 Shader によって直接 GPU リソースを読み書きできるようになったために、場合に応じてこの両者を使い分ける必要があります。

例えば Shadow Map を生成する場合は OpenGL の RH 座標系の Projection Matrix を用いますが、 Shadow Map を Sampling する場合は、Shadow Matrix で Z の符号を反転して Z 範囲を -1.0~1.0 から D3D と同じ 0~1.0 に変更しなければなりません。 Direct3D LH 座標系では Mapping 生成時と Sampling 時の Matrix は、uv 範囲を変更するだけで済みます。 とはいえ Matrix に畳み込めるため正直どちらでも構わないのが実情です。

基本的には Z を変更するだけですが、 Projection Matrix の生成や Frustum の Culling で違いが生じます。 特に Frustum と AABB の位置関係は注意。

座標だけでなく Animation の回転も Z 成分が逆になります。

Clip (Device) 座標系

x/w y/w z/w
OpenGL -1.0 ~ 1.0 -1.0 ~ 1.0 -1.0 ~ 1.0
Direct3D -1.0 ~ 1.0 -1.0 ~ 1.0 0.0 ~ 1.0

Vertex Shader の出力が Clip 座標系になります。 上の表は w 除算後のもので、実際は -w~w を出力します。

z の範囲が異なるため、Direct3D と OpenGL では Projection Matrix に互換性がありません。 プラットフォーム依存をナクするためには、Projection Matrix を事前に変換するか、または Shader 内で座標調整が必要です。

Projection Matrix を変換する場合は下記のようなります。

struct Matrix44 {
    float  _11, _12, _13, _14;
    float  _21, _22, _23, _24;
    float  _31, _32, _33, _34;
    float  _41, _42, _43, _44;
};
 
// Direct3D Projection Matrix to OpenGL
void D3DtoGLProjection( Matrix44& mat )
{
    _13= 2.0f * _13 - _14;
    _23= 2.0f * _23 - _24;
    _33= 2.0f * _33 - _34;
    _34= 2.0f * _43 - _44;
}
 
// OpenGL Projection Matrix to Direct3D
void GLtoD3DProjection( Matrix44& mat )
{
    _13= (_13 + _14) * 0.5f;
    _23= (_23 + _24) * 0.5f;
    _33= (_33 + _34) * 0.5f;
    _43= (_43 + _44) * 0.5f;
}

↑またこの変換は、OpenGL で Shadow map を用いる場合にも必要になります。 Shadow Map sampling 時は Z 範囲が 0~1.0 になるためです。

Shader 側で調整する場合は、Projection Matrix の相互変換は不要です。

Shader で Clip 座標系の違いを吸収する場合は下記の通り。 OpenGL の GLSL で Direct3D 用の Projection Matrix を利用した場合の例です。

// GLSL
uniform mat4   PView;
in vec3        POSITION;
 
main()
{
    vec4   opos= vec4( POSITION.xyz, 1.0 ) * PView;
 
    opos.z= 2.0 * opos.z - opos.w;   // ← 追加コード
 
    gl_Position= opos;
}

UV 座標系

API 上は制限がなく、あくまでデータがどちらのルールで作られているか次第になります。

D3D と OpenGL では原点位置が異なりますが、外部で作られたデータを用いる場合はプログラム的には特に何も手を加える必要がありません。

  • D3D : v 原点を上とみなす。メモリには 上から下に向かって画像が格納されているとみなす。
  • GL : v 原点を下とみなす。メモリには 下から上に向かって画像が格納されているとみなす。

つまり D3D も GL も、v 座標が 0 の場合は Texture Memory の先頭からデータを読み込むことになるからです。 D3D と GL は uv 座標の v だけでなく Texture Image の画像自体も上下反転しています。

ただし、動的に Rendering して生成した Texture の場合は GL と D3D は上下反転しているので要注意。 次の Screen 座標系とも関係しますが、Viewport や Scissor 、Framebuffer は左下原点とみなしてレンダリングが行われるからです。

opengl/lefthand.1397574574.txt.gz · 最終更新: 2014/04/16 00:09 by oga

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki