レンダリング コードに非常に大きな設計上の障害があります。基本的にこれは、API 固有のコード (OpenGL コードや DirectX など) を必要としないものです。今、私は問題を解決する方法について多くの方法を考えてきましたが、どれを使用すればよいのか、これらのアイデアをどのように改善すればよいのかわかりません.
簡単な例を挙げるために、例としてテクスチャを使用します。テクスチャは、GPU メモリ内のテクスチャを表すオブジェクトです。実装に関しては、特定の方法で似ている可能性があります。つまり、実装でGLuint
またはLPDIRECT3DTEXTURE9
を使用してテクスチャに似ているかどうかです。
これを実際に実装するために私が考えた方法を次に示します。より良い方法があるかどうか、またはどの方法が別の方法よりも優れているかはまったくわかりません。
方法 1: 継承
継承を使用できますが、これがこの問題の最も明白な選択のようです。ただし、このメソッドには仮想関数が必要であり、Texture オブジェクトを作成するには TextureFactory クラスが必要です。new
オブジェクトごとに を呼び出す必要がありTexture
ます (例: renderer->getTextureFactory()->create()
)。
この場合に継承を使用することをどのように考えているかを次に示します。
class Texture
{
public:
virtual ~Texture() {}
// Override-able Methods:
virtual bool load(const Image&, const urect2& subRect);
virtual bool reload(const Image&, const urect2& subRect);
virtual Image getImage() const;
// ... other texture-related methods, such as wrappers for
// load/reload in order to load/reload the whole image
unsigned int getWidth() const;
unsigned int getHeight() const;
unsigned int getDepth() const;
bool is1D() const;
bool is2D() const;
bool is3D() const;
protected:
void setWidth(unsigned int);
void setHeight(unsigned int);
void setDepth(unsigned int);
private:
unsigned int _width, _height, _depth;
};
次に、OpenGL (またはその他の API 固有) テクスチャを作成するには、 などのサブクラスを作成する必要がありますOglTexture
。
方法 2: 「TextureLoader」またはその他のクラスを使用する
この方法は見た目ほど簡単ですが、別のクラスを使用してテクスチャの読み込みを処理します。これは、状況に応じて(または必要と感じるかどうかに応じて)、仮想関数を使用する場合と使用しない場合があります。
例: ポリモーフィック テクスチャ ローダー
class TextureLoader
{
public:
virtual ~TextureLoader() {}
virtual bool load(Texture* texture, const Image&, const urect2& subRect);
virtual bool reload(Texture* texture, const Image&, const urect2& subRect);
virtual Image getImage(Texture* texture) const;
};
これを使用すると、Texture
オブジェクトは POD タイプのみになります。ただし、これを機能させるには、ハンドル オブジェクト/ID がTexture
クラス内に存在する必要があります。
たとえば、これは私が実装する可能性が高い方法です。ただし、基本クラスを使用して、ID 全体を一般化できる場合があります。Resource
グラフィックス リソースの ID を保持する基本クラスなど。
方法 3: Pimpl イディオム
ロード/リロードなどの方法を実装する pimpl イディオムを使用できます。テクスチャ。これには、テクスチャを作成するための抽象ファクトリ クラスが必要になる可能性が高くなります。これが継承を使用するよりも優れているかどうかはわかりません。この pimpl イディオムは方法 2 と組み合わせて使用できます。つまり、Texture オブジェクトはローダーへの参照 (ポインター) を持ちます。
方法 4: 概念/コンパイル時のポリモーフィズムを使用する
一方、コンパイル時のポリモーフィズムを使用して、仮想関数を宣言しないことを除いて、基本的に継承メソッドで提示したものを使用できます。これは機能しますが、OpenGL レンダリングから DirectX レンダリングに動的に切り替えたい場合、これは最適なソリューションではありません。OpenGL/D3D 固有のコードを Texture クラス内に配置するだけで、同じインターフェイス (load/reload/getImage/etc.) を持つ複数のテクスチャ クラスが存在し、名前空間内にラップされます (使用する API に似ています。ogl
、などd3d
)。
方法 5: 整数を使用する
テクスチャ オブジェクトへのハンドルを格納するために整数を使用することもできます。これはかなり単純に思えますが、「厄介な」コードが生成される可能性があります。
この問題は、Geometry、Shaders、ShaderPrograms などの他の GPU リソースにも存在します。
また、グラフィカル リソースの作成、読み込みなどを Renderer クラスで処理することも考えました。ただし、これはSPRに違反します。例えば
Texture* texture = renderer->createTexture(Image("something.png"));
Image image = renderer->getImage(texture);
誰かが私を案内してくれませんか、私はこれについて考えすぎていると思います。Irrlicht、Ogre3D など、オンラインで見つけたさまざまなレンダリング エンジンを観察してみました。Ogre と Irrlicht は継承を使用していますが、これが最善の方法かどうかはわかりません。void* や整数を使用したり、API 固有の (主に OpenGL) コードをクラス内に配置したりするものもあります (たとえば、GLuint を Texture クラス内に直接配置するなど)。どのデザインが自分に最も適しているかを本当に決めることはできません.
対象とするプラットフォームは次のとおりです。
- Windows/Linux/Mac
- iOS
- おそらくアンドロイド
OpenGL はこれらすべてのプラットフォームで機能するため、OpenGL 固有のコードのみを使用することを検討しました。ただし、そうすると、PS3 などの OpenGL を使用できない他のプラットフォームに移植したい場合、コードをかなり変更する必要があると感じています。私の状況に関するアドバイスをいただければ幸いです。