在不久的将来,我将开始开发游戏引擎。我想要包含的一个功能是拥有多个渲染系统,例如directx 9/10/11和OpenGL。这样,使用此引擎的游戏将能够支持更多玩家,因为如果一个渲染系统不起作用,它将恢复为另一个。
所以我的问题是,我将如何做到这一点?我研究了一下,看到Ogre3D使用了这样的东西,但我不知道他们是怎么做到的。我基本上希望能够插入不同的渲染系统并使用相同的代码来运行它。
答案 0 :(得分:5)
使用C ++接口的强大功能!
创建一个抽象,定义渲染系统的接口
class Renderer
{
//...
virtual void DrawRect(Color c, Vector2 Pos) = 0;
//...
}
然后在每个渲染系统中实现它:
class D3DRenderer : Renderer
{
void DrawRect(Color c, Vector2 Pos);
}
这种方法快速,简单,高效,这就是它在许多游戏引擎中使用的原因。 应该注意的是,它确实需要在构建这样的东西时知道某些API限制(甚至像行主要列和列主要矩阵这样的小东西)。
答案 1 :(得分:5)
您可以创建一个接口类,允许您进一步扩展您希望扩展的API。
我过去已经这样做了创建一个DirectX9 \ 11渲染器,我希望尽快将其扩展到OpenGL。这项任务确实有很多,但很容易解释它的基本工作。不幸的是,我所处的项目是封闭式的,所以如果您对此有任何疑问,请随时提出。
首先,您需要创建一个单独的项目以用作.lib / .dll,我称之为“RenderInterface”。这将包含VertexBuffer,IndexBuffer,Shaders以及最重要的IRenderInterface和IRenderUtility的基本接口,在后面的实现中可能包含诸如ID3D11DeviceContext和ID3D11Device之类的项目。
“RenderInterface”项目中我最重要的两个项目是IRenderInterface和IRenderUtility。这里的想法是让IRenderInterface重要提升,例如创建渲染目标,呈现和初始化API。其中IRenderUtility将执行更常见的操作,例如创建顶点/索引缓冲区,创建着色器和渲染。我在初始化和渲染时更频繁地传递IRenderUtility,并且很少传递IRenderInterface。这是为了防止用户在不需要时进行意外操作。该项目还将包括将在整个代码库中使用的一些常见结构\枚举。
然后我创建了另一个项目,例如D3D11RenderInterface。这个D3D11RenderInterface将具有IRenderInterface,IRenderUtlity,IVertexShader,IVertexBuffer等的实现。
一个过于简单的例子是
class IRenderUtility
{
public:
virtual void Draw(unsigned int vertexCount, unsigned int vertexStartOffset) = 0;
};
class D3D11RenderUtility : public IRenderUtility
{
protected:
ID3D11DeviceContext *mD3D11DeviceContext;
public:
void Draw(unsigned int vertexCount, unsigned int vertexStartOffset)
{
mD3D11DeviceContext->Draw(vertexCount, vertexStartOffset);
};
};
这可以通过在IRenderUtility :: Draw调用之前调用IRenderUtility来设置VertexBuffer和IVertexBuffer。
然后,在您的应用程序中只需要加载RenderInterface项目和api实现。还有其他方法可以做到这一点,比如整个代码库中的#define'ing代码,但imo,这只是一个混乱。
答案 2 :(得分:3)
对此的一个解决方案是使用预处理器。
#ifdef DIRECT_X
// something for DX
#else
// something for OpenGL etc.
#endif
此方法也被SDL等库使用。如果您想使用渲染系统,只需#define
即可。此外,从跨平台兼容性,您可能需要使用特定于平台的宏。请参阅this。
更好的方法是将特定于平台的方法放入单独的翻译单元中。这将使清洁和更易读的代码,更易于开发和维护。
例如:
class Rectangle
{
public:
void draw(int,int,int,int);
};
<强> rectangle_dx_9.cpp 强>:
#include "rectangle.hpp"
void
Rectangle ::
draw(int upper_left_corner_x, int upper_left_corner_y,
unsigned int length, unsigned int width)
{
// Direct X 9 specific code here.
}
<强> rectangle_open_gl.cpp 强>:
#include "rectangle.hpp"
void
Rectangle ::
draw(int upper_left_corner_x, int upper_left_corner_y,
unsigned int length, unsigned int width)
{
// OpenGL specific code here.
}
等
现在,您需要做的就是,对于Direct X 9,将构建过程配置为使用 rectangle_dx_9.cpp ,而对于OpenGL,使用 rectangle_open_gl.cpp 。
答案 3 :(得分:2)
首先,我认为实现OpenGL和DirectX后端并不值得付出努力。您只是通过不同的API使用相同的硬件。我会选择OpenGL,因为它是一个开放标准,并且支持更多操作系统 - 包括可以使用DirectX的Windows。
其次,您应该了解OpenGL(或DirectX)版本之间的差异。通常,新版本会添加新功能并弃用一些旧功能 - 尽管这些功能仍然可用。很少有旧功能实际上被放弃,例如,已完成DirectX 10中的固定功能管道。这是我建议使用OpenGL的另一个原因 - 它更向后兼容,您可以更轻松地支持多个版本。
支持较新的OpenGL版本通常意味着您使用的是旧版本中未提供的新功能。为了与旧版本保持兼容,您基本上有两个选择:
如果你想同时支持DirectX和OpenGL,你必须为你的渲染子系统提供一个完整的抽象接口,可能还有三个实现--DirectX 9,DirectX 10/11和OpenGL。如上所述,您可以在实施中补偿向后兼容的API更新。