Статья взята с GameDev.ru
Читая книгу [1] я наткнулся на очень простой и изящный способ решения этой задачи в OpenGL приложении с помощью замечательной команды gluUnProject(...), о существовании которой раньше я ничего не знал. Для тех, кто уже знаком с этой командой, весь текст, приводимый далее, будет неинтересен. Для тех же, кто с ней не знаком или не читал книгу [1], приведу куски кода, демонстрирующие использование этой команды.
Итак, функция gluUnProject, прототип которой находится в файле glu.h имеет вид:
C++ Копировать
int APIENTRY gluUnProject ( GLdouble winx, GLdouble winy, GLdouble winz, const GLdouble modelMatrix[16], const GLdouble projMatrix[16], const GLint viewport[4], GLdouble *objx, GLdouble *objy, GLdouble *objz);
, где
winx, winy - x,y координаты курсора мыши в системе координат viewport-a.
winz - z координата курсора мыши. Задаётся в пределах -1..+1
(-1 для ближней плоскости отсечения и +1 для дальней).
modelMatrix - текущая матрица видового преобразования.
projMatrix - текущая матрица проективного преобразования.
viewport - текущие параметры viewport-a.
objx,objy,objz - возвращаемые мировые координаты.
Далее приведу текст функции, которая по оконным координатам курсора вычисляет мировые координаты концов селектирующего отрезка СО, который можно использовать для выделения объектов сцены.
C++ Копировать
class CVector3 { public: float x,y,z; }; void calc_select_line(int mouse_x, int mouse_y, CVector3& p1, CVector3& p2) { // mouse_x, mouse_y - оконные координаты курсора мыши. // p1, p2 - возвращаемые параметры - концы селектирующего отрезка, // лежащие соответственно на ближней и дальней плоскостях // отсечения. GLint viewport[4]; // параметры viewport-a. GLdouble projection[16]; // матрица проекции. GLdouble modelview[16]; // видовая матрица. GLdouble vx,vy,vz; // координаты курсора мыши в системе координат viewport-a. GLdouble wx,wy,wz; // возвращаемые мировые координаты. glGetIntegerv(GL_VIEWPORT,viewport); // узнаём параметры viewport-a. glGetDoublev(GL_PROJECTION_MATRIX,projection); // узнаём матрицу проекции. glGetDoublev(GL_MODELVIEW_MATRIX,modelview); // узнаём видовую матрицу. // переводим оконные координаты курсора в систему координат viewport-a. vx = mouse_x; vy = viewport[3] - mouse_y - 1; // вычисляем ближний конец селектирующего отрезка. vz = -1; gluUnProject(vx, vy, vz, modelview, projection, viewport, &wx, &wy, &wz); p1 = CVector3(wx,wy,wz); // вычисляем дальний конец селектирующего отрезка. vz = 1; gluUnProject(vx, vy, vz, modelview, projection, viewport, &wx, &wy, &wz); p2 = CVector3(wx,wy,wz); }
Вот и всё!
Если Вам не требуется тестировать объекты сцены на пересечение с СО, а лишь нужно узнать мировые координаты видимой точки поверхности объекта, лежащей под курсором мыши, то в качестве параметра vz следует подставить соответствующее данному пикселю значение буфера глубины. Узнать его можно при помощи команды:
glReadPixels(vx, vy, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &vz);
Если объект сцены состоит из треугольников, то для проверки пересечения треугольника с СО можно воспользоватьс функцией:
C++ Копировать
bool intersect_triangle_line(CVector3 v1, // вершины треугольника. CVector3 v2, CVector3 v3, CVector3 n, // нормаль треугольника. CVector3 p1, // первый конец отрезка. CVector3 p2, // второй конец отрезка. CVector3& pc) // возвращаемая точка пересечения. { // вычисляем расстояния между концами отрезка и плоскостью треугольника. float r1 = n & (p1 - v1); float r2 = n & (p2 - v1); // если оба конца отрезка лежат по одну сторону от плоскости, то отрезок // не пересекает треугольник. if( f1_sgn(r1) == f1_sgn(r2) ) return FALSE; // вычисляем точку пересечения отрезка с плоскостью треугольника. CVector3 ip = (p1 + ((p2 - p1) * (-r1 / (r2 - r1)))); // проверяем, находится ли точка пересечения внутри треугольника. if( (((v2 - v1) ^ (ip - v1)) & n) <= 0) return FALSE; if( (((v3 - v2) ^ (ip - v2)) & n) <= 0) return FALSE; if( (((v1 - v3) ^ (ip - v3)) & n) <= 0) return FALSE; pc = ip; return TRUE; }
Здесь:
Операции с векторами " & " и " ^ " обозначают соответственно Dot Product и Cross Product.
C++ Копировать
int f1_sgn(const float& k) // - функция, вычисляющая знак числа. { if( k > 0 ) return 1; if( k < 0 ) return -1; return 0; }
Литература:
[1] - "OpenGL графика в проектах DELPHI" М.Краснов. (стр. 314)