К вопросу о перпендикулярности полигона к вектору
© Паша Малинников 18 Октября 2000 г.
Письмо, полученное
от моего коллеги по изысканиям в OpenGL, вновь заставило обратиться меня к такой
часто затрагиваемой теме, как техника BillBoard. Речь идет о методе вычислений
координат полигона, чаще всего четырехугольника, обеспечивающих его ортогональную
(перпендикулярную) ориентацию относительно зрителя.
Как правило, эта техника используется для создания эффектов сияния, исходящего
от источников света. Текстура с изображением свечения накладывается на полигон,
который, постоянно поворачиваясь к зрителю, создает впечатление объемности сияния.
Ряд последовательно накладываемых текстур создает впечатление анимированности
изображения, что с успехом применяется для имитации взрывов, огня, иногда дыма,
водной поверхности и т.д.
Другими словами, исключительная юзабельность приведенной техники очевидна и
не вызывает сомнений. Остается только уяснить для себя последовательность действий
для получения заветных координат 4-х точек полигона в зависимости от положения
наблюдателя, или, если хотите, вектора из точки расположения камеры в точку
расположения полигона.
Пусть камера находится в точке C (cX, cY, cZ), а центр полигона - в точке P
(pX, pY, pZ). Компоненты вектора, соединяющего точку C с точкой P будут выглядеть
так:
cp^ (cpX, cpY, cpZ) = cp^ (cX - pX, cY - pY, cZ - pZ)
Следующим шагом должно стать нахождение перпендикуляра к этому вектору. Мы
можем сделать это, найдя перпендикуляр плоскости, проходящей через вектор cp^.
Такая плоскость может быть задана любым дополнительным вектором, можно, к примеру,
использовать нормализованный вектор cz^ (0, 0, 1), совпадающий с направлением
оси Z.
Вектор cross1, перпендикулярный плоскости, заданной векторами cp^ и cz может
быть найден, как векторное произведение этих векторов:
cross1^ = cp^ Х cz^
При векторном произведении компоненты вектора вычисляются следующим образом:
cross1.x = cp.y * cz.z - cp.z * cz.y;
cross1.y = cp.z * cz.x - cp.x * cz.z;
cross1.z = cp.x * cz.y - cp.y * cz.x;
Таким образом, мы получили численные характеристики вектора, являющегося горизонталью
нашего полигона (т.к. в качестве дополнительного мы выбрали вектор, параллельный
оси Z). Но для однозначного определения ориентации плоскости, проходящей через
полигон, нужно найти вектор cross2^, перпендикулярный cp^ и cross1^. Найдем
его тем же способом векторного произведения:
cross2^ = cp^ Х cross1^
Располагая векторами cross1^ и cross2^, мы можем найти координаты вершин четырехугольника
с центром в точке P:
P1.x = P.x + cross1.x;
P1.y = P.y + cross1.y;
P1.z = P.z + cross1.z;
P2.x = P.x - cross2.x;
P2.y = P.y - cross2.y;
P2.z = P.z - cross2.z;
P3.x = P.x - cross1.x;
P3.y = P.y - cross1.y;
P3.z = P.z - cross1.z;
P4.x = P.x + cross2.x;
P4.y = P.y + cross2.y;
P4.z = P.z + cross2.z;
*Приложение.
Один из возможных вариантов применения в коде.
//Типа вектор :-)
typedef struct
{ double x;
double y;
double z;
} Vector;
//Функции векторных операций
//Длина-------------------------------------------------------
double VectorLength( Vector v )
{ return sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
}
//Нормализация (приведение компонент к долям единицы)---------
Vector VectorNormalize( Vector v )
{ Vector vresult;
double l = VectorLength( v );
vresult.x = v.x/l;
vresult.y = v.y/l;
vresult.z = v.z/l;
return vresult;
}
//То самое векторное произведение-----------------------------
Vector VectorMultiply( Vector v1, Vector v2 )
{ Vector vresult;
vresult.x = v1.y * v2.z - v1.z * v2.y;
vresult.y = v1.z * v2.x - v1.x * v2.z;
vresult.z = v1.x * v2.y - v1.y * v2.x;
return vresult;
}
//Определение составляющих вектора, заданного двумя
точками---
Vector VectorDiff( Vector v1, Vector v2 )
{ Vector vresult;
vresult.x = v1.x - v2.x;
vresult.y = v1.y - v2.y;
vresult.z = v1.z - v2.z;
return vresult;
}
//Поехали-----------------------------------------------------
//На самом деле это не вектор, а точка, в которой находится
камера
//Ну и отфонарные значения для примера
Vector C;
C.x = 14;
C.y = 13;
C.z = 5;
//Центр полигона
Vector P;
P.x = 15;
P.y = 15;
P.z = 3;
//Находим вектор зрительной оси
Vector cp = VectorDiff(C, P);
//Единичный вектор, параллельный оси Z
Vector cz;
cz.x = 0;
cz.y = 0;
cz.z = 1;
//Нормализуем вектор зрительной оси и находим его перпендикуляры
cp = VectorNormalize(cp);
Vector cross1 = VectorMultiply(cp, cz);
Vector cross2 = VectorMultiply(cp, cross1);
//Рассчитываем координаты точек четырехугольника
glBegin(GL_QUADS);
glVertex3d(P.x + cross1.x, P.y + cross1.y, P.z + cross1.z);
glVertex3d(P.x - cross2.x, P.y - cross2.y, P.z - cross2.z);
glVertex3d(P.x - cross1.x, P.y - cross1.y, P.z - cross1.z);
glVertex3d(P.x + cross2.x, P.y + cross2.y, P.z + cross2.z);
glEnd();
Демка: billboard.zip(84 K)
Исходник: billboard_src.zip(90
K)
*Примечание.
Ну конечно же в функциях векторных операций можно и нужно использовать указатели.
*Реальный пример класса, позволяющий использовать все операции над векторами,
приведен в Приложении № 1
* В моих статьях описываются алгоритмы, не все из которых являются
классическими или полностью совершенными. Поэтому приветствуются любые замечания,
комментарии и критика.
Posted: 2.02.2k1
Autor: Паша Малинников
<mpa@cci.lg.ua>
|