Visual Basic - мастерская разработчика
Библиотеки

DirectX

Обзоры
DirectDraw
Direct3D
DirectX Audio
DirectPlay
DirectInput
Fido Topics
SourceCode
Tools&Libs

OpenGL

Статьи и учебники
Fido Topics
SourceCode
Tools&Libs

Архив по Glide

Движки

Обзоры
Учебники
SourceCode
Downloads

Создание игр

Ваши игры

Обзорные статьи
Учебники
Fido Topics
SourceCode
Download

Stuff

Программер-Чат

Псевдо-FTP
Disclaimer
Оффтопик

 

К вопросу о перпендикулярности полигона к вектору

© Паша Малинников 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>


 


Проект
Создание Народного Учебника по OpenGL

Участвовать!
Поиск
Найдите статью или файл:


Рассылка
Новости сайта
La Vision в вашем почтовом ящике








Программирование на С++ Delphi и Паскаль
Центр демо-искусства в России