Страниц: [1]
  Печать  
Автор Тема: Простой 3D движок  (Прочитано 2308 раз)
da-nie
Full Member
***
Offline Offline

Сообщений: 167



Просмотр профиля
« : Декабря 08, 2015, 02:45:14 pm »

Попробовал сегодня портировать свой думоподобный 3D движок. Строго говоря, я уже пару лет пытаюсь заставить себя переписать на новые принципы свою старую игру 2002 года под DOS. Пока лень побеждает. Но движок выковырять и переписать я себя-таки заставил. Портировать саму игру пока не могу - там есть места прямой работы с видеопамятью  (скажем, для спецэффектов), которые под событие таймера так просто не переделать.
Сам движок в архиве. Разрешение я задал 640x400 (в файле сvideo.h в макросах задаётся) - несколько тормозит на машине без ускорителя (драйвер VESA).
Инструментарий был сделан под DOS (на Watcom C) на "лишь бы было" и я даже сейчас не вспомню, как с ним работать. Smiley Но на всякий случай приложу, как и оригинальную игру (немного доработанную в этом году Smiley ).
После распаковки tar.gz архива, нужно скопировать папку 3DEngine внутри в каталог /usr/share.
Управление: курсор - ходить, alt-присесть, space - задействовать.
Сам движок использует BSP-дерево. Отличия от DOOM в том, что в DOOM лабиринт разбивается до выпусклых многоугольников, а я разбиваю до сегментов. И ещё, в графической части есть небольшое отличие - я использую для стен (не полов и потолков!) такую фигню, как mipmapping - по мере удаления от игрока разрешение текстуры уменьшается, что позволяет на стенах избежать эффектов переливания "случайных" пикселей, как в DOOM. Для полов-потолков я такую фишку тогда не сделал. Движок умеет использовать освещённость секторов, но так как изначально игра 2002 года такого не умела,а я её карту "накатил" поверх движка, то освещённость этой карты всегда на максимуме.
Текстуры я когда-то снимал из DOOM. Все текстуры 128x128 пикселей.
Ну вот такая вот штука, пользы от которой мало, а выбросить жалко. Smiley
« Последнее редактирование: Декабря 08, 2015, 09:45:34 pm от da-nie » Записан

И день и ночь в пути
da-nie
Full Member
***
Offline Offline

Сообщений: 167



Просмотр профиля
« Ответ #1 : Декабря 09, 2015, 11:39:33 am »

На экране ЕС1866. Smiley Там драйвера видеокарты найдены, так что процессор почти не нагружает.

Записан

И день и ночь в пути
da-nie
Full Member
***
Offline Offline

Сообщений: 167



Просмотр профиля
« Ответ #2 : Мая 22, 2017, 09:16:20 pm »

Я-таки поборол неприязнь к яндекс-диску и потому выкладываю версию своего движка для QNX, работающую с использованием порталов.
Управление: курсор, alt-присесть, space-открыть двери/нажать переключатель, page up и page down - голову вверх/вниз.

Движок под QNX: https://yadi.sk/d/fSo8RKzf3JR3Y9
Редактор карт под Windows: https://yadi.sk/d/iR9EXPMt3JR3ea

Скриншот:

Записан

И день и ночь в пути
da-nie
Full Member
***
Offline Offline

Сообщений: 167



Просмотр профиля
« Ответ #3 : Мая 31, 2017, 08:32:54 pm »

А кто-нибудь знает, почему вот такое:

Код:
unsigned long *vpt=адрес, полученный от Direct Draw 7 с помощью Lock();
unsigned long VideoLineWidth=размер строки, полученный от Direct Draw 7 с помощью Lock();
for(unsigned long n=s1;n<=s2;n++,vpt+=VideoLineWidth) *(vpt)=n;

Работает на I5 (со встроенной видеокартой) в 18 (!) раз медленнее (а на AMD FX-4100 с внешней видеокартой в 4 раза медленнее), чем вот это:

Код:
unsigned long *vpt=адрес, полученный от Direct Draw 7 с помощью Lock();
for(unsigned long n=s1;n<=s2;n++,vpt++) *(vpt)=n;

Компилятор VC6. Оптимизация O2 (O3 у него нет Smiley ) Какой он код делает я пока не посмотрел. Однако, я наивно полагал, что для процессора делать inc eax или add eax,ecx для адреса вряд ли даст такую разницу в скорости. Можно, конечно, списать на скорость работы с видеопамятью, но работа идёт с вторичной плоскостью, а она невидима, и к тому же работа идёт через абстракцию Direct Draw. Странно это... Могу только предположить, что адрес, полученный от Direct Draw имеет некую особенность по работе с ним.
Это я обнаружил, что у меня текстурирование горизонтали выполняется гораздо быстрее вертикали. Smiley
Записан

И день и ночь в пути
lastcross
Full Member
***
Offline Offline

Сообщений: 224


Просмотр профиля
« Ответ #4 : Июня 01, 2017, 02:47:04 pm »

А кто-нибудь знает, почему вот такое:
...
Работает на I5 (со встроенной видеокартой) в 18 (!) раз медленнее (а на AMD FX-4100 с внешней видеокартой в 4 раза медленнее), чем вот это:
...

Предполагаю, что причиной этому кэш-мисы. И врдяли тут есть сильная зависимость от внешней или встроенной видеокарты. Насколько я правильно понимаю, все инструкции от лока - будут работать непосредственно на CPU. А значит времена загрузка/выгрузка в кэш-CPU данных должна отразится, особенно если порции этих данных не способствуют оптимальной работе CPU. В теории конечно, также может и повлиять способы доступа к видеопамяти. Но это, имхо, вторичная по значимости причина.

Ну и если это кэш-мис, то завязывали бы Вы с DirectX7 и рендером на CPU. Практически любые видеоадаптеры поддерживают DirectX9 и Shader model 2.0. Большинство блендов можно решить посредством обсчета на GPU
Записан
da-nie
Full Member
***
Offline Offline

Сообщений: 167



Просмотр профиля
« Ответ #5 : Июня 01, 2017, 07:44:34 pm »

Цитировать
Предполагаю, что причиной этому кэш-мисы.

Предположение интересное, но вряд ли верное. Дело в том, что размер линии 640*4 байта. А линий 480. И никаким постоянным кэш-промахом тут и не пахнет. Smiley

Цитировать
В теории конечно, также может и повлиять способы доступа к видеопамяти.

Вот это гораздо более вероятная причина. SmileyПотому как именно этим в сущности и отличаются компьютеры на процессорах I5 и FX. И FX тут слабее I5, но FPS у движка выше в 1.5 раза! У софтверного движка! Smiley

Цитировать
Ну и если это кэш-мис, то завязывали бы Вы с DirectX7 и рендером на CPU.

Я давным-давно играюсь и с OpenGL. Smiley Вот, например: https://yadi.sk/d/9HgK51XX3JDa4g  Правда, с версией 1. Версии 2-3-4 я не стал трогать - не было нужды. А софтверные движки мне просто интересны. Smiley Как и старые компьютеры (Amiga, ZX-Spectrum, I286 - I486). И очень интересно запускать на I486 свой движок, перенесённый обратно под MS-DOS и смотреть как он работает. Smiley Да, и Windows у меня XP. Smiley Ретроград-с. Smiley
« Последнее редактирование: Июня 01, 2017, 08:00:25 pm от da-nie » Записан

И день и ночь в пути
lastcross
Full Member
***
Offline Offline

Сообщений: 224


Просмотр профиля
« Ответ #6 : Июня 01, 2017, 08:09:36 pm »

Предположение интересное, но вряд ли верное. Дело в том, что размер линии 640*4 байта. И никаким кэш-промахом тут и не пахнет. Smiley
Цитировать
Вот это гораздо более вероятная причина. SmileyПотому как именно этим в сущности и отличаются компьютеры
Мне показалось что Вас удивило также снижение скорости для разного кода, но на одинаковой конфигурации. Из того что я увидел - различие лишь в обходе блока памяти. При этом медленнее работает именно тот, который теоретически может потребовать обращение к памяти за пределами кэша на каждой (или с каким-либо шагом) итерации.
Это мое предположение. Ну или я не правильно понял причину вашего удивления - сравнение между разными конфигурациями.
И да, тут гадать на кофейной гуще нечего. Профайлер и - в путь.
Записан
da-nie
Full Member
***
Offline Offline

Сообщений: 167



Просмотр профиля
« Ответ #7 : Июня 01, 2017, 08:19:37 pm »

Цитировать
Мне показалось что Вас удивило также снижение скорости для разного кода, но на одинаковой конфигурации.

Верно. Но также верно и то, что на более слабом компьютере с внешней видеокартой FPS оказался выше,чем на более сильном со встроенной, а также на более слабом компьютере разница в выполнении циклов в 4 раза, а не в 18, как на более сильном.

Цитировать
И да, тут гадать на кофейной гуще нечего. Профайлер и - в путь.

Совершенно верно.

Записан

И день и ночь в пути
da-nie
Full Member
***
Offline Offline

Сообщений: 167



Просмотр профиля
« Ответ #8 : Июня 01, 2017, 08:25:43 pm »

Цитировать
то завязывали бы Вы с DirectX7 и рендером на CPU.

Я вам сейчас более страшную вещь покажу: http://radiokot.ru/forum/viewtopic.php?f=2&t=104238 и http://radiokot.ru/forum/viewtopic.php?f=2&t=110471  Grin

Ну и совсем уж 99% людей непонятная, зачем она нужна,  штука: http://radiokot.ru/forum/viewtopic.php?f=2&t=79144  Grin

А рендер на CPU - это так, цветочки.  Cool
Записан

И день и ночь в пути
da-nie
Full Member
***
Offline Offline

Сообщений: 167



Просмотр профиля
« Ответ #9 : Июня 06, 2017, 09:36:55 pm »

Забавно, если заменить адрес от DirectDraw на адрес в обычной памяти, FPS резко увеличивается и циклы становятся одинаковыми по времени выполнения.
Записан

И день и ночь в пути
da-nie
Full Member
***
Offline Offline

Сообщений: 167



Просмотр профиля
« Ответ #10 : Июня 17, 2017, 08:32:15 pm »

Решил я тут попробовать всё-таки отложенную отрисовку (Deferred Rendering) в OpenGL по совету lastcross. И пока читал про неё, вспомнил, что была у меня написана в качестве эксперимента в 2003 году одна программка - она считывала буфер кадра OpenGL (кстати, ужасно тормознутая операция была на видеокартах ATI - сейчас не знаю, у меня nVidia. Я как-то такую функцию (glReadPixels) для расчёта освещённости оптодатчика в моделирующей программе использовал - разница в скорости с ATI была в 100 раз!), считывала Z-буфер, рассчитывала освещение и модулировала буфер кадра этим освещением. Предварительно полигоны выводились в условных цветах (код цвета равен номеру полигона). Итого, проходя по буферу было известно, какому полигону принадлежала точка на экране. Но работала она очень-очень небыстро. Поглядел я на программу и сообразил, что буфер кадра вообще можно было не считывать - всё равно цвета всех полигонов жёстко привязаны к номеру, а номер и так известен. Но это, конечно,только подобие отложенной отрисовки - никаких шейдеров, только CPU. И была там у меня проблема - как найти, кто освещает точку? Ведь для этого нужно проверять пересечение линии к источнику света с полигонами. И это совсем не быстро было даже для двух полигонов (это ведь для каждой точки нужно делать). А как сейчас это делают в настоящей отложенной отрисовке с шейдерами? Из статей на русском языке я что-то ничего внятного пока не нашёл (вот, например, классическое объяснение).

А делал я тогда так:
Код:
int x,y;
 glClearColor(0,0,0,0);
 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
 glEnable(GL_DEPTH_TEST);
 glDepthFunc(GL_LEQUAL);
 glMatrixMode(GL_MODELVIEW);
 glLoadIdentity();
 glTranslatef(-5,-3,-10);
 glRotatef(30,1,0,0);
 //получим параметры матриц
 glGetIntegerv(GL_VIEWPORT,viewport);
 glGetDoublev(GL_MODELVIEW_MATRIX,mvmatrix);
 glGetDoublev(GL_PROJECTION_MATRIX,projmatrix);
 //перенесём их в "нормальные" структуры
 MATRIX Project(4,4);
 MATRIX ModelView(4,4);
 for(x=0;x<4;x++)
  for(y=0;y<4;y++)
  {
   Project.SetElement(y+1,x+1,projmatrix[x*4+y]);
   ModelView.SetElement(y+1,x+1,mvmatrix[x*4+y]);
  }
 MATRIX PxM;
 Project.MatrixProduct(&ModelView,&PxM);
 MATRIX INV_PM;
 PxM.GetInvertMatrix(&INV_PM);
 //запишем эту матрицу в специальный массив
 double INV[5][5];//[y][x]
 for(x=1;x<=4;x++)
  for(y=1;y<=4;y++) INV_PM.GetElement(y,x,&INV[y][x]);

 DrawScene(0);//выводим сцену в нормальных цветах
 glMatrixMode(GL_PROJECTION);
 glPushMatrix();
 glLoadIdentity();
 gluOrtho2D(0.0,SCREEN_WIDTH,0.0,SCREEN_HEIGHT);
 glMatrixMode(GL_MODELVIEW);
 glReadPixels(0,0,SCREEN_WIDTH,SCREEN_HEIGHT,GL_RGB,GL_UNSIGNED_BYTE,PictureArray);
 glMatrixMode(GL_PROJECTION);
 glPopMatrix();
 glMatrixMode(GL_MODELVIEW);

 glClear(GL_DEPTH_BUFFER_BIT);
 DrawScene(1);//выводим сцену в специальных цветах

 glMatrixMode(GL_PROJECTION);
 glPushMatrix();
 glLoadIdentity();
 gluOrtho2D(0.0,SCREEN_WIDTH,0.0,SCREEN_HEIGHT);
 glMatrixMode(GL_MODELVIEW);
 glReadPixels(0,0,SCREEN_WIDTH,SCREEN_HEIGHT,GL_RGB,GL_UNSIGNED_BYTE,ColorArray);
 glReadPixels(0,0,SCREEN_WIDTH,SCREEN_HEIGHT,GL_DEPTH_COMPONENT,GL_FLOAT,DepthArray);
 glMatrixMode(GL_PROJECTION);
 glPopMatrix();
 glMatrixMode(GL_MODELVIEW);

 unsigned char *color_ptr=ColorArray;
 unsigned char *picture_ptr=PictureArray;
 GLfloat *depth_ptr=DepthArray;

 float xn=2*(0-viewport[0])/viewport[2]-1;
 float yn=2*(0-viewport[1])/viewport[3]-1;
 
 float px=INV[1][1]*xn+INV[1][2]*yn+INV[1][4];
 float py=INV[2][1]*xn+INV[2][2]*yn+INV[2][4];
 float pz=INV[3][1]*xn+INV[3][2]*yn+INV[3][4];
 float w=INV[4][1]*xn+INV[4][2]*yn+INV[4][4];

 float dy_px=INV[1][2]*(2.0/viewport[3]);
 float dy_py=INV[2][2]*(2.0/viewport[3]);
 float dy_pz=INV[3][2]*(2.0/viewport[3]);
 float dy_w=INV[4][2]*(2.0/viewport[3]);

 float dx_px=INV[1][1]*(2.0/viewport[2]);
 float dx_py=INV[2][1]*(2.0/viewport[2]);
 float dx_pz=INV[3][1]*(2.0/viewport[2]);
 float dx_w=INV[4][1]*(2.0/viewport[2]);

 for(y=0;y<SCREEN_HEIGHT;y++,px+=dy_px,py+=dy_py,pz+=dy_pz,w+=dy_w)
 {
  float c_px=px;
  float c_py=py;
  float c_pz=pz;
  float c_w=w;
  for(x=0;x<SCREEN_WIDTH;x++,depth_ptr++,c_px+=dx_px,c_py+=dx_py,c_pz+=dx_pz,c_w+=dx_w)
  {
   int index=(int)(*color_ptr)-1;
   color_ptr++;
   index+=(int)((*color_ptr))<<8;
   color_ptr++;
   index+=(int)((*color_ptr))<<16;
   color_ptr++;
   if (index<0)//нет ничего
   {
    picture_ptr+=3;
continue;
   }
   float z=2*(*depth_ptr)-1;//узнаем глубину пикселя
   //найдём его мировые координаты
   float cn_w=c_w+INV[4][3]*z;
   if (cn_w==0)
   {
    picture_ptr+=3;
continue;
   }
   cn_w=1/cn_w;
   float cn_px=(c_px+INV[1][3]*z)*cn_w;
   float cn_py=(c_py+INV[2][3]*z)*cn_w;
   float cn_pz=(c_pz+INV[3][3]*z)*cn_w;
   //узнаем освещённость точки
   float r,g,b;
   GetVertexColor(index,cn_px,cn_py,cn_pz,&r,&g,&b);
   *picture_ptr=(*picture_ptr)*r;
   picture_ptr++;
   *picture_ptr=(*picture_ptr)*g;
   picture_ptr++;
   *picture_ptr=(*picture_ptr)*b;
   picture_ptr++;
  }
 }
 //накладываем на сцену её освещённость
 glMatrixMode(GL_PROJECTION);
 glPushMatrix();
 glLoadIdentity();
 gluOrtho2D(0.0,SCREEN_WIDTH,0.0,SCREEN_HEIGHT);
 glMatrixMode(GL_MODELVIEW);
 glDrawPixels(SCREEN_WIDTH,SCREEN_HEIGHT,GL_RGB,GL_UNSIGNED_BYTE,PictureArray);
 glMatrixMode(GL_PROJECTION);
 glPopMatrix();
 glFlush();

А получалось так для двух источников света разных цветов и двух полигонов тоже разных цветов:
« Последнее редактирование: Июня 17, 2017, 08:48:14 pm от da-nie » Записан

И день и ночь в пути
lastcross
Full Member
***
Offline Offline

Сообщений: 224


Просмотр профиля
« Ответ #11 : Июня 20, 2017, 01:33:07 pm »

А как сейчас это делают в настоящей отложенной отрисовке с шейдерами? Из статей на русском языке я что-то ничего внятного пока не нашёл

На русском например так. Сразу скажу - без шейлеров этот подход не имеет смысла. Одно из требований - наличие multiple render targets, который не требуется "гонять" из памяти GPU в память CPU расчитанные упакованные данные для освещения.
Записан
da-nie
Full Member
***
Offline Offline

Сообщений: 167



Просмотр профиля
« Ответ #12 : Июня 20, 2017, 05:10:46 pm »

Спасибо! Эту статью я ещё не читал.
До шейдеров дело потом дойдёт - я с ними почти не знаком. Пока же всё то же самое можно прогнать на CPU и посмотреть нюансы реализации.

 Cheesy Боресков (а это, оказывается, его сайт - а я когда-то по его книжке "Компьютерная графика. Полигональные модули" учился) жжёт. Smiley http://steps3d.narod.ru/tutorials/c-minus-minus.html

А это что, у Борескова шутка такая?   Huh?
Цитировать
Код:
std::string a = "abc";
std::string b = a;
Для многих реализаций обе переменные a и b будут оказывать на один и тот же буфер в памяти, по-возможности, отслеживая моменты, когда необходимо "расщепить" общий буфер, дав каждой переменной по своей копии.

Однако в случае многонитевых приложений подобная политика запросто приводит к возникновению т.н. raise condition, например, одна нить может уничтожить одну переменную, а другая - что-то записать в нее. При этом сущесвтует вероятность того, что произойдет обращение к уже освобожденному блоку памяти и падению программы.

Можно, конечно возразить, что никто не обещал, что STL будет успешно работать в многонитевых приложениях. Однако в ряде реализаций не выдается даже предупреждения на стадии компиляции о возможных проблемах.

И если STL нельзя использовать в многонитевых приложениях (а многие реальные приложения являются многонитевыми, много Вы видели общаний что callback выдет вызван на той же нити, на которой Вы его поставили ?

В данном случае попытка оптимизации приводит к скрытым и тяжело обнаруживаемым проблемам.

Я-то наивно ожидаю, что a и b разные объекты.

И я так понял, что тени в отложенной отрисовке наносятся обычной текстурой тени (как и раньше). Странно. Это ведь требует отрисовки сцены со стороны каждого используемого источника света. А если там полигонов миллионы? Неужели не тормозит?
« Последнее редактирование: Июня 20, 2017, 10:16:34 pm от da-nie » Записан

И день и ночь в пути
lastcross
Full Member
***
Offline Offline

Сообщений: 224


Просмотр профиля
« Ответ #13 : Июня 21, 2017, 02:12:03 pm »

А это что, у Борескова шутка такая?   Huh?

Зависит от какой стандарт вы используете и/или какой компилятор, а возможно какую имплементацию stl. Насколько я понимаю - речь об COW в std::string. Тут есть объяснения со ссылкой на пункты стандарта. Когдато это было не запрещено (но это не значит что имплементация должна содержать счетчики), с C++11 стало запрещено.

Цитировать
И я так понял, что тени в отложенной отрисовке наносятся обычной текстурой тени (как и раньше). Странно. Это ведь требует отрисовки сцены со стороны каждого используемого источника света. А если там полигонов миллионы? Неужели не тормозит?
Тени насколько мне известно никак не завязаны на то, какой Вы тип ренедеринга выбрали. И в том и в другом случае - милион полигонов. Тени текстурные - потому как в сочетании с DS их проще смешивать (да и вообще они выглядят естественне и более контролируемые с меньшими требованиями к объектам сцены)
Само DS упрощает лишь отношение, для расчета освещения, уходя от зависимости NxL (количество вершин на количество источников света) .. к почти K+L (количество проходов  + количество источников).
При этом, упакованные в MRT данные позволяют формировать дополнительные пост-эффекты, всякого рода размытия (DOF) или эмбиент-ы (SSAO), так как G-буффер у нас расчитан на предыдущих этапах для DS, а значит для этих эффектов он будет практически бесплатным
« Последнее редактирование: Июня 21, 2017, 02:14:22 pm от lastcross » Записан
da-nie
Full Member
***
Offline Offline

Сообщений: 167



Просмотр профиля
« Ответ #14 : Июня 21, 2017, 09:53:23 pm »

Цитировать
Зависит от какой стандарт вы используете и/или какой компилятор, а возможно какую имплементацию stl.

Угу. Sad Я уже посмотрел.

Цитировать
Тени текстурные

Жаль. Я думал, есть простой способ определить, затенена точка конкретным источником света или нет и получить точную тень.

Цитировать
Само DS упрощает лишь отношение, для расчета освещения,

Это, наверное, только когда происходит многократная перерисовка пикселя по мере вывода сцены.

Цитировать
И в том и в другом случае - милион полигонов.

Да, но этот миллион нужно будет вывести для каждого источника света каждый раз, чтобы построить карту теней. Неужели при этом получается приличный FPS?
« Последнее редактирование: Июня 21, 2017, 10:02:27 pm от da-nie » Записан

И день и ночь в пути
Страниц: [1]
  Печать  
 
Перейти в: