На фуллскриновом разрешении 640*480 выдаёт 60 кадров в секунду, и это на 2.2 Гигагерцах. Понизил частоту проца до 800 мегагерц ~15 кадров в секунду (иногда и 10). Запустил на пентиуме MMX - 1 кадр в секунду(!). И это при том, что графика-то на уровне вульфа3д, и это без воспроизведения звуков, просчёта AI, дверей, та и размер карты явно меньше. Тогда как рейкастинговые игры с нормальным алгоритмом для пня - раз плюнуть, на разрешениях 800*600.
Конкретно в данном примере используется SDL, в дополнительном cpp-файле (quickcg.cpp) реализован оптимизированный вывод пикселей на экран, как я понял (ну и заодно рисование графических примитивов, которые в конкретно этом примере не юзаются). Как можно оптимизировать сам процесс выборки лучей ? Понизить точность просчёта во благо производительности.
Добавлено (17.12.2013, 10:34) --------------------------------------------- Хм... Ещё одна особенность, на той версии SDL, что предоставлялась с исходником , фреймрейт вдвое шустрее , нежели на той, с которой компилировал я. У меня версия SDL 2.0.1, кажись. Но всё равно слишком маленький fps, как для сотен мегагерц.
Там когда вплотную к "стене" прижимаешься, шибко гладкая перспектива. Может из-за этого так медленно рисует ? То что просчитывает столбцы, кратные одному пикселю?
Сообщение отредактировал Alkosha - Вторник, 17 Декабря 2013, 12:45
рекаст делается для рендера, т.е. из каждого пикселя пускается луч, или просто пара лучей от самого игрока или еще какого объекта?
Для рендера слишком жирно. По-моему, тут всё же предоставлен пример того, как реализован рейкаст в вульфе3д, то есть по столбцам. Из "глаз" игрока пускаются два луча, ну и в соответствии с этим вертикально масштабируется определённый столбец при "столкновении" луча со стеной (+прорисовка пола и потолка)
ЦитатаXakep ()
можно разбить уровень на бинарное дерево
это рационально делать для масштабных локаций. Массив карты не шибко-то и большой. Тем более низкий fps даже если в упор на стенку смотреть, а не только вдаль.
ЦитатаXakep ()
обычно такой рекаст делают для пред расчета освещенности сцены и запекания ее потом в lightmap
в современном геймдеве - да (точнее начиная с первого квейка). Но тут речь идёт об олд-фажном подходе рисования коридоров, а не освещения (а-ля, думо-подобные шутаны, хотя в думе уже юзалось бинарное дерево, но это сложно для меня).
ЦитатаXakep ()
Еще вариант, перенести все расчеты на GPU через шейдеры или OpenCL/CUDA.
Не вариант. Только CPU... Если уж и задействовать GPU, то всё вышеописанное можно и на openGL осуществить, полигональной графикой.
ну и на самом сайте много чего интересного можно найти (http://ray-tracing.ru/)
Всё же рей-трейсинг и рей-кастинг - несколько разные вещи. Хоть и там и там происходит просчёт столкновения луча. Рейкастинг ориентирован на лучи фронтальной перспективы. Рейтрейсинг же определяет столкновения лучей света с поверхностью трёхмерной модели.
Сообщение отредактировал Alkosha - Пятница, 27 Декабря 2013, 10:38
а разбиение на дерево как раз отсекает не нужные части сцены.
Мне казалось с этим как раз рейкаст и справляется. Ведь луч идёт от игрока к первому встречному объекту, то есть всё что за видимым объектом - автоматически отбрасывается. Ну это при визуализации , разумеется.
то что у тебя так тормозит вряд ли из-за рейкастига.
Наверное процедурки quickCG.cpp на самом деле не такие уж и быстрые. Потом на досуге проверю с какой скоростью рисуется попиксельно.
Добавлено (17.12.2013, 13:07) --------------------------------------------- В вульфенштайне были ассемблерные подпрограммы для вывода пикселя на экран, только это с расчётом того, что прога будет работать на i80286-ом проце. Мне б хотя бы для 166-ти мегагерцового пентиума оптимизировать.
непонятная шляпка получается. Скачал и скомпилировал под ту же версию SDL (1.2.9) что и прилагалась к исходнику, с таким же самым разрешением, и всё той же quickCG.cpp , всё то же самое. Тупо скопипастил. Тот билд, что на исходнике работает ровно вдвое быстрее, чем тот что скомпилирован у меня.(конечно на 800 мегагерцах 30 кадров в секунду - ужасный показатель для этого примера, но всё же.) Создавал проект в кодблоксе по шаблону SDL Project (тот что прилагается к примеру тоже был скомпилирован в кодблоксе). Может ли быть это от того, что линкер ненужные либы подключает ?
Добавлено (18.12.2013, 12:06) --------------------------------------------- И не сказать, что пиксели рисуются медленно. Всё с того же сайта скомпилировал пример без текстурирования стен (пол и потолок отсутствуют соответственно) - на 2.2 гигагерцах зашкаливает за 100 кадров в секунду (фреймрейт зависит от уровня закраски, немного понижается, когда вплотную к "стене" подойти, но не ниже 100 кадров всё равно).
Сообщение отредактировал Alkosha - Среда, 18 Декабря 2013, 13:24
В Кодеблокс Project->Build options->Compiler flags
Данкешон. Все флаги категории optimize включил - и эзешник стал легче (500 кб , а был почти два метра), и фреймрейт стал такой же, как и в примере... Но всё равно , 25 кадров для 800 мегагерц - маловата будит! Такой фреймрейт удовлетворителен разве что для 7-ми мегагерцовой MС68000.
Слишком много вычислений в теле цикла... В частности умножения... На каждый пиксель "стен"
Код
int d = y * 256 - h * 128 + lineHeight * 128; //256 and 128 factors to avoid floats int texY = ((d * texHeight) / lineHeight) / 256; int color = texture[texNum][texWidth * texY + texX]; //make color darker for y-sides: R, G and B byte each divided through two with a "shift" and an "and" if(side == 1) color = (color >> 1) & 8355711; buffer[x][y] = color;
На каждый пиксель потолка(потолка) приходится такое:
Код
currentDist = h / (2.0 * y - h); //you could make a small lookup table for this instead
Вот только для какой переменной подходит операция побитного сдвига.
Код
int d1=lineHeight * 128; int d2=h * 128; for(int y = drawStart; y < drawEnd; y++) { int d = y *256 - d2 + d1; //256 and 128 factors to avoid floats int texY = ((d * texHeight) / lineHeight) >>8; Uint32 color =texture[texNum][texWidth * texY + texX]; if(side == 1) color = (color >> 1) & 8355711; buffer[x][y] = color; }
То бишь только для texY если скроллировать переменную "y" - там шляпа с текстурами ваще конкретная. Что странно, ибо "y" интовый, то есть не дробное число. И переменные "d1" и "d2" должны быть отдельно. Если до тела цикла записать так:
Код
d1 = lineHeight * 128 + h * 128
То получается, говоря заумным языком геймдевелопера, текстурная шляпенция.
Добавлено (25.12.2013, 23:34) --------------------------------------------- Подумываю на асме выполнять вычисления... Но я ни разу не пробовал использовать ассемблерные вставки. Более того, не знаю как затем результат вычисления поместить из регистров в переменные.
если переходить на openGL , то и рейкастинг как таковой не нужен (за исключением игровых моментов, типа просчёта выстрелов или коллизии с объектами). Я хочу , чтоб моя прога работала на пентиуме MMX , без 3д акселераторов. Только ЦП и ничего лишнего.
Сообщение отредактировал Alkosha - Пятница, 27 Декабря 2013, 10:37
Как бы полностью от всех дробей и умножений избавиться? Там, где числа кратные 2 в определённой степени , то понятно... А вот в таких ситуациях скроллом никак не получится ?
Добавлено (26.12.2013, 15:31) --------------------------------------------- Очень много времени занимает чтение\запись ОЗУ... Нужно данные как-то задержать в кэше.
Сообщение отредактировал Alkosha - Четверг, 26 Декабря 2013, 15:19
Ну тут очевидно, что самые затратные участки там, где происходит выборка пикселей из текстур (пола\потолка\спрайтов). Со сплошным закрашиванием этот рейкастинг достаточно шустрый.
Добавлено (27.12.2013, 12:31) --------------------------------------------- Ага. У операции сдвига приоритет меньше , чем у сложения и вычитания. Вот от чего была "текстурная шляпа". Нужно было просто в скобки заключить.
Дело оказывается не в рейкастинге. Сама SDL медленно выводит пиксели. Нужно непосредственно записывать в видео-озу vga-контроллера. Похоже придётся прибегнуть к асму, либо искать альтернативу SDL'ке для попиксельного вывода.
У тебя просто проц уже не справляется, уменьши разрешение хотя бы. У тебя какая хоть видюха, а то даже Descent 3 в 1999г. уже использовал аппаратное ускорение.
Всё проц справляется.
На софтверном рендеринге Descent 1 и Descent 2 на разрешениях 640*480 летают. А там, как всем известно, даже полигональное 3д. Рейкастинги тем более он шпарит только так. Порт дюка eduke32 на разрешении 800*600 тоже работает идеально (чуток проседает правда, когда много wav-семплов воспроизводит, но если понизить воспроизведение сэмплов до 22 килогерц, то всё ровненько идёт) Порт дума Doom95 тоже идеально работает на разрешении 640*480. И даже сравнить тот же дум, где разная высота стен, векторное построение карт, куча спрайтов, ИИ, воспроизведение звуковых сэмплов, и тот пример, который я разбираю.
Добавлено (04.01.2014, 11:43) --------------------------------------------- Даже полигональное 3дэ для того проца раз плюнуть, если не используется коррекция перспективы при текстурировании. NFS 1 (NFS SE), и подобные гоночки, например.
Сообщение отредактировал Alkosha - Суббота, 04 Января 2014, 11:44
Судя по описанию eduke32 использует OpenGL Doom95 использует DirectX.
Там есть опциональный выбор рендеринга. На openGL вообще не прёт, ибо видяха не тянет это API. (видяшка ATI Rage II) и при выборе софтверного рендеринга в eduke, там именно рейкастинговый графон, а не спрайтово-полигональное 3д, как если бы было на openGL. А на DX видяха вообще адски медленно рисует. Есть несколько игор с выбором D3D рендерера и софтверного. На D3D значительно тормознее, тот же MDK, или motorace. Хоть она фильтрует текстуры, производит коррекцию перспективы, тонирование Гуро дизерингом. Но по скорости уступает тому, что рисует непосредственно проц, поэтому всегда выбираю именно software render.
Кстати установленная ОСь win98 SE. Где всё ещё была поддержка ДОС-программ. Или вы хотите сказать, что DOS эмулируется , и картинку выводит исключительно DX ?
Сообщение отредактировал Alkosha - Суббота, 04 Января 2014, 16:11
ДОС эмитируется когда она запускается в окне. чтобы был чистый ДОС надо выгрузить винду.
Но запускаю-то я в фуллскрине. Тем паче, я даже при выгруженной винде (перезагрузка в режиме MS-DOS) запускал те же самые descent'ы , и квейк , и nfs... Разница лишь в том, что в этом режиме не грузятся дрова на мышу, потому в квейке , например, только клавой можно управлять. А fps примерно такой же.
ЦитатаOpenGOO ()
Кстати, в движках Duke Nukem 3D и Doom не использовался рейкастинг.
О_о Эммм... Вы наверное спутали с третьим думом и с дюком нюкемом форэва.
В дюке нюкеме 3д есть возможность вертеть камеру вверх/вниз, и там нет сдвига перспективы по вертикали, то есть нет точки схода по оси Y, ровно так же, как нет её hertic'e , hexen'e, или воксельном, но в то же время рейкастинговом comanche. Или rise of triad, в котором движок основан вообще на wolf3d. И ещё одна отличительная черта рейкастинговых движков - нет этажа над этажом. уровни двухмерные при виде сверху в любом случае.
Промелькала такая мысля. Но будет ли там возможность всё сделать на свой лад? То есть не очередной клон дума, а полностью видоизменить от и до. ZDoom этож вроде типа готового конструктора (а-ля, гей-мейкеры всякие)
Сообщение отредактировал Alkosha - Суббота, 04 Января 2014, 22:52
Тут скорее всего придётся самому писать прогу, для замены пикселя символом. Я видел лишь единственный пример на openGL, где отрендеренная картинка конвертировалась в символы посредством шейдеров glsl
Сообщение отредактировал Alkosha - Понедельник, 06 Января 2014, 13:26
Вы сперва определитесь с понятием "3д". Зачастую анаглифное кино тоже называют ТРИ ДЭ.
таки да, первый дум можно отнести к три дэ, имхо. Так как у спрайтов там как минимум три параметра положения в пространстве, как и в дюке нюкеме ТРИ ДЭ. Но это не полигональное три дэ, а рейкастинг однозначно. Для скептиков есть чит IDCLIP , при прохождении за пределы стен замечательно видно, что рендерится картинка столбцами, как и положено рейкастингу.
Но как бы там ни было, итоговая картинка всё равно проецируется на двухмерную плоскость, то бишь дисплей юзера.
есть исходник 2д платформера под библиотеку SFML. Хочу этот самый алгоритм заюзать, только под управлением движка irrlicht. В первую очередь компилятор ругался на переменную типа String. ни #include "windows.h" ни #include <string.h> не помогло, потому я заменил String на char*
Далее переменную типа FloatRect объявил так: rect<s32> rectan присвоил ей значение следующим образом: rectan=rect<s32>(7*32,9*32,40,50);
сейчас ругается на строчку rectan.left += dx * time; ,
E:\TURBO\platform\main.cpp|75|error: 'class irr::core::rect<int>' has no member named 'left'|
но ведь структуре rect<s32> присутствует поле left
Код
#include <SFML/Graphics.hpp>
using namespace sf; float offsetX=0, offsetY=0; const int H = 12; const int W = 40;
String TileMap[H] = {
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", "B B B", "B B B", "B B B", "B B B", "B 0000 BBBB B", "B B B", "BBB B B", "B BB BB B", "B BB B", "B B BB BB B", "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
PLAYER(Texture &image) { sprite.setTexture(image); rect = FloatRect(7*32,9*32,40,50);
dx=dy=0.1; currentFrame = 0; }
void update(float time) {
rect.left += dx * time; Collision(0);
if (!onGround) dy=dy+0.0005*time; rect.top += dy*time; onGround=false; Collision(1);
currentFrame += 0.005*time; if (currentFrame > 6) currentFrame -=6 ;
if (dx>0) sprite.setTextureRect(IntRect(40*int(currentFrame),244,40,50)); if (dx<0) sprite.setTextureRect(IntRect(40*int(currentFrame)+40,244,-40,50));
if (!onGround) dy=dy+0.0005*time; rect.top += dy*time; onGround=false; Collision(1);
currentFrame += 0.005*time; if (currentFrame > 6) currentFrame -=6 ;
if (dx>0) sprite.setTextureRect(IntRect(40*int(currentFrame),244,40,50)); if (dx<0) sprite.setTextureRect(IntRect(40*int(currentFrame)+40,244,-40,50));
class MyEventReceiver : public IEventReceiver { public: // наш собственный обработчик событий virtual bool OnEvent(const SEvent& event) { // просто запоминаем состояние любой клавиши - нажата/отжата if (event.EventType == EET_KEY_INPUT_EVENT) KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
return false; }
// метод возвращающий состояние для запрошенной клавиши virtual bool IsKeyDown(EKEY_CODE keyCode) const { return KeyIsDown[keyCode]; }
//конструктор, в цикле сбрасываем статус для всех клавиш MyEventReceiver() { for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i) KeyIsDown[i] = false; }
private: // массив для хранения статусов клавиш bool KeyIsDown[KEY_KEY_CODES_COUNT]; };
public : void gravity() { if(flrf==false) { gg.y-=0.5; } }
};
MYphys phys;
class sky { public:void rain(){ if(rainflag==true){ rainflag = false;
ps = smgr->addParticleSystemSceneNode(false); IParticleEmitter* em1 = ps->createBoxEmitter( aabbox3d< f32 >(0,00,00,resw,1,1), // размер эмиттера(куба) vector3df(0.5f,-2.0f,0.0f), // начальное направление 50,100, // частота испускания (мин,макс) SColor(0,255,255,255), // самый темный цвет SColor(0,255,255,255), // самый яркий цвет 400,800,30, // време жизни (мин,макс), угол dimension2df(32.f,32.f), // минимальный размер частиц dimension2df(96.f,32.f)); // максимальный размер частиц
ps->setEmitter(em1); // отдаем эмиттер системе //em->drop(); // а лично нам он не нужен
Добавлено (21.02.2014, 21:07) --------------------------------------------- Там гибрид из того, что я начинал ранее, потом забил. Вот нашёл чё-то на SFML , хочу перевести свой платформер.