Опубликован: 13.07.2010 | Уровень: специалист | Доступ: платный
Самостоятельная работа 20:

Геометрические примитивы в OpenGL

Упражнение 2. Задание размера точки (PointsZ)

Когда в предыдущем варианте мы рисовали точку, то по умолчанию ее размер равен одному пикселу. Изменить величину точки можно функцией glPointSize(GLfloat size).

Функция glPointSize() принимает один параметр, задающий приблизительный диаметр в пикселах рисуемой точки. Поддерживаются не все желаемые размеры, поэтому следует проверять, доступен ли размер, который мы задаем для точки. Чтобы найти диапазон размеров и наименьший шаг изменения размера, применяется следующий код

GLfloat sizes[2], step;
glGetFloatv(GL_POINT_SIZE_RANGE, sizes);  // Получаем диапазон
glGetFloatv(GL_POINT_SIZE_GRANULARITY, &step);  // Получаем шаг

Массив размеров содержит наименьший и наибольший возможные значения точки, поддерживаемый функцией glPointSize(). При приближении размера к краю диапазона он замораживается.

Точки, в отличие от других геометрических объектов, не меняются при делении на коэффициент перспективы (aspect ratio). То есть они не становятся меньше при удалении от точки наблюдения, и не становятся больше при приближении к наблюдателю. Точки всегда являются квадратными. Чтобы сделать их круглыми, нужно использовать технику защиты от наложения ( antialiasing ). Она позволяет сглаживать зазубренные края и округлять углы.

Реализуем увеличение размера точек на практике.

  • Добавьте в конец файла Primitives.cpp следующий код новой функции PointsZ()
#include "points.h" // Включение перенесенного кода
  
//**********************************************************
// Функция рисования пружины точками с увеличением размера
// Все углы в радианах
void PointsZ() // Здесь изменили название!!!
{
  // Запомнить первоначальное состояние матрицы вращения
  glPushMatrix();
  
  // Настроить два последовательных поворота
  // для будущей визуализации сцены
  glRotatef(xRot, 1.0f, 0.0f, 0.0f);// Первое состояние матрицы вращения
  glRotatef(yRot, 0.0f, 1.0f, 0.0f);// Следующее состояние
  
  GLfloat sizes[2], step; // Для определения диапазона и 
              // шага изменения размеров точки
  glGetFloatv(GL_POINT_SIZE_RANGE, sizes);// Получаем диапазон
  GLfloat curSize = sizes[0]; // Сначала установим минимальный размер
  glGetFloatv(GL_POINT_SIZE_GRANULARITY, &step);  // Получаем шаг
  
  // Задаем три оборота в радианах
  GLfloat x,y,z,angle; // Переменные для координат и угла  
  z = -50.0f;// Задаем начальную координату z
  
  for(angle = 0.0f; angle <= 2.0f * GL_PI * 3.0f; angle += 0.1f){
    // Вычисляем очередную точку на окружности
    x = 50.0f * sin(angle);
    y = 50.0f * cos(angle);
  
    // Задаем размер точки перед указанием примитива
    glPointSize(curSize);
  
    // Посылаем на визуализацию каждую точку по отдельности
    glBegin(GL_POINTS);
      // Задаем точку
      glVertex3f(x, y, z);
    glEnd(); // Закончили визуализацию
  
    z += 0.5f; // Приближаем к зрителю
    curSize += step; // Увеличиваем размер точки
  }
  
  // Восстанавливаем матрицу вращения в исходное состояние
  glPopMatrix();
    
  // Восстанавливаем минимальный размер
  glPointSize(sizes[0]);
}
Листинг 21.8. Функция PointsZ() рисования пружины точками с увеличением размера

Обратите внимание, что функцию glPointSize нужно вызывать за пределами операторных скобок glBegin...glEnd. Не все функцию допускают такую "обертку".

  • Добавьте в начало файла Primitives.cpp прототип функции PointsZ()
void PointsZ(void);
  • Добавьте в функции main() название новой опции меню
// Точка входа приложения
void main(void)
{
  glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
  glutCreateWindow("Primitives");  // Заголовок окна
  glutDisplayFunc(RenderScene);  // Обновление сцены при разрушении окна
  glutReshapeFunc(ChangeSize);  // При изменении размера окна
  SetupRC();
  
  // Создание меню и добавление опций выбора
  glutCreateMenu(ExecuteMenu);
  glutAddMenuEntry("Рисование функцией Points", 1);
  glutAddMenuEntry("Точки с увеличением размера", 2);
  glutAddMenuEntry("Опция 3", 3);
  glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем 
  // Конец создания меню
  
  glutSpecialFunc(SpecialKeys); // Для управления с клавиатуры
  
  glutMainLoop();  // Цикл сообщений графического окна
}
Листинг 21.10. Корректировка меню
  • Добавьте в функцию RenderScene() вызов функции PointsZ() при соответствующем выборе пользователя
//****************************************************
// Функция обратного вызова для рисования сцены
void RenderScene(void)
{
  // Окно очищается текущим цветом,
  // установленным функцией glClearColor()
  glClear(GL_COLOR_BUFFER_BIT);
  
  // Отработка выбора пользователя
  // Пока один вариант !
  switch(::choice){
    case 1:
      Points();
      break;
    case 2:
      PointsZ();
      break;
  }
  
  // В буфер вводятся команды рисования
  glFlush();
}
Листинг 21.11. Вызов нового варианта рисования
  • Проверьте отсутствие синтаксических ошибок в текущем файле редактирования Primitivez.cpp, выполнив команду меню оболочки Build/Compile
  • Добавьте к проекту новый заголовочный файл PointsZ.h и переместите! в него код функции PointsZ() из файла Primitivez.cpp, а на освободившееся место включите этот заголовочный файл инструкцией
#include "PointsZ.h"
  • Проследите за тем, чтобы эта инструкция следовала в коде после инструкции
#include "Points.h"
  • Постройте проект и получите примерно такой результат


Хорошо заметно, что все отображаемые точки имеют квадратную форму. Но можно указать OpenGL сглаживать углы квадратов, за счет чего линия будет смотреться лучше. Но это потом...

Заметьте также, что благодаря меню, мы можем теперь запускать любое упражнение, не перестраивая проект приложения.

Упражнение 3. Рисование линий в трехмерном пространстве (Lines)

Следующий этап наших исследований: задать две вершины в трехмерном пространстве и соединить их прямой линией. Именно для этого предназначен код

glBegin(GL_LINES);...glEnd();

Приведенный ниже короткий фрагмент кода рисует отрезок с вершинами ( 0, 0, 0 ) и ( 50, 50, 50 )

glBegin(GL_LINES);
  glVertex3f(0.0f, 0.0f, 0.0f);
  glVertex3f(50.0f, 50.0f, 50.0f);
glEnd();

Обратите внимание на то, что две вершины задают один примитив - для двух заданных вершин рисуется одна линия. Если задать в GL_LINES нечетное количество вершин - последняя из них будет проигнорирована: примитив "линия" рисуется парными вершинами.

В качестве примера нарисуем ряд линий, концы которых размещаются в одной плоскости по окружности симметрично относительно центра этой окружности. Результатом будет иллюзия расходящегося веера в трехмерном пространстве.

  • В панели Solution Explorer двойным щелчком левой кнопкой мыши на файле PointsZ.h откройте его на редактирование.
  • Выполните команду File/Save PointsZ.h As меню оболочки и запишите в текущий каталог проекта копию файла с именем Lines.h

  • В панели Solution Explorer вызовите контекстное меню для узла Primitives проекта и, выполнив команду Add/Existing Item, присоедините этот файл к текущему проекту
  • Очистите содержимое вновь полученного файла и замените его следующим кодом
//**********************************************************
// Функция рисования веера линий
void Lines()
{
  // Запомнить первоначальное состояние матрицы вращения
  glPushMatrix();
  
  // Настроить два последовательных поворота
  // для будущей визуализации сцены
  glRotatef(xRot, 1.0f, 0.0f, 0.0f);// Первое состояние матрицы вращения
  glRotatef(yRot, 0.0f, 1.0f, 0.0f);// Следующее состояние
  
  GLfloat x,y,z,angle; // Переменные для координат и угла  
  z = 0.0f;// Размещаем линии в плоскости x0y
  
  // Размещаем веер линий
  glBegin(GL_LINES);
    // Генерируем 20 пар точек на окружности симметрично ее центра
    for(angle = 0.0f; angle <= GL_PI; angle += (GL_PI /20.0f)){
      // Вычисляем очередную линию на верхней половине окружности
      x = 50.0f * sin(angle);
      y = 50.0f * cos(angle);
      glVertex3f(x, y, z); // Первая конечная точка отрезка
  
      // Вычисляем очередную линию на нижней половине окружности
      x = 50.0f * sin(angle + GL_PI);
      y = 50.0f * cos(angle + GL_PI);
      glVertex3f(x, y, z); // Вторая конечная точка отрезка
    }
  glEnd(); // Закончили визуализацию
  
  // Восстанавливаем матрицу вращения в исходное состояние
  glPopMatrix();
}
Листинг 21.16. Код рисования веерных линий
  • Разместите в конце файла Primitives.cpp включение файла Lines.h
.................................................  
#include "Points.h"    // Включение перенесенного кода
#include "PointsZ.h"  // Включение кода рисования точек с увеличением размера
#include "Lines.h"    // Веер линий
Листинг 21.17. Конец файла Primitives.cpp
  • Добавьте в начало файла Primitives.cpp прототип функции Lines()
.................................................  
// Прототипы функций
void RenderScene(void);
void SetupRC(void);
void ExecuteMenu(int);
void ChangeSize(int, int);
void Points(); // Пустой аргумент необязателен
void SpecialKeys(int key, int x, int y);// Можно указать только типы
void PointsZ();
void Lines();  
.................................................
Листинг 21.18. Добавление прототипа функции Lines() в начало файла Primitives.cpp
  • Скорректируйте меню в функции main()
.................................................  
  // Создание меню и добавление опций выбора
  glutCreateMenu(ExecuteMenu);
  glutAddMenuEntry("Рисование функцией Points", 1);
  glutAddMenuEntry("Точки с увеличением размера", 2);
  glutAddMenuEntry("Веер линий", 3);
  glutAttachMenu(GLUT_RIGHT_BUTTON);// Присоединяем 
  // Конец создания меню
.................................................
Листинг 21.19. Корректировка меню в файле Primitives.cpp
  • Скорректируйте обработку выбора пользователя в функции рендеринга
//****************************************************
// Функция обратного вызова для рисования сцены
void RenderScene(void)
{
  // Окно очищается текущим цветом,
  // установленным функцией glClearColor()
  glClear(GL_COLOR_BUFFER_BIT);
  
  // Отработка выбора пользователя
  switch(::choice){
    case 1:
      Points();
      break;
    case 2:
      PointsZ();
      break;
    case 3:
      Lines();
      break;
  }
  
  // В буфер вводятся команды рисования
  glFlush();
}
Листинг 21.20. Корректировка функции RenderScene() в файле Primitives.cpp
  • Запустите приложение и получите следующий результат при управлении стрелками


Иван Циферблат
Иван Циферблат
Россия, Таганрог, 36, 2000