Несколько систем рендеринга - PullRequest
3 голосов
/ 16 января 2012

В ближайшее время я начну разработку игрового движка. Одна функция, которую я хочу включить, - это использование нескольких систем рендеринга, таких как DirectX 9/10/11 и OpenGL. Таким образом, игра, использующая этот движок, сможет поддерживать больше игроков, поскольку если одна система рендеринга не работает, она вернется к другой.

Итак, мой вопрос, как бы я поступил так? Я немного исследовал и увидел, что Ogre3D использует что-то подобное, но я понятия не имею, как они это делают. Я в основном хочу иметь возможность подключать различные системы рендеринга и использовать один и тот же код для его запуска.

Ответы [ 4 ]

5 голосов
/ 19 января 2012

Вы можете создать интерфейсный класс, который позволит вам расширять API, на который вы надеетесь расширить.

Я делал это в прошлом, чтобы создать DirectX9 \ 11 Renderer, и я надеюсь в скором времени расширить его до OpenGL. В этой задаче есть много уверенности, но ее простую работу легко объяснить. К сожалению, проект, на котором я работаю, является закрытым, поэтому, если у вас есть какие-либо вопросы по этому поводу, пожалуйста, не стесняйтесь спрашивать.

Сначала вы захотите создать отдельный проект для использования в качестве .lib / .dll, я назвал это «RenderInterface». Он будет содержать базовые интерфейсы для VertexBuffer's, IndexBuffer's, Shaders и, что наиболее важно, IRenderInterface и IRenderUtility, которые в более поздней реализации могут потенциально содержать такие элементы, как, например, ID3D11DeviceContext и ID3D11Device.

Моими двумя наиболее важными элементами в проекте «RenderInterface» являются IRenderInterface и IRenderUtility. Идея заключается в том, чтобы IRenderInterface выполнял тяжелую работу, такую ​​как создание целей рендеринга, представление и инициализация API. В то время как IRenderUtility будет выполнять более общие задачи, такие как создание буферов вершин / индексов, создание шейдеров и рендеринг. Я чаще передаю IRenderUtility при инициализации и рендеринге, а IRenderInterface редко передается. Это сделано для того, чтобы пользователи не могли выполнять случайные операции, когда им это не нужно. Этот проект также будет включать некоторые распространенные структуры \ enums, которые будут использоваться во всей базе кода.

Затем я создаю другой проект, такой как 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);
    };
};

Это работает, когда VertexBuffer и IVertexBuffer устанавливаются посредством вызова IRenderUtility перед вызовом IRenderUtility :: Draw.

Тогда в вашем приложении нужно будет только загрузить проект RenderInterface и реализацию API. Есть и другие способы сделать это, например, # определение кода по всей вашей кодовой базе, но imo, это просто грязно.

5 голосов
/ 16 января 2012

Используйте возможности интерфейсов C ++!

создайте реферат, который определяет интерфейс вашей системы рендеринга

class Renderer
{
 //...
    virtual void DrawRect(Color c, Vector2 Pos) = 0;
 //...
}

, а затем внедрите его в каждую систему рендеринга:

class D3DRenderer : Renderer
{
    void DrawRect(Color c, Vector2 Pos);
}

этот метод быстрый, простой и эффективный, поэтому он используется во многих игровых движках.Следует отметить, что при построении таких вещей нужно знать определенные ограничения API (даже такие мелочи, как мажор строки или мажор столбца).

3 голосов
/ 16 января 2012

Использование препроцессора

Одним из решений этой проблемы является использование препроцессора.

#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 .

2 голосов
/ 16 января 2012

Во-первых, я не думаю, что стоит потратить усилия на реализацию как OpenGL, так и DirectX.Вы бы просто использовали одно и то же оборудование через разные API.Я бы пошел на OpenGL, потому что это открытый стандарт и поддерживается в других операционных системах - включая Windows, где доступен DirectX.

Во-вторых, вы должны понимать разницу между версиями OpenGL (или DirectX).Обычно новая версия добавляет новые функции и не поддерживает некоторые старые функции, которые, тем не менее, еще можно использовать.В редких случаях старые функциональные возможности фактически отбрасываются, как, например, это было сделано с конвейером фиксированных функций в DirectX 10. Это еще одна причина, по которой я бы рекомендовал использовать OpenGL - она ​​более обратно совместима, и вам легче поддерживать несколько версий.

Поддержка более новой версии OpenGL обычно означает, что вы используете новые функции, которые не были доступны в более старых версиях.Чтобы оставаться совместимым со старыми версиями, у вас в основном есть два варианта:

  • Предоставьте две реализации для части вашего приложения, используя функции OpenGL, которые недоступны в более старых версиях.Затем вам нужно написать альтернативную реализацию для функции, которая использует только функции OpenGL, доступные в более старых версиях.
  • Отключите функцию, которая использует новые функции OpenGL, если функции недоступны.Обычно это означает, что некоторые эффекты не отображаются, когда версия OpenGL не поддерживает их.Это не вариант для функций вашего движка рендеринга, которые требуются, а не просто для того, чтобы «выглядеть лучше».

Если вы хотите поддерживать как DirectX, так и OpenGL, вам нужно будет предоставить полный наборабстрактный интерфейс для вашей подсистемы рендеринга и, возможно, три реализации - DirectX 9, DirectX 10/11 и OpenGL.Как уже упоминалось, вы можете компенсировать обратно совместимые обновления API в вашей реализации.

...