多个渲染系统

Posted

技术标签:

【中文标题】多个渲染系统【英文标题】:Multiple render systems 【发布时间】:2012-02-11 05:51:50 【问题描述】:

在不久的将来,我将开始开发游戏引擎。我想要包含的一项功能是拥有,例如 directx 9/10/11 和 OpenGL。这样一来,使用该引擎的游戏将能够支持更多玩家,因为如果一个渲染系统不起作用,它将恢复到另一个渲染系统。

所以我的问题是,我该怎么做呢?我研究了一下,发现 Ogre3D 使用了类似的东西,但我不知道他们是如何做到的。我基本上希望能够插入不同的渲染系统并使用相同的代码来运行它。

【问题讨论】:

很简单:它们抽象了渲染部分。 完成一个怎么样,然后你就会知道需要什么? 是的,这就是我要找的东西:抽象接口。所以我会实现一个单一的接口,它可以加载一个包含函数调用的库,这些函数调用根据渲染系统的不同而不同? 【参考方案1】:

利用 C++ 接口的强大功能!

创建一个定义渲染系统接口的抽象

class Renderer

 //...
    virtual void DrawRect(Color c, Vector2 Pos) = 0;
 //...

然后在每个渲染系统中实现它:

class D3DRenderer : Renderer

    void DrawRect(Color c, Vector2 Pos);

这种方法快速、简单、高效,这也是许多游戏引擎使用它的原因。 应该注意的是,在构建这样的东西(即使是像行主矩阵和列主矩阵这样的小东西)时,它确实需要了解某些 API 限制。

【讨论】:

【参考方案2】:

您可以创建一个接口类,允许您进一步扩展您希望扩展到的 API。

我过去曾这样做来创建 DirectX9\11 渲染器,我希望尽快将其扩展到 OpenGL。这项任务有很多确定性,但很容易解释它的基本工作。不幸的是,我参与的项目是封闭源代码,所以如果您对此有任何疑问,请随时提出。

首先,您需要创建一个单独的项目以用作 .lib/.dll,我将其命名为“RenderInterface”。这将包含 VertexBuffer、IndexBuffer、Shader 的基本接口,最重要的是 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,这只是混乱。

【讨论】:

谢谢。这种方法很有意义。问题是,我对 DirectX 及其提供的功能知之甚少,所以我不知道我可以使用哪些“功能”。我只需要阅读一下。 设计理念是在抽象层上,它独立于 API(比如 DirectX 或 OpenGL)有什么。【参考方案3】:

首先,我认为同时实现 OpenGL 和 DirectX 后端是不值得的。您只需通过不同的 API 使用相同的硬件。我会选择 OpenGL,因为它是一个开放标准,并且支持更多操作系统 - 包括支持 DirectX 的 Windows。

其次,您应该了解 OpenGL(或 DirectX)版本之间的差异。通常,新版本会添加新功能并弃用一些旧功能 - 但这些功能仍然可用。很少有旧功能实际上被放弃,例如DirectX 10 中的固定功能管道已经完成。这是我建议宁愿使用 OpenGL 的另一个原因 - 它更向后兼容,您可以更轻松地支持多个版本。

支持较新的 OpenGL 版本通常意味着您正在使用旧版本中没有的新功能。为了与旧版本保持兼容,您基本上有两种选择:

使用旧版本中不可用的 OpenGL 功能为您的应用程序部分提供两种实现。然后,您必须为仅使用旧版本中可用的 OpenGL 功能的功能编写替代实现。 如果功能不可用,请禁用使用新 OpenGL 功能的功能。这通常意味着某些效果在 OpenGL 版本不支持时不会渲染。这不是渲染引擎所需功能的选项,而不仅仅是“看起来更好”。

如果您想同时支持 DirectX 和 OpenGL,您必须为您的渲染子系统提供一个完整的抽象接口,并且可能需要三个实现 - DirectX 9、DirectX 10 / 11 和 OpenGL。如前所述,您可以在实现中补偿向后兼容的 API 更新。

【讨论】:

好的。忘掉OpenGL吧。我仍然想实现几个版本的 directx 来支持旧硬件。 正如我所说,您必须编写一个 DirectX 9 和一个 DirectX 10 / 11 实现;您可能可以在它们之间共享一些代码。重要的是,一种实现与 DX9 相关联,另一种与 DX10/11 相关。 我完全同意flyx,而且我遇到的只有少数电脑DX渲染速度胜过OpenGL渲染速度,而且这些电脑是10-15岁,过时的图形驱动程序等等。更不用说你不能在 DirectX 上做你可以在 OpenGL 上做的所有事情:***.com/questions/8764013/…,最终你会为 0.1% 的新用户浪费 10 倍以上的时间......所以,如果你想要那些 0.1%用户自己,准备好他们以 1-5 fps 运行您的游戏。【参考方案4】:

使用预处理器

对此的一种解决方案是使用预处理器。

#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

【讨论】:

我想要一个不依赖游戏引擎本身代码的插件式系统。所以就像其他人说的,可以调用不同渲染系统的抽象接口。

以上是关于多个渲染系统的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL中的多个渲染目标与Cg

如何阻止多个重新渲染执行多个 api 调用 useEffect?

如何渲染多个纹理?

Metal:在一个渲染过程中使用多个 MTLRenderCommandEncoder

深度缓冲区不使用多个着色器程序

是否可以一次通过多个视图渲染对象