Подскажите, пожалуйста, планируете ли вы возобновление программ высшего образования? Если да, есть ли какие-то примерные сроки? Спасибо! |
Интерактивные приложения OpenGL
Получение и анализ содержания буфера выбора
Содержание буфера выбора может быть проанализировано после вызова команды смены режима RenderMode при изменении режима на отличный от режима выбора, например, на режим формирования изображения:
C#: GL.RenderMode(RenderingMode.Render); Object Pascal: glRenderMode(GL_RENDER);
Команда RenderMode в качестве результата возвращает количество записей в буфере выбора. Каждая запись буфера выбора имеет следующий формат:
- Первое значение - количество имен объектов в записи. Обозначим это значение как N. Это значение соответствует количеству элементов в стеке имен при попадании примитивов объекта в область выбора, так как содержание стека имен копируется в буфер выбора.
- Второе и третье значения – наименьшее и наибольшее значение буфера глубины для примитивов, попавших в сцену выбора, данного именованного объекта. Значения являются беззнаковыми четырехбайтными целыми числами.
- Начиная с четвертого значения, следуют имена объектов, находящиеся в стеке имен, который копируется в буфер выбора при формировании данной записи. Количество имен определяется первым элементом записи (значение N).
После первой записи в буфере последовательно располагаются остальные записи. Количество записей определяется значением, возвращаемым командой RenderMode при смене режима, и равно количеству именованных объектов, примитивы которых попали в сцену выбора.
Рассмотрим пример использования буфера выбора. Нарисуем в пространстве 4 треугольника и попробуем с помощью буфера выбора определить треугольники, которые находятся под указателем "мыши" в момент нажатия левой кнопки "мыши". Для задания области выбора будем использовать область проекции размером 2*2 пиксела около курсора в момент нажатия кнопки "мыши".
В листинге 10.8 приведено содержание обработчиков событий Paint и MouseDown компонента GLControl на C#.
Rectangle p; private double[] m = new double[16]; uint[] buf = new uint[128]; // буфер выбора private void glControl1_Paint(object sender, PaintEventArgs e) { // определение текущего режима OpenGL int[] aMode = new int[1]; GL.GetInteger(GetPName.RenderMode, aMode); RenderingMode mode = (RenderingMode)aMode[0]; if (mode == RenderingMode.Select) { GL.MatrixMode(MatrixMode.Projection); // сохранение матрицы проекций для формирования изображения // в стеке матриц и в переменной M GL.PushMatrix(); GL.GetDouble( GetPName.ProjectionMatrix, m); // формирование матрицы проекций для режима выбора GL.LoadIdentity(); int[] vp = new int[4]; GL.GetInteger(GetPName.Viewport, vp); Tao.OpenGl.Glu.gluPickMatrix(p.X, vp[3] - p.Y, 5, 5, vp); GL.MultMatrix(m); GL.MatrixMode(MatrixMode.Modelview); // инициализация стека имен GL.InitNames(); GL.PushName(0); } // очистка буферов цвета и глубины GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); // поворот изображения GL.LoadIdentity(); GL.Rotate(AngleX, 1.0, 0.0, 0.0); GL.Rotate(AngleY, 0.0, 1.0, 0.0); GL.Rotate(AngleZ, 0.0, 0.0, 1.0); // формирование изображения if (mode == RenderingMode.Select) GL.LoadName(1); GL.Color3(0f, 0f, 1f); GL.Begin(BeginMode.Triangles); GL.Vertex3(0,0,0); GL.Vertex3(-1,-1,0); GL.Vertex3(-1,1,0); GL.End(); if (mode == RenderingMode.Select) GL.LoadName(2); GL.Color3(1f, 0f, 0); GL.Begin(BeginMode.Triangles); GL.Vertex3(0,0,0); GL.Vertex3(1,1,0); GL.Vertex3(1,-1,0); GL.End(); if (mode == RenderingMode.Select) GL.LoadName(3); GL.Color3(0f, 1f, 1f); GL.Begin(BeginMode.Triangles); GL.Vertex3(0,0,1); GL.Vertex3(-1,-1,1); GL.Vertex3(-1,1,1); GL.End(); if (mode == RenderingMode.Select) GL.LoadName(4); GL.Color3(1f, 1f, 0f); GL.Begin(BeginMode.Triangles); GL.Vertex3(0,0,1); GL.Vertex3(1,1,1); GL.Vertex3(1,-1,1); GL.End(); // завершение формирования изображения GL.Flush(); GL.Finish(); if (mode == RenderingMode.Select) { int rec_count = GL.RenderMode(RenderingMode.Render); // восстановление матрицы проекции из стека GL.MatrixMode(MatrixMode.Projection); GL.PopMatrix(); GL.MatrixMode(MatrixMode.Modelview); // вывод содержания буфера выбора uint obj_count = 0; uint el = 0; string s = ""; for (int i = 1; i <= rec_count; ++i) { obj_count = buf[el]; s += String.Format("{0}, {1}, {2}", obj_count, buf[el + 1], buf[el + 2]); for (int obj=1; obj <= obj_count; ++obj) s += String.Format(", {0}", buf[el + 2 + obj]); s += "\n"; el += obj_count + 3; } if (rec_count > 0) MessageBox.Show(s); } else glControl1.SwapBuffers(); } private void glControl1_MouseDown(object sender, MouseEventArgs e) { p.X = e.X; p.Y = e.Y; GL.RenderMode(RenderingMode.Select); glControl1.Invalidate(); }Листинг 10.8. Пример определения выбранного объекта сцены с помощью буфера выбора на C#
В обработчике события MouseDown компонента GLControl, который выполняется при нажатии кнопки "мыши", осуществляется сохранение координат указателя в глобальной переменной P типа Rectangle.
В дальнейшем координаты указателя, сохраненные в переменной P, используются в обработчике события Paint. В начале обработчика события Paint определяется режим библиотеки с помощью команды GetInteger. Если активным является режим выбора, то выполняется несколько операций:
- Сохраняется текущая матрица проекции в стеке матриц для её восстановления при переходе в режим формирования изображения.
- Текущая матрица проекций так же сохраняется в переменной M, для того чтобы на её основе сформировать матрицу проекции для режима выбора.
- Формируется матрица проекции для режима выбора и инициализируется стек имен: он очищается c помощью команды InitNames и в него помещается одно значение с помощью команды PushName. При формировании изображения оно будет заменяться с помощью команды LoadName на имя формируемого объекта сцены.
Ниже приведены обработчики событий формы OnPaint и OnMouseDown программы на Object Pascal, которые выполняют действия аналогичные программе на C# ( пример 10.9).
// глобальные переменные var SB : Array [0..128] of GLuint;// буфер выбора P: TPoint; M : Array [0..3, 0..3] of GLdouble; procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); Begin // сохранение координат указателя в переменной P // и переключение в режим выбора p.x:=x; p.y:=y; glRenderMode(GL_SELECT); InvalidateRect(Handle, nil, False); end; procedure TForm1.FormPaint(Sender: TObject); var i : GLint; Count, Mode: GLuint; vp : Array [0..3] of GLint; rec_count: integer; obj, obj_count, el: integer; s: string; begin // определение текущего режима OpenGL glGetIntegerv(GL_RENDER_MODE, @Mode); if Mode=GL_SELECT then begin glMatrixMode(GL_PROJECTION); // сохранение матрицы проекций для формирования изображения // в стеке матриц и в переменной M glPushMatrix; glGetDoublev(GL_PROJECTION_MATRIX, @M); // формирование матрицы проекций для режима выбора glLoadIdentity; glGetIntegerv(GL_VIEWPORT, @vp); gluPickMatrix(p.x, vp[3]-p.y, 5, 5, @vp); glMultMatrixd(@M); glMatrixMode(GL_MODELVIEW); // инициализация стека имен glInitNames; glPushName(0); end; // очистка буфера цвета glClear (GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); glLoadIdentity; // поворот сцены вокруг осей координат, // выполняемый с помощью клавиатуры glRotatef(AngleX, 1.0, 0.0, 0.0); glRotatef(AngleY, 0.0, 1.0, 0.0); glRotatef(AngleZ, 0.0, 0.0, 1.0); // формирование треугольников if Mode=GL_SELECT then glLoadName(1); glColor3f(0,0,1); glBegin(GL_TRIANGLES); glVertex3f(0,0,0); glVertex3f(-1,-1,0); glVertex3f(-1,1,0); glEnd; if Mode=GL_SELECT then glLoadName(2); glColor3f(1,0,0); glBegin(GL_TRIANGLES); glVertex3f(0,0,0); glVertex3f(1,1,0); glVertex3f(1,-1,0); glEnd; if Mode=GL_SELECT then glLoadName(3); glColor3f(0,1,1); glBegin(GL_TRIANGLES); glVertex3f(0,0,1); glVertex3f(-1,-1,1); glVertex3f(-1,1,1); glEnd; if Mode=GL_SELECT then glLoadName(4); glColor3f(1,1,0); glBegin(GL_TRIANGLES); glVertex3f(0,0,1); glVertex3f(1,1,1); glVertex3f(1,-1,1); glEnd; if Mode=GL_SELECT then begin rec_count:=glRenderMode(GL_RENDER); // восстановление матрицы проекции из стека glMatrixMode(GL_PROJECTION); glPopMatrix; glMatrixMode(GL_MODELVIEW); // вывод содержания буфера выбора obj_count := 0; el := 0; with Form2.Memo1.Lines do begin Clear; for i:=1 to rec_count do begin obj_count := sb[el]; s := IntToStr(obj_count) + ',' + IntToStr(sb[el + 1]) + ',' + IntToStr(sb[el + 2]); for obj := 1 to obj_count do s := s + ',' + IntToStr(sb[el + 2 + obj]); el := el + (obj_count + 3); Add(s); end; end; end else SwapBuffers(DC); end;Листинг 10.9. Пример определения выбранного объекта сцены с помощью буфера выбора на Object Pascal
Необходимо так же обратить внимание на содержание буфера выбора. Библиотека помещает информацию в буфер выбора в порядке формирования примитивов. Т. е. она не сортирует их по удалённости от наблюдателя. Поэтому для корректного определения ближайшего к наблюдателю объекта необходимо самостоятельно провести анализ значений буфера глубины (второй и третий элемент записи) и на их основе определить ближайший объект от наблюдателя.
Краткие итоги
Для реализации интерактивных приложений OpenGL можно использовать анализ цвета пикселов выбранной области в отображаемом буфере и во вторичном буфере или режим выбора объектов. Режим выбора объектов по сравнению с анализом цвета пикселов позволяет получить информацию о невидимых объектах в выбранной области. Для анализа цвета пикселов используется команда ReadPixels.
При использовании режима выбора используется команда RenderMode для переключения между режимами формирования изображения и режимом выбора, буфер имен, а так же процедура gluPickMatrix библиотеки GLUT для определения выбранной области в объемной сцене.