четверг, 13 ноября 2008 г.

Lod'ы шейдеров в Direct3D10 и отсутствие FFP

Привел от рисовку объектов при Direct3D10 рендере в порядок. Теперь они не исчезают при большом расстоянии от камеры.
В движке есть понятие Lod для шейдеров. С каким-то шагом можно задавать более сложные шейдеры для объектов. Это дает прирост производительность на старом железе(а может и на новом). К примеру возьмём дерево, которое анимируется на ветру вершинным шейдером, в шейдере есть тригонометрическая функция для нее генерируется достаточно много инструкций, очевидно дерево можно не анимировать с очень большого расстояния, в этом случае включится простой шейдер, просто трансформирующий вершину. Другой пример - нет смысла делать эффект бампа и освещения на большом расстоняии - можно ограничится диффузионным освещением или вообще без него. Конечно переключение возможно будет заметно. Аналогичная ситуация с анимацией травы. Можно сделать лоды для скелетной анимации. Но там более сложные параметры.
В случае OpenGL/Direct3D9, если не будет найден подходящий лод, то будет выставлены NULL шейдеры, т.к. для них есть фиксированный конвейер то можно вызвать IDirect3DDevice9::SetTransform(world) и glLoadMatrixf(worldViewMatrix). Для Direct3D10 в этом случае не будет ничего рисоваться. Я решил эту проблему таким образом. Для каждого формата вершин создал список шейдеров по умолчанию. В случае выставления NULL шейдера, будет браться шейдер по умолчанию, если есть вершинный буфер.
Это простое и удобное решение. Не нужно дополнительных шейдеров, тем более шейдера по умолчанию простые и короткие, поэтому их код хранится прямо в реализации класса рендера движка. Для шейдеров с альфа тестом, есть дополнительный список шейдеров по умолчанию для каждого формата, само собой с параметром - текущий AlphaRef, тут имеется ввиду значение, передаваемое в IDirect3DDevice9::SetRenderState(D3DRS_ALPHAREF, AlphaRef) в Direct3D9 и в glAlphaFunc(func, AlphaRef) в OpenGL. В Direct3D10 убран альфа тест из FFP, поэтому нужен такой параметр по которому делается discard.
Таким образом объекты перестали исчезать.

понедельник, 10 ноября 2008 г.

Добавлен встроенный профайл

Добавил в движок встроенный профайл. По очень простой схеме,
некий класс содержащий данные о текущей строке имени файла и имени функции. Этот класс имеет конструктор с такими параметрами. С помощью макроса
#define PROFILE Profile::ProfileData data(__FILE__, __LINE__, __FUNCTION__);
можно в начале любой функции добавлять отсчёт времени. В конструкторе запоминается стартовое время работы. В деструкторе формируется строка в виде "File, line, function, time"
и рассчитывается время работы функции, данные добавляются с список. Все банально и просто. Нужно ещё решить как-то проблему мигания значение цифр, что происходит из-за разброса значений времени.

четверг, 6 ноября 2008 г.

Объединение одинаковых пересекающихся объектов на сцене.

При сильно загруженной сцене бывает очень много вызовов от рисовок геометрии, всем известными под названием DIP (IDirect3DDevice9::DrawIndexedPrimitive для Direct3D9, glDrawElements для OpenGL и ID3D10Device::DrawIndexed для Direct3D10). Большое количество таких вызовов может нанести серьезный удар по быстродействию рендера, особенно это более актуально для Direct3D 9, именно в в момент вызова идет проверка на валидность пиксельного и вершинного шейдера, правильная линовка данных передаваемых из вершинного в пиксельный, проверка вершинных деклараций на соответствие того что использует вершинный шейдер и что находится в вершинном буфере, так-же индексы вершин и остальные параметры. В OpenGL все намного проще, после выполнения каждой функции может возникнуть ошибка. Таким образов время как-бы равномерно распределяется между всеми вызовами функция перед вызовом от рисовки.
Ну в Direct3D 10 DIP cost значительно снижен.
Так-же частые вызовы может серьёзно нагрузить CPU по вызовам функций. Обычно до 1000 DIP на кадр ещё не все так плохо.
Итак при загрузки сцены я сделал некую простейшую оптимизацию, ищем объекты с одинаковы мешами и если они пересекаются своими ограничивающими боксами, собираем их в 1 мешь с учтя матрицу трансформации. Что это дает?
1) уменьшение нагрузки на CPU при обходе Scene Graph и при сортировке объектов;
2) уменьшение DIP cost при отриcовке в Depth текстуру для теней;
3) уменьшение DIP cost при финальной от рисовке объектов;
Минусы в том что будет небольшой перерасход памяти. Но если к примеру у меня на сцене дерево ели имеет 350 полигонов и их несколько сотен, то объедение по 3-4 в 1 мешь будут значительно сокращать DIP cost. Пока тесты особых положительных результатов не дали но число DIP и число объектов в кадре значительно сокращены в грубом приближении на 30% в среднем. Повышение FPS вроде не более 10%. Буду тестировать на более слабом железе. На будущее конечно нужно подумать об использовании Instansing'а. Железо сейчас c такой функциональностью уже достаточно доступно и инстансинг есть уже в 3 API в том числе и поддержка у ATI для OpenGL.

пятница, 31 октября 2008 г.

Подгонка шейдеров под все графические API

Пока Direct3D10 рендер ещё сырой, иногда возвращаюсь к нему и постепенно дописываю.
Cейчас избавился в движке от OpenGL state(передаю матрицы трансформаций напрямую) .
Когда он был заменял макросы на соответствующие параметры при компиляции из Cg в GL_ARB_vertex_program. Код GLSL полученный из Cg не содержит GL state - видно nVidia сразу продумала это на будущее как deprecated функциональность.
При использовании этих макросов компилятор HLSL для Direct3D10 почему-то неверно распределял регистры в отличие от компилятора HLSL Direct3D9. Я пока не стал разбираться с этим, а временно скинул и переписал шейдеры специально для Direct3D10. Немного переписав шейдеры и добавив системный макрос D3D10 я избавился отлишней папки с шейдерами для Direct3D10.Настало время разобраться с использованием Cg для Direct3D10.

среда, 22 октября 2008 г.

Добавлена оптимизация перемножения матриц через SSE

Добавил в движок поддержку SSE в частности для перемножения матриц 4x4, 4x3. Написал аллокатор для STL std::vector выделяющий память, выровненную по границе 16 байт. Используются функции _aligned_malloc,_aligned_free. Большое спасибо Арсению Капулкину(Zeux) за исправленный код.

пятница, 3 октября 2008 г.

Разработка 3D движка

Итак, постепенно сюда буду писать прогресс разработки собственного трехмерного движка. Этой идеей уже сейчас никого не удивишь. Очень много людей пытаются написать 3D движок.
Целью разработки прежде всего является желание стать профессиональным программистом.
Разработка происходит в свободное время, временами с перерывами и ненормированным графиком(2-3 часа в день в среднем) и начата довольно давно где-то в конце 2004 года. В общей сложности общее время плотной работы 2 года. Одновременно с движком изучался С/C++ 3D графика и остальные смежные области, так и программирование в целом. Особого плана работы не было. Делалось то что получалось постепенно расширялась функциональность, проводилась оптимизация, улучшение архитектуры. Некоторые ошибки, недочёты, до сих пор не исправлены, Причины тут разные: либо отсутствие времени, опыта, или желание улучить то что по силам в данный момент, вернувшись к тому что поправить/доработать сложней. Движок не претендует на полноту и совершенство т.к. все понимают задача эта нетривиальная. Итак опишу что сейчас сделано и реализовано:
I. Не графическая функциональность
1) Физика: гора неработающего кода на Tokamak в дальнейшем будет выкинут и заменен на Ageia PhysX. Есть базовая функциональность. позволяющая в будущем добавить физическую библиотеку
2) Звук на основе библиотеки OpenAL с потоковой загрузкой ogg(libvorbis) и mp3(libmad),
3D звук(требует тестирования)
3) Поддержка Lua скриптов с доступом из скрипта нескольких переменных что позволяет ка минимум менять анимацию, реализовывать слежение за камерой, Debug Log у консоль из скриптов
4) Консоль - незаменимое средство отладки диагностики и управления движком от перезагрузки ресурсов до смены текущего рендера. Является так-же средством просмотра лога в реальном времени изменения/переключения режимов работы движка и смены рендера.
5) Самый начальный уровень сети инициализация сокетов Брекли создание сервера и клиентов. Посылка данных.(требуется доработка)
6) Защита от фатальных ошибок(целочисленное деление на ноль, нарушенный доступ к памяти), протоколирование работы, c записью Dump'а памяти на диск.
7) видео проигрыватель на основе theora;
8) файловая система с поддержкой zip;
II. Графика
1) Поддержка Direct3D9/Direct3D10/OpenGL. Смена рендера возможно командой консоли changerender 0(1)(2) где параметр тип рендера.
2) поддержка шейдерных языков HLSL/Cg/GLSL(из Сg кода)
3) Shadow Map тени с само затенением.(Standart Shadow Map) - в данный момент ведется улучшения теней посредством алгоритма Parallel Split Shadow Map.
4) попиксельное освещение.
5) Свой формат шейдеров(описание шейдера + константы) с менеджером шейдеров, Гибкая возможность задания констант, не изменяя код движка, прямо в файле
6) Свой формат материалов на основе идей из RenderMonkey и Ogre.

7) Гибкая система частиц с набором неограниченного числа различных независимых эмиттеров со своим набором аффекторов. + сохранение/загрузка из файла с менеджированием.
8) Иерархическая организация сцены в виде Scene Graph.
9) Поддержка динамических объектов в Scene Graph.
10) Frustum Culling(Оптимизированая проверка Box'а всего по 2 точкам)
11) Occluision Culling(Software, ведутся исследования для реализации Иеррахического Occlusion Query HQQ)
12) Рендер множества объектов(как в виде генерации мешей, на площади или текстурированных прямоугольников(плашек), в данный момент используется для редера растительности)
13) Свой формат статических моделей;
14) Скелетная анимация + свой формат моделей;
15) Морфичная анимация + свой формат моделей;
16) Рендеринг ландшафта с использованием технологии "Chunked Lod"(требует заделывания щелей между лодами);
17) Задание отсечения объектов по дистанции;
18) Задание альфа теста по дистанции;
19) Лоды для шейдеров(возможно требует доработки)
20) Менеджер полно экранного постпроцессинга с загрузкой описания постпроцессинга из файла. (Bloom, Depth of Filed)


Из тулзов разработан редактор системы частиц, экспортер скелетной
и морфичной анимации из 3DS Max, экспортер статических мешей.

Многие спросят зачем используется OpenGL? Тут все просто это первый API который я начал изучать, Не хочу кидать его поддержку т.к. там не так много ошибок как раньше хотя поддерживать его сложней всего, тут холивар устраивать не будем, у каждого есть мнение на этот счет. Мне кажется профессионал должен знать в совершенстве все 3D API.

Код писался на C/C++. В коде отсутствуют глобальные переменные и функции(кроме WinMain) Используется STL/boost(boost::any, boost::shared_ptr). Код по возможности писался кросплатформенным. Движок гарантированно компилится на Microsoft Visual C++ 2005/2008, Intel C++ 10.x, gcc(MinGW) с последним компилятором возникли проблемы при компиляции Direct3D10.

Пока скриншоты выкладывать не буду, хотя есть что показать поправлю побольше ошибок + немного улучшу.

четверг, 14 августа 2008 г.

Cg 2.1 Beta

Вышел Cg 2.1 Beta. Очень радует поддержка Direrct3D10. Не так давно спрашивал про это у nVidia разработчиков, ответили не планируется пока поддержки. Компилятор свой для Direct3D10 они не писали что тоже радует, не будет ошибок и несовместимостей. для компиляции используют функцию D3DX10CompileFromMemory, для получения всех констант используется D3DX10ReflectShader. Не совсем понятно почему результат cgGetProgramString(cgProgram, CG_COMPILED_PROGRAM); выдает hlsl код опять с измененными именами констант, точка входа именуются "main" Вот к примеру результат компиляции:
// hlsl10v output by Cg compiler
// cgc version 2.1.0009, build date Aug 6 2008
// command line args: -q -profile vs_4_0 -entry VSmain -profileopts -d3d
//vendor NVIDIA Corporation
//version 2.1.0.9
//profile vs_4_0
//program VSmain
//semantic ViewProjMatrix : WorldViewProj
//semantic MatTexture : C4
//semantic tex1 : TEXUNIT0
//semantic tex2 : TEXUNIT1
//semantic ShadowMap : TEXUNIT2
//var float4x4 ViewProjMatrix : WorldViewProj : _ZZ2SViewProjMatrix[0], 4 : -1 : 1
//var float4x4 MatTexture : C4 : _ZZ2SMatTexture[0], 4 : -1 : 1
//var sampler2D tex1 : TEXUNIT0 : : -1 : 0
//var sampler2D tex2 : TEXUNIT1 : : -1 : 0
//var sampler2D ShadowMap : TEXUNIT2 : : -1 : 0
//var float4 Pos : $vin.POSITION : POSITION : 0 : 1
//var float2 texCoord : $vin.TEXCOORD0 : TEXCOORD0 : 1 : 1
//var float4 VSmain.Pos : $vout.POSITION : : -1 : 1
//var float2 VSmain.texCoord : $vout.TEXCOORD0 : : -1 : 1
//var float4 VSmain.shadowCrd : $vout.TEXCOORD1 : : -1 : 1

#pragma pack_matrix(row_major)

struct VS_OUTPUT {
float4 Pos : POSITION;
float2 texCoord : TEX0;
float4 shadowCrd : TEX1;
};

struct X1X {
float4 POSITION : SV_Position;
float2 TEX0 : TEX0;
float4 TEX1 : TEX1;
};

float4 _ZZ3SrZh0001;
float4 _ZZ3SrZh0003;
X1X _ZZ3SZDcout;
uniform float4x4 _ZZ2SViewProjMatrix : WorldViewProj;
uniform float4x4 _ZZ2SMatTexture : C4;

// main procedure, the original name was VSmain
X1X main(in float4 _ZZ4SPos : POSITION, in float2 _ZZ4StexCoord : TEX0)
{

VS_OUTPUT _ZZ4SOut;

_ZZ3SrZh0001 = _ZZ4SPos.x*_ZZ2SViewProjMatrix._11_12_13_14;
_ZZ3SrZh0001 = _ZZ3SrZh0001 + _ZZ4SPos.y*_ZZ2SViewProjMatrix._21_22_23_24;
_ZZ3SrZh0001 = _ZZ3SrZh0001 + _ZZ4SPos.z*_ZZ2SViewProjMatrix._31_32_33_34;
_ZZ3SrZh0001 = _ZZ3SrZh0001 + _ZZ4SPos.w*_ZZ2SViewProjMatrix._41_42_43_44;
_ZZ4SOut.Pos = _ZZ3SrZh0001;
_ZZ3SrZh0003 = _ZZ4SPos.x*_ZZ2SMatTexture._11_12_13_14;
_ZZ3SrZh0003 = _ZZ3SrZh0003 + _ZZ4SPos.y*_ZZ2SMatTexture._21_22_23_24;
_ZZ3SrZh0003 = _ZZ3SrZh0003 + _ZZ4SPos.z*_ZZ2SMatTexture._31_32_33_34;
_ZZ3SrZh0003 = _ZZ3SrZh0003 + _ZZ4SPos.w*_ZZ2SMatTexture._41_42_43_44;
_ZZ4SOut.shadowCrd = _ZZ3SrZh0003;
_ZZ4SOut.texCoord = _ZZ4StexCoord;
_ZZ3SZDcout.POSITION = _ZZ4SOut.Pos;
_ZZ3SZDcout.TEX0 = _ZZ4SOut.texCoord;
_ZZ3SZDcout.TEX1 = _ZZ4SOut.shadowCrd;
return _ZZ3SZDcout;
} // main end
Ну на самом деле данный код не планируется подсовывать D3D10CompileShader проще сделать все стандартно
взять ID3D10Blob* из cgD3D10GetCompiledProgram и самому создать шейдер миную Cg RunTime, точно так-же как я сделал в случае использования Cg в OpenGL для профилей
Direct3D9, и OpenGL (glslv,glslf,arbvp1,arvfp1).
В общем можно встраивать это в движок