Работа с произвольной камерой
Рассмотрим любую камеру как точку - центр проецирования и экран - плоский прямоугольник в 3D пространстве, на плоскость которого идет проецирование. Наша стандартная камера, например, задается точкой (0,0,-dist) и экраном с вершинами (-xSize/2,ySize/2), ..., (xSize/2,-ySize/2). Можно задать эту систему тремя векторами, задающими с точки зрения камеры направления вперед, вправо и вверх; вектор "вперед" соединяет центр проецирования и центр экрана, вектор "вправо" соединяет центр экрана и правую его границу, вектор "вверх", соответственно, центр экрана и верхнюю его границу. Обозначим эти вектора как p, q и r соответственно, а центр проецирования за s. Вот пример для стандартной камеры.
|
Здесь (для стандартной камеры; обозначим ее вектора как Sp, Sq, Sr, Ss)
Sp = p = (0,0,dist) Sq = q = (xSize/2,0,0) Sr = r = (0,ySize/2,0) Ss = s = (0,0,-dist)
Любые три взаимно перпендикулярных вектора и точка - центр координат задают в 3D пространстве систему координат. Так что объект мы можем рассматривать в системе обычных координат (x,y,z), в системе координат стандартной камеры (Sp,Sq,Sr) или в системе (p,q,r), соответствующей какой-то произвольной камере. В любом случае, если (a,b,c) - координаты точки в системе координат камеры (точнее, в системе координат с центром в точке s и базисом (p,q,r)), то координаты проекции точки на экране равны
screenX = xSize/2 + xSize/2 * a/c screenY = ySize/2 - ySize/2 * b/c
В случае стандартной камеры переход от обычной системы координат к системе координат камеры очевиден:
a = x / (xSize/2) b = y / (ySize/2) c = (z + dist) / dist
Подставив это в формулы для screenX, screenY, получим как раз те самые формулы для проекции на стандартную камеру.
Поскольку со стандартной камерой нам достаточно удобно и понятно работать, для произвольной камеры мы должны сделаеть такое преобразование пространства, что как бы совместит произвольную камеру и стандартную камеру. То есть, такое преобразование, что вектора p, q, r перейдут в Sp, Sq, Sr, а точка s в точку Ss.
Посчитаем матрицу для *обратного* преобразования; оно должно переводить Sp, Sq, Sr, Ss в p, q, r, s. Преобразование, переводящее Ss в s (и наоборот) - это обычный паралелльный перенос; остается написать преобразование перевода Sp, Sq, Sr в p, q, r. Пусть у нас есть координаты p, q, r в системе координат (x,y,z):
p = (px,py,pz) q = (qx,qy,qz) r = (rx,ry,rz)
Для Sp, Sq, Sr координаты (в этой же системе) известны и равны следующему:
Sp = (0,0,dist) Sq = (xSize/2,0,0) Sr = (0,ySize/2,0)
Пусть T - искомая матрица перевода,
[ a b c ] T = [ d e f ], a..i - какие-то неизвестные. [ g h i ]
Поскольку T переводит Sp, Sq, Sr в p, q, r; то есть
p = T*Sp q = T*Sq r = T*Sr
то, подставляя, например, p и Sp, получаем:
[ px ] [ a b c ] [ 0 ] [ c*dist ] [ py ] = [ d e f ] [ 0 ] = [ f*dist ], откуда [ pz ] [ g h i ] [ dist ] [ i*dist ]
c = px/dist, f = py/dist, i = pz/dist.
Аналогично находим все остальные элементы матрицы T:
[ qx*2/xSize rx*2/ySize px/dist ] T = [ qy*2/xSize ry*2/ySize py/dist ] [ qz*2/xSize rz*2/ySize pz/dist ]
Но нас интересует обратное к этому преобразование. Оно задается обратной матрицей к T, то есть такой матрицей T1, что
[ 1 0 0 ] T * T1 = T1 * T = [ 0 1 0 ] [ 0 0 1 ]
Обратная матрица, вообще говоря, существует далеко не всегда, да и вычисление ее в общем случае - достаточно неприятная задача. Однако в данном случае из-за специального вида матрицы T (конкретнее, из-за того, что T - ортогональная матрица) она не только всегда существует, но и считается очень просто:
[ qx*2/xSize rx*2/ySize px/dist ] [ qx1 rx1 px1 ] T = [ qy*2/xSize ry*2/ySize py/dist ] = [ qy1 ry1 py1 ] [ qz*2/xSize rz*2/ySize pz/dist ] [ qz1 rz1 pz1 ]
[ qx1/lq qy1/lq qz1/lq ] T1 = [ rx1/lr ry1/lr rz1/lr ] [ px1/lp py1/lp pz1/lp ]
где
lp = px1*px1 + py1*py1 + pz1*pz1 lq = qx1*qx1 + qy1*qy1 + qz1*qz1 lr = rx1*rx1 + ry1*ry1 + rz1*rz1
Сделав сначала параллельный перенос, совмещающий s и Ss, а потом полученное преобразование, как раз и получим преобразование, переводящее произвольную камеру в стандартную.
Теперь надо выяснить, как, собственно посчитать координаты p, q, r через имеющиеся у нас характеристики: положение, направление, угол зрения и угол поворота. 3D Studio (и мы вслед за ней) рассчитывает эти вектора по такому алгоритму:
Считаем p = target - location
Если p.x == 0 и p.z == 0, то q = (0, 0, 1); иначе q = (p.z, 0, -p.x)
Считаем r = crossProduct(p, q) - векторное произведение p на q
Считаем lp = length(p) - длина p
Приводим r и q к длине 2*lp*tan(FOV/2)
Здесь мы не учитываем поворот камеры вокруг своей оси, его удобнее сделать после перехода к стандартной камере - в этом случае получаем обычный поворот относительно оси z на угол roll.
Таким образом, окончательная матрица перевода должна представлять собой произведение матрицы параллельного переноса, матрицы T1 и матрицы поворота вокруг оси z на угол roll:
FinalCameraMatrix = RollMatrix * T1 * MoveMatrix
Расчет матриц RollMatrix и MoveMatrix очевиден.
1 2 3 4
8 8 8
| |