ОСНОВЫ 3D ГРАФИКИ
2.5. Работа с произвольной камерой
Рассмотрим любую камеру как точку - центр проецирования и экран - плоский
прямоугольник в 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 (и мы вслед за ней) рассчитывает эти вектора по такому
алгоритму:
1. Считаем p = target - location
2. Если p.x == 0 и p.z == 0, то q = (0, 0, 1); иначе q = (p.z, 0, -p.x)
3. Считаем r = crossProduct(p, q) - векторное произведение p на q
4. Считаем lp = length(p) - длина p
5. Приводим r и q к длине 2*lp*tan(FOV/2)
Здесь мы не учитываем поворот камеры вокруг своей оси, его удобнее сделать
после перехода к стандартной камере - в этом случае получаем обычный поворот
относительно оси z на угол roll.
Таким образом, окончательная матрица перевода должна представлять собой
произведение матрицы параллельного переноса, матрицы T1 и матрицы поворота
вокруг оси z на угол roll:
FinalCameraMatrix = RollMatrix * T1 * MoveMatrix
Расчет матриц RollMatrix и MoveMatrix очевиден.