2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




Начать новую тему Ответить на тему
 
 3D -> 2D (устранение багов, приведение к общепринятому виду)
Сообщение03.03.2011, 21:52 


03/03/11
1
Здравствуйте,

Задался целью написать простенькую реализацию wireframe-графики. Скажу сразу, что использовал не матрицы преобразований координат, а функции, вычисляющие углы отклонения точки от направления "взгляда" камеры и преобразующие эти углы в отступы по горизонтали и вертикали на мониторе.

В результате это всё работает, но осталось несколько багов, которые хотелось бы устранить. Если еще точнее - привести всё это к общепринятому виду. :)

Сначала - листинг основных моментов кода (без рутины вроде описания вершин тестового куба и т.п. вещей). Пишу на C/C++ (хотя от C++ тут фактически ничего нет):

Функция GetAngleXflat вычисляет азимутальный угол между направлением из точки src в dst и направлением вдоль оси X ("горизонтальный" угол):

Код:
double GetAngleXflat (ThePoint src, ThePoint dst)
{
   double tempangle;
   if(src.x == dst.x)  // if TAN = +/- infinity
   {
      if(dst.y > src.y) // uppermost part
         tempangle = pi/2;
      else // lowermost part
         tempangle = 3*pi/2;
   }
   else  // usual case
   {
      if(dst.x > src.x)  // right semicircle
         tempangle = atan((dst.y-src.y)/(dst.x-src.x));
      else // left one
         tempangle = atan((dst.y-src.y)/(dst.x-src.x))+pi/2;
   }
   if(tempangle < 0)
      tempangle = tempangle + 2*pi; // right bottom quarter
   return tempangle;
}


Функция GetAngleY вычисляет зенитный угол ("вертикальный" угол), правда, не от направления по Z, как обычно принято, а от направления "вдоль горизонта".

Код:
double GetAngleY (ThePoint src, ThePoint dst)
{
   double tempangle;
   double xydist;
   double zdiff;
   xydist = sqrt(pow((src.x-dst.x),2)+pow((src.y-dst.y),2));
   zdiff = dst.z-src.z;
   if(xydist==0) // just in case camera and some point is the same point
      tempangle = 0;
   else
      tempangle = atan(zdiff/xydist);
   return tempangle;
}


Функция GetAngleSky вычисляет телесный "горизонтальный" угол (он отличается от "обычного" горизонтального, см. картинку после кода):

Код:
double GetAngleSky (double AngleX, double AngleY)
{
   double tempangle;
   tempangle = asin(sin(AngleX)*cos(AngleY));
   re
turn tempangle;
}

Изображение

Именно этот телесный угол должен влиять на координату точки по X - иначе все вертикальные прямые всегда будут вертикальными на мониторе.

Функция UpdCoord для каждой точки по ее координатам x,y,z определяет ее экранные координаты с учетом того, насколько далеко она отстоит от точки, куда смотрит камера.
Два "типа монитора" - сферический и плоский. В сферическом воображаемый луч от точки проходит через сферическую поверхность перед камерой, оставляя на ней отметку в опреденном месте, в плоском - через "плоский прямоугольник". В первом случае зависимость координаты от угла прямая, во втором пришлось немного поупражняться в тригонометрии.

Код:
void UpdCoord(ThePoint& Pnt)
{
   double tempangleX;
   double tempangleY;
   double coeff;
   double delta;

   tempangleX = GetAngleXflat(cam,Pnt);
   tempangleX = tempangleX-CamAngleX; // насколько оно ЛЕВЕЕ центра взгляда
   tempangleY = GetAngleY(cam,Pnt);
   tempangleY = tempangleY-CamAngleY; // насколько оно ВЫШЕ центра взгляда
   tempangleX = GetAngleSky(tempangleX,tempangleY);
   if(MonitorShape==1)
   {
      coeff = tempangleX / FOVx;
   }
   else
   {
      coeff = cos(pi/2-tempangleX)/2/cos(pi/2-FOVx/2);
   }
   Pnt.scrx = 320-coeff*640;
   if(MonitorShape==1) // сферический монитор
   {
      coeff = tempangleY / FOVy;
   }
   else // плоский монитор
   {
      coeff = cos(pi/2-tempangleY)/2/cos(pi/2-FOVy/2);
   }
   Pnt.scry = 240-coeff*480;
}


Собственно, вот что происходит при обновлении экрана:

Код:
void TheDraw (void)
{
   int i;
   Form1->Image1->Picture = 0;
   for (i = 0; i < 4; i++)
      UpdCoord(grid[i]);
   for (i = 0; i < 12; i++)
      UpdCoord(TheCube[i]);
   for (i = 0; i < 20; i++)
      DrawLine(TheCube[CubeLines[2*i]], TheCube[CubeLines[2*i+1]]);
}


Всё бы было хорошо (как в сферическом, так и в плоском мониторе), если бы не одно но. Вернее, два, для двух случаев:

1) Если мы игнорируем телесный угол и строим координату X по углу отклонения точки от вертикальной плоскости, выходящей из камеры, а координату Y - по углу отклонения от такой же горизонтальной плоскости, то все вертикали всегда будут прямыми. Даже если мы смотрим вверх / вниз (наклоняя камеру), что не есть хорошо.
2) Если мы применяем телесный угол, то объекты становятся бочкообразными (см. скрины) и, более того, начинают не совсем адекватно реагировать на опускание / поднимание камеры и т.п. (сжатие, изменение формы).

Мое предположение заключается в том, что я как-то неправильно определяю "видимый угловой размер" линии (ну или угловое отклонение точки) через этот пресловутый телесный угол. Вернее, формула-то там правильная для картинки, но, может быть, стоит применить другую модель вычисления этого самого "углового размера".

Скрины:

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

На последнем скрине видно, что при опускании камеры вниз линии "разъезжаются". Достаточно запустить любую игру и найти в ней две вертикальные балки, чтобы убедиться, что при опускании взгляда линии должны сходиться (да и это следует из здравого смысла).

В общем, ошибка где-то в замене обычного горизонтального угла телесным. С радостью выслушаю замечания / советы.

 Профиль  
                  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ 1 сообщение ] 

Модераторы: Karan, Toucan, PAV, maxal, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


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

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group