APIに依存しないブリッジ(OpenGL / D3D / Whateverなど)。それらをどのように使用しますか。賛否両論[非公開]


12

あなたは3Dエンジンを作っています。最高のマルチプラットフォームの世界が必要です。突然、WindowsマシンでDirect3Dを使用し、OSX / LinuxでOpenGLを使用する場合、両方のサポートされている機能を最小公分母に犠牲にする必要があることに気づきます。

3つのOSでOpenGLを使用する人もいます。すべてが良いです。次に、グラフィックスAPIバックエンドを任天堂のGXに移植する必要があります。また、PS3とXbox360のパスを作成する必要があります。

職業はなんですか?それ自体で最も一般的な分母ではない独自のAPIを設計し、各プラットフォーム用にバックエンド実装を記述しますか、それとも独自のブランチである各プラットフォーム用に記述しますか?

独自のAPIを設計することを選択した場合、ブリッジパターンまたは独自のブードゥー教を使用しますか?あなたがすべてを理解し、キッチンシンクアプローチが停止する必要があることに気づいたとき、狂気はどこで止まり、基本的にはブランチとして各プラットフォームに個別のエンジンがあります。または、すべてにこだわってキッチンシンクを行い、各プラットフォームのバックエンドモジュール専門分野でプラットフォーム固有の仕様を保持します。

回答:


9

私は最小公分母アプローチのファンではありません。それを行うと、機能が損なわれ、パフォーマンスが低下する可能性があります。

代わりに、私が過去にやったことは、ライブラリで少し高いレベルの機能を提供することです。そのライブラリは(ほとんど)APIに依存せず、どこでも使用できますが、ライブラリの実装はプラットフォーム/グラフィックスのバックエンドが異なると完全に異なります。したがって、たとえば、SetStateX()関数を使用する代わりに、RenderMesh()やCreateRenderTarget()などの高度な関数を使用できます。

新しいプラットフォームに移行するときは、本当に薄いレイヤーよりも多くの作業が必要になりますが、そのプラットフォームに最適な方法で物事を実装できるので、それは完全に価値があります。ネイティブでユニークな機能の利点。

もう1つ:カプセル化をわずかに破ることを恐れないでください。特定の機能を備えたプラットフォームにいることを知り、それらを使用できないことに不満を感じることはありません。高レベルのコードがプラットフォームを利用できるように、何らかの種類のバックドアを残すことは非常に便利です(たとえば、D3DデバイスまたはOpenGLコンテキストを取得できるなど)。


3
あなたは私が言いたかったことを言ったと思う、より良いだけだ。
AShelly

6

私が言えることは、Ogre3Dを見るだけです。C ++、オープンソース(現在はMITライセンス)で記述されており、すべての主要なプラットフォームですぐに実行できます。レンダリングAPIを抽象化し、2、3の設定でDirectXからOpenGLに切り替えることができます。ただし、DirectXとOpenGLの機能セットの違いについては、特定の機能をサポートする、またはサポートしないとは言えません。

Runic GamesのTorchlightはOgreを使用して書かれており、MacとPCでプレイしましたが、両方で非常によく動作します。


2
Ogreアプローチの場合は+1。私はそれについて知っています、いくつかのコードを読んでいます。アプローチについての個人的な話や、そのような状況で他の人が何をするかを聞くことにもっと興味がありました。
キーフレーム

2
ありがとう!まあ、私はほとんどOgreがやったようにそれをするでしょう。私は多くのクロスプラットフォーム開発でインターフェース/ファクトリーアプローチを使用しましたが、実際にはそうでなければどうするかわかりません。ただし、複数のプラットフォームで同時に作業する必要があると思います。Windowsですべてをコーディングしようとせずに、たとえばMacに移植しようとしないでください。
ケーシー

はい!私は自分のクロスAPIラッパーを展開することに本当にうんざりしていたので、Ogreを使い始めました。振り返っていない。:)
jacmoe

1

私はグラフィックスに対してこれを行っていませんが、クロスプラットフォームオーディオツールキット(PC / XBOX / PS2)を作成しました。オプションとしてプラットフォーム固有の機能だけでなく、最小公分母機能を備えた独自のAPIを作成するルートに進みました。学んだ教訓は次のとおりです。

重要なのは、各プラットフォームのコア機能をカプセル化し、成長を可能にする処理パスを定義することです。これを行うには、各プラットフォームの低レベルAPIを実際に理解して、適切な抽象化を識別できるようにする必要があります。最も能力の高いプラットフォームの高度な機能へのアクセスを提供しながら、チェーンが最も能力の低いプラットフォームで機能することを確認してください。これを正しく行うための作業を行うと、後で多くの労力を節約できます。

オーディオの場合、チェーンは次のようなものでした SoundSources -> [Decoders] -> Buffers -> [3D Positioner] ->[Effects] -> Playersでした。

グラフィックスのために、それであってもよいですModel -> Shapes -> Positioner -> Texturer -> [Lighting] -> [Particle Effects] -> Renderer(これはおそらく完全に間違ったセットです、私はグラフィックの男ではありません)。

コアオブジェクトを処理するフロントエンドAPIと、APIを低レベル機能にマップするプラットフォーム固有のバックエンドを記述します。各機能にベストエフォートを提供します。たとえば、PCとXBOXでは、サウンドチップのHRTF機能を使用して3Dオーディオポジショニングが行われ、PS2は単純なパンとフェードを使用しました。グラフィックエンジンは、照明で同様のことを行う場合があります。

プラットフォームに中立なコードを使用して、できるだけ多くのフロントエンドを実装します。リバーブオブジェクトをサウンドオブジェクトにアタッチするコード、またはシェイプオブジェクトにテクスチャアセットをアタッチするコードは、アクティブなオブジェクトを反復処理するコードと完全に共通する必要があります。一方、低レベルのオブジェクトは、共通のインターフェースを除き、完全にプラットフォーム固有である場合があります。

APIまたは構成ファイルで、ユーザーがプラットフォーム固有のオプションを指定できることを確認してください。プラットフォーム固有のコードを構成ファイルに保持することで、プラットフォーム固有のコードをゲームレベルにプッシュしないようにしました。あるプラットフォームの構成ファイルは「effect:SuperDuperParticleGenerator」を指定し、別のプラットフォームは「effect:SoftGlow」

プラットフォームを確実に並行して開発します。プラットフォーム固有のインターフェイスが適切に定義され、それ自体でテスト可能であることを確認してください。これにより、多くの「プラットフォームレベルまたはAPIレベル」が回避されます。デバッグ時の問題。


0

モバイルプラットフォーム(Windows Mobile、Android)用のYoghurtGumと呼ばれるオープンソースゲームエンジンを書いています。これは私の大きな問題の1つでした。まず、このように解決しました:

class RenderMethod
{

public:

  virtual bool Init();
  virtual bool Tick();
  virtual bool Render();

  virtual void* GetSomeData(); 

}

あなたは見つけましたvoid*か?これは、頂点プールRenderMethodDirectDrawRenderMethodDirect3D返す一方でDirectDrawサーフェスを返すためです。他のすべても同様に分割されました。ポインターまたはポインターのSpriteいずれかを持つクラスがありました。それはちょっと吸い込まれた。SpriteDirectDrawSpriteDirect3D

だから最近、私は物事をたくさん書き直しています。私が今持っているのは、RenderMethodDirectDraw.dllRenderMethodDirect3D.dllです。実際、Direct3Dの使用を試みて失敗し、代わりにDirectDrawを使用することができます。これは、APIが同じままであるためです。

スプライトを作成する場合は、直接ではなく、ファクトリを使用して作成します。その後、ファクトリはDLL内の正しい関数を呼び出し、それを親に変換します。

したがって、これはRenderMethodAPI にあります。

virtual Sprite* CreateSprite(const char* a_Name) = 0;

そして、これはの定義ですRenderMethodDirectDraw

Sprite* RenderMethodDirectDraw::CreateSprite(const char* a_Name)
{
    bool found = false;
    uint32 i;
    for (i = 0; i < m_SpriteDataFilled; i++)
    {
        if (!strcmp(m_SpriteData[i].name, a_Name))
        {
            found = true;
            break;
        }
    }

    if (!found) 
    {
        ERROR_EXPLAIN("Could not find sprite named '%s'", a_Name);
        return NULL; 
    }

    if (m_SpriteList[m_SpriteTotal]) { delete m_SpriteList[m_SpriteTotal]; }
    m_SpriteList[m_SpriteTotal] = new SpriteDirectDraw();

    ((SpriteDirectDraw*)m_SpriteList[m_SpriteTotal])->SetData(&m_SpriteData[i]);

    return (m_SpriteList[m_SpriteTotal++]);
}

これが理にかなっていることを願っています。:)

PSこれにはSTLを使用したかったのですが、Androidにはサポートがありません。:(

基本的に:

  • すべてのレンダリングを独自のコンテキストで保持します。DLL、静的ライブラリ、または単なるヘッダーの束。RenderMethodX、SpriteX、およびStuffXがあれば、最高です。
  • Ogreソースからできるだけ多くを盗みます。

編集:はい、このような仮想インターフェースを持つことは理にかなっています。最初の試行が失敗した場合、別のレンダリング方法を試すことができます。このようにして、すべてのコードレンダリングメソッドに依存しないようにすることができます。


1
同時に複数の実装をアクティブにしない場合、仮想インターフェースを使用することは本当に理にかなっていますか?
ノクターンドラゴン

0

SDLを使用したいこのです。D3D、OpenGl、OpenGL ES、およびその他のプラットフォーム固有のバックエンド向けのレンダラーバックエンドがあり、あらゆる種類の異なるプラットフォームで利用可能で、現在活発に開発されており、多くの異なる言語にバインドされています。

さまざまなレンダラーの概念を抽象化し、シンプルなクロスプラットフォームAPIでビデオの作成(およびサウンドと入力の処理など)を可能にします。また、Blizzardの主任開発者の1人であるSam Lantingaによって設計されました。特に、移植ゲームの作成とクロスプラットフォームゲームの作成を容易にするため、高品質のライブラリを扱っていることがわかります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.