Опубликован: 02.12.2011 | Доступ: свободный | Студентов: 980 / 117 | Оценка: 5.00 / 4.00 | Длительность: 09:26:00
Специальности: Программист
Теги: .net, open source, opengl
Лекция 11:

Интерактивные приложения OpenGL

< Лекция 10 || Лекция 11: 123 || Лекция 12 >

Получение и анализ содержания буфера выбора

Содержание буфера выбора может быть проанализировано после вызова команды смены режима 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. Если активным является режим выбора, то выполняется несколько операций:

  1. Сохраняется текущая матрица проекции в стеке матриц для её восстановления при переходе в режим формирования изображения.
  2. Текущая матрица проекций так же сохраняется в переменной M, для того чтобы на её основе сформировать матрицу проекции для режима выбора.
  3. Формируется матрица проекции для режима выбора и инициализируется стек имен: он очищается 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 для определения выбранной области в объемной сцене.

< Лекция 10 || Лекция 11: 123 || Лекция 12 >
Владислав Нагорный
Владислав Нагорный

Подскажите, пожалуйста, планируете ли вы возобновление программ высшего образования? Если да, есть ли какие-то примерные сроки?

Спасибо!

Лариса Парфенова
Лариса Парфенова

1) Можно ли экстерном получить второе высшее образование "Программная инженерия" ?

2) Трудоустраиваете ли Вы выпускников?

3) Можно ли с Вашим дипломом поступить в аспирантуру?