Принцип организации графического движка
Для начала сразу скажу, что боюсь называть это «движком», ибо это не движок пока вовсе, а просто надстройка над директиксом. Я просто задался целью создать приемлемую для меня архитектуру, которая позволяла бы мне создавать модульные, масштабируемые приложения, которые можно было бы легко расширять. Глядя на стандартные примеры из сдк понял, что в них все процедурно-ориентированное, и чтобы добавить новую сущнгость, например, понятие ОБЪЕКТА, нужно будет переписать практически все процедуры, мне же хочется модульности. Решил сделать всё объектно-ориентированным, но теперь, по мере кодинга, немного запутался в связях между сущностями.
Для начала решил определиться с основными классами. Ими стали класс Объекта, Сцены и Движка. В движок я засунул все служебные переменные, необходимые для инициализации директа и создания директового девайса, необходимого для отображения. Сцена у меня содержит настройки окна (в случае, если прога работает в оконном режиме) и список всех объектов, которые должны быть отрендерены. Для этого я воспользовался вектором из стандартной библиотеки, а в главном цикле проге происходит рендеринг всех объектов, входящих в список, думаю, смысл понятен… Так вот отсюда мой первый вопрос: должен ли каждый объект включать метод
И так для каждого объекта сцены? Но тогда получится, что движку будет необходимо «знать» обо всех возможных видах объектов, ведь класс Object – это просто абстрактный класс. Либо можно сделать просто чтобы все объекты сцены представлялись движку как меши и движок занимался непосредственно рендерингом мешей (ну и их текстурированием)?
Короче, пока мой основной вопрос: какая сущность должна рендерить объекты сцены : сами эти объекты должны заниматься своим рендерингом или их должен рендерить движок?
или
но, наверное, тут сомнений должно быть меньше :)
А твой вопрос из разряда, как лучше:
или
хм, ну и все-таки, какой именно вариант предпочтительнее из этих вот предложенных тобой двух?
С одной стороны, движку нужна информация об объекте, чтобы отрисовать его, с другой стороны, объекту может потребоваться информация о состоянии движка (и других объектах), чтобы отрисовать себя.
Но первый вариант (когда объект сам себя отрисовывает) будет все же логичнее и нагляднее.
А Engine::Render(Object& obj) в итоге сведется к чему-то типа:
obj.Render();
}
:)
{
public:
virtual int Init(sGraphicsMode*, iViewport* = 0) = 0;
virtual void Shutdown() = 0;
// ...
virtual void BeginFrame(sView& View) = 0;
virtual void EndFrame() = 0;
// ...
};
// ...
class sEngine: public iObject
{
public:
sView View;
iRenderer* pRenderer;
iViewport* pViewport;
// ...
void Update();
};
void sEngine::Update()
{
if(CheckRenderer())
{
pRenderer->BeginFrame(View);
// ....
pRenderer->EndFrame();
}
}
1)с чего вдруг ты решил написать движок?Захотелось Half Life ускорить?Если да,то помогу,чем смогу(оптимизация по скорости не повредит!)
2)ты уверен,что допишешь его?
3)надо ли делать классовую структуру движку?Не лучше ли в формате API?
Ты дал мало информации по организации движка:что он будет делать,как.Распиши подробней
Для этого используются "абстрактные" методы - виртуальные методы с "нулевым" кодом. К примеру, класс Object должен иметь набор свойств/методов, которыми можно описать все необходимые "общие" действия.
Соотвественно, производные классы, описывающие объекты конкретного вида, будут содержать в себе переопределённые методы, реализующие конкретный вариант данного действия
А то, как это делать - разницы особой нет.
Хотя по логике вещей, объекты могут быть самого разного типа, а их отображение - выполняться принципиально различающимися способами. Поэтому лучше всё-таки объявить абстрактный метод void Object::Render (void). Так будет больше свободы в описании конкретной реализации данного действия.
а по поводу твоего поста могу сказать, что есть смысл каждый обжект делить на меши.. каждый меш - можна уже будет рассматривать как отдельную единицу(которую нужно\ненужно рендерить, для этого тоже есть стандартные техники, уже даже хардварные). Для каждого типа материала - совокупность всех мешей - собирается в батч.. для каждого батча(!!) вызывается метод рендер. Почему так, а не иначе? Можно конечно и по-другому, но, так будет быстрее, потому-что смена материала - дорого по времени.
Объек только выглядит. Любые изменения формы, содержания и смысловой нагрузки объектов должны выполнять действия (actions).
С точки зрения технической реализации, любую сетку необходимо разбивать на составляюцие части по признаку различия материалов/шейдерных эффектов. Это требует как D3D, так и OGL.
Так и надо. Только не "движку", а "системе визуализации". Выносим рендер в отдельный класс, назначение которого - вытягивать и отрисовывать такую сетку. Помимо этого, рендер инициирует запуск эффектов (в частности, шейдерных операций), затребованных объектом/действием.
Эх, только вот тогда со скоростью придется распрощаться. В производительных проектах нужно структурное программирование а ООП лишний раз тормозит программу.
А с чего бы это ООП тормознее? Тормознее когда руки не оттуда растут.
Ну да. А для набора кода лучше всего использовать блокнот. Производительность, как правило, падает из-за неэффективного управления ресурсами, а не из-за того, что мы теряем несколько наносекунд при выполнении какого-то вызова.
Да не теряем мы ничего при ООП, откуда вы это взяли? Даже наоборот, приобретаем в некоторых реализациях.
Сравним
do_action(&obj);
и
obj.do_action();
В реализации ОО-вызовов от MS передача укзателя на объект осуществляется в регистре ecx, а не в стеке, как в первом листинге. Поэтому скорость выполнения во втором случае будет даже больше, при других равных условиях.
Ну здесь мы чуть-чуть потеряем, согласен. Но это не проблема ООП. Если брать код с одинаковыми возможностями, то ООП нисколько не уступает структурному подходу (это для параноиков).
Я вот только что подумал.. Давайте подумаем над тем, что графический движок должен делать, и чего он делать не должен, тогда сразу станет ясно, сколько времени мы так сказать потеряем, если будем использовать ООП.
Итак, графический движок должен, загружать обьекты и текстуры в видеопамять, производить трансформацию координат, отсечение невидимвых граней, наложение материалов, расчет освещения, ну и наложение эффектов.
Итак, учитывая то, что нормально написаный движок производит трансформацию координат при помощи вершинного шейдера, отсечение невидимых граней сначала на уровне бсп - просчитывается заранее, а более подробный анализ также реализован аппаратно, расчет освещения и пост процессинг выполняются при помощи пиксельных шейдеров, то главная задача - следить за своевременной загрузкой данных в видео память.. Собственно говоря, Загрузка данных может быть всегда выделена в отдельный поток.. поэтому потери в несколько наносекунд будут незаметны(ну, все есстественно зависит от рук), зато удобство работы с объектной моделью просто неоспоримо.
Это, конечно, мое имхо, так как я профессионально такими вещами не занимался..
Отнюдь. Мы живём в третьем тысячелетии, и каждому, по-моему, должно быть понятно, что ООП - более удачная технология, нежели любая вариация на тему процедурно-ориентированных техник программирования. Лучше настолько же, насколько электролобзик круче ножовки. Пропил одинаковый и там, и там. Однако, издержки при использовании нового инструмента и овладением навыком работы с ним ничтожно малы, по сравнению с предоставляемым им потенциалом и удобством в обращении. Кто не согласен - сносите Винды (их ядра жрут-то ого-го) и ставьте что-нибудь из прошлого века. MSDOS вполне подойдёт. А заодно напишите консольную версию вашего любимого web-браузера. А то, при использовании окон, с производительностью, поди, пришлось навеки распрощаться...
ООП само по себе издержек в производительности за собой практически не влечёт. Зато их может обеспечить абстрагирование, которого можно с помощью ООП достичь. Для того, чтобы перейти на более высокий уровень абстракции, зачастую необходимо, чтобы существующая прослойка была законченной, то есть робастной и самодостаточной. А это - некий код, приводящий её в такое состояние, код, корректно снимающий сделанные изменения, плюс код, обеспечивающий работу самой системы абстрагирования. Да туда же можно приписать и код, обслуживающий возможность повторного использования (вряд ли кто станет отрицать, что это - веское преимущество при использовании ООП). Это может быть, например, система проверок на корректность предоставленной блоку кода информации. Такая проверка нередко бывает излишней при использовании, например, класса, в тех условиях, для которых он и был создан. Зато она бывает безумно полезной при портировании класса в иное приложение. Вот вам и издержки.
Однако, их можно уменьшить в разы, если чётко себе представлять, из каких логических компонент состоит ядрышко, и каков характер взаимодействия между ними. Такого понимания, на мой взгляд, при работе над проектами, номинирующими на звание "сложный", да к тому же с возможностью растянуть сроки выполнения, необходимо достичь прежде, чем написать хоть строчку кода. Так, может, выдвинем фразу
на первый план, только в несколько иной форме: а чё вообще от системы требуется-то? Наличия каких возможностей мы добиваемся?
Могу сказать на 90%, что при таком подходе, автор топика не допишет движок, будет несколько раз начинать его заново, каждый раз меняя некоторую часть системы, но до логического конца дойти ему не удастся - фундамент будет трескаться то в одном месте, то в другом. К тому же такого рода ПО не под силу одному человеку, необходимо собирать команду. При этом сразу вспоминаю байку про то, как программисты дом строили :)
ogre3d.ru
Огр - это как раз ОО движок, который работает и с DX, и с OGL. Рапространяется вместе с исходниками. Написан на cpp. Посмотри на его иерархию - может найдешь что-то полезное.