Создание графических моделей с помощью Open Graphics Library

Основные графические примитивы OpenGL и их свойства

Разбить на страницы
Показывать лекцию целиком

Цель лекции: Научиться использовать основные примитивы OpenGL для формирования изображения.

Примеры на C# (WinForms) и Object Pascal (Delphi)

Пример № 4.1 Пример демонстрирует формирование несвязанных отрезков.

Пример № 4.2 Пример демонстрирует формирование замкнутой ломаной.

Пример № 4.3 Пример демонстрирует формирование несвязанных треугольников.

Пример № 4.4 Пример демонстрирует формирование треугольников с общими гранями.

Пример № 4.5 Пример демонстрирует формирование треугольников с общей вершиной.

Пример № 4.6 Пример демонстрирует формирование несвязанных четырехугольников.

Пример № 4.7 Пример демонстрирует формирование четырехугольников с общими гранями.

Пример № 4.8 Пример демонстрирует формирование многоугольника.

Пример № 4.9 Пример демонстрирует построение графика функции y = z*Sin(x*n) с возможностью изменения коэффициентов n и z с помощью клавиатуры.

В OpenGL все изображения формируются из графических примитивов, основными из которых являются точки, отрезки, треугольники, четырехугольники, многоугольники. Базовым графическим примитивом для построения сложных объёмных изображений является треугольник. Такие примитивы как четырехугольники и многоугольники хотя и создаются с помощью отдельных команд, но формируются библиотекой OpenGL из набора треугольников. Необходимая детализация сложных изображений достигается за счет уменьшения размера примитивов, из которых строится изображение, и увеличения их количества.

Формирование примитива начинается с вызова команды Begin и передачи ей в качестве значения параметра mode целочисленной константы, которая определяет формируемый примитив:

Begin (mode: GLenum);

В Object Pascal для реализации команды Begin используется процедура

procedure glBegin (mode: GLenum);

Например, для формирования точек используется константа GL_POINTS, для формирования отрезков –константа GL_LINES.

В библиотеке OpenTK для реализации команды Begin используется статический метод класса GL (C#):

void Begin(BeginMode mode);

Формируемый примитив определяется с помощью элемента перечисления BeginMode. Для формирования точек используется значение этого перечисления Points, для формирования отрезков – значение Lines.

Формирование примитивов завершается с помощью команды End.

В Object Pascal эта команда реализуется с помощью процедуры:

procedure glEnd;

В библиотеке OpenTK для реализации команды End используется статический метод класса GL (C#):

void End();

Все примитивы состоят из набора вершин. Для задания расположения вершин в пространстве используются нормализованные координаты.

Координаты каждой вершины определяются с помощью команды OpenGL:

Vertex[2 3 4][s i f d]

Вызов этой команды должен выполняться межу вызовами команд Begin и End, иначе команда Vertex будет проигнорирована библиотекой.

Команда может иметь от 2 до 4 параметров. Если команда имеет три параметра, то ей передаются декартовые координаты вершин. В варианте команды с двумя параметрами они соответствуют декартовым координатам по оси OX и OY, координата по оси OZ принимается равной нулю.

В Object Pascal команда Vertex реализована набором процедур, объявление двух из них приведено ниже:

procedure glVertex2d (x,y: GLdouble);
procedure glVertex3d (x,y, z: GLdouble);

В библиотеке OpenTK команда реализуется с помощью статических методов класса GL с различным числом параметров и типами данных. Объявление двух из них приведено ниже.

void Vertex2(double x, double y);
void Vertex3(double x, double y, double z);

Необходимо обратить внимание на то, что для каждого вызова команды Begin должен быть соответствующий вызов команды End. Их можно сравнить с парой операторов begin и end языка Pascal, которые так же являются парными. Но в отличие от них для команд Begin и End компилятор не проверяет их парность, и эта задача ложится полностью на программиста. При несоблюдении парности вызовов команд Begin и End часто возникают довольно сложно выявляемые ошибки в формировании изображения.

Точки

Для формирования точек в качестве значения параметра mode команды Begin необходимо указать значение:

  • константу GL_POINTS в программе на Object Pascal;
  • значение Points перечисления BeginMode в программе на C#.
  • Перед формированием точек могут быть указаны их размер и определён режим сглаживания, который будет использоваться при их отображении. Эти параметры должны быть установлены перед вызовом команды Begin.

    Размер точки

    Размер точки может быть установлен с помощью команды PointSize. В Object Pascal команда реализуется с помощью процедуры:

    procedure glPointSize (size: GLfloat);
    

    В библиотеке OpenTK для этого используется статический метод класса GL (C#):

    void PointSize(float size);
    

    По умолчанию размер точки равен одному пикселу. Необходимо обратить внимание на то, что хотя координаты точки задаются в масштабе сцены, но размер точки задается в координатах устройства вывода изображения. Связано это с тем, что точки формируются на одном из последних этапов формирования изображения – на этапе растеризации.

    Режим сглаживания точки

    Управление режимом сглаживания точки осуществляется с помощью команд Enable и Disable. Команда Enable включает режим сглаживания точек, команда Disable отключает его. По умолчанию режим сглаживания точек отключен.

    В Object Pascal эти команды реализованы с помощью процедур:

    procedure glEnable(cap: GLenum);
    procedure glDisable(cap: GLenum);
    

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

    В библиотеке OpenTK для реализации этих команд используются статические методы класса GL (C#):

    void Enable(EnableCap cap);
    void Disable(EnableCap cap);
    

    Для управления режимом сглаживания необходимо указать значение PointSmooth перечисления EnableCap.

    При отключенном режиме сглаживания точка рисуется в виде квадрата. При включенном режиме сглаживания библиотека пытается изобразить точку в виде круга. На рис 4.1 приведены изображения точки разных размеров с выключенным (изображения слева) и включенным (изображения справа) режимом сглаживания.

    (рис 4.1) Изображения точки разного размера с использованием режима сглаживания и без него

    Отрезки

    Библиотека реализует три примитива для формирования отрезков.

    Несвязанные отрезки

    Для формирования несвязанных отрезков значение параметра команды Begin должно быть равно значению Lines перечисления BeginMode при использовании библиотеки OpenTK (C#) и константе GL_LINES в программе на Object Pascal.

    Каждая пара вершин определяет отрезок. Если задано нечетное количество вершин, то последняя вершина игнорируется и не будет использоваться для построения отрезков. Если будет задана всего одна вершина, то библиотека не сможет построить ни одного отрезка, так как для создания отрезка требуется как был приведен пример формирования несвязанных отрезков. В и формируется три несвязанных отрезка, хотя вершин задано ).

    GL.Begin(BeginMode.Lines);
      GL.Vertex3(-0.5, -0.5, 0);
      GL.Vertex3(-0.5, 0.5, 0);
      GL.Vertex3(0, -0.5, 0);
      GL.Vertex3(0, 0.5, 0);
      GL.Vertex3(0.5, -0.5, 0);
      GL.Vertex3(0.5, 0.5, 0);
      GL.Vertex3(0.7, -0.5, 0);
    GL.End()
    
    glBegin(GL_LINES);
      glVertex3f(-0.5, -0.5, 0);
      glVertex3f(-0.5, 0.5,  0);
      glVertex3f(0, -0.5, 0);
      glVertex3f(0, 0.5,  0);
      glVertex3f(0.5, -0.5, 0);
      glVertex3f(0.5, 0.5,  0);
      glVertex3f(0.7, -0.5, 0);
    glEnd
    
    (рис 4.2) Схема формирования несвязанных отрезков

    Ломаные линии

    Для формирования ломаных линий значение параметра команды Begin должно быть равно значению LineStrip перечисления BeginMode при использовании библиотеки OpenTK (C#) и константе GL_LINE_STRIP в программе на языке Object Pascal.

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

    Если в листинге 4.1 заменить параметр процедуры ).

    (рис 4.3) Схема формирования ломаной линии (рис 4.4) Схема формирования замкнутой ломаной

    Замкнутая ломаная

    Для формирования замкнутой ломаной необходимо задать значение параметра mode команды Begin:

  • константу GL_LINE_LOOP в программе на языке Object Pascal;
  • значение LineLoop перечисления BeginMode в программе на языке C#.
  • Примитив формируется аналогично ломаной линии с одним дополнением: отрезком так же соединяются первая и последняя вершина. Таким образом, в результате получается замкнутая ломаная.

    Если в листинге 4.1 программы на C# заменить параметр метода ). По сравнению с рис 4.1" type="example">листинге 4.1 программы на C# заменить параметр метода ). По сравнению с Чтобы получить аналогичный результат в программе на Object Pascal, фрагмент которой приведен в листинге 4.2, необходимо заменить параметр процедуры glBegin на константу GL_LINE_LOOP.

    Ширина и сглаживание линии

    Для всех типов отрезков могут быть заданы такие параметры как ширина и сглаживание линий.

    Для установки ширины линии используется команда LineWidth. В качестве параметра команде передается ширина линии в пикселах. В библиотеке OpenTK команда LineWidth реализуется с помощью статического метода класса GL (C#):

    void LineWidth(float width);
    

    В Object Pascal команда реализуется с помощью процедуры:

    procedure glLineWidth (width: GLfloat);
    

    Библиотека OpenGL имеет ограничение на максимальную ширину линии. Это значение, как и для размера точки, зависит от конкретной реализации библиотеки OpenGL. Алгоритм измерения ширины линии зависит от установленного режима сглаживания линий. По умолчанию режим сглаживания линий отключён. В этом случае ширина линии вычисляется вдоль оси OX или оси OY проекции в зависимости от угла наклона линии. Если координаты концов отрезка на проекции (x1, y1) и (x2, y2), то ширина отрезка будет вычисляться вдоль оси OY если |y1 – y2| < |x1 – x2| и в направлении оси OX если |y1 – y2| > |x1 – x2|. Поэтому при отключенном режиме сглаживания (режим по умолчанию) визуальная ширина линии может меняться в зависимости от угла наклона проекции линии.

    При включенном режиме сглаживания линий толщина отрезка измеряется перпендикулярно линии. Для включения и отключения режима сглаживания линий используются соответственно команды Enable и Disable со значением параметра GL_LINE_SMOOTH на Object Pascal и значением LineSmooth перечисления EnableCap на C#.

    Многоугольники

    Многоугольник представляет собой замкнутую форму, поверхность которой может быть закрашена и границы которой определяются набором вершин, соединённых отрезками. Многоугольники являются основными компонентами, из которых формируются трехмерные закрашенные объекты. Библиотека позволяет формировать несколько типов многоугольников: треугольники, четырехугольники, многоугольники.

    У всех многоугольников выделяют лицевую и обратную грани. Библиотека позволяет для каждой из граней задавать различные свойства, например, такие как: свойства материала, видимость, освещённость, режим отображения. По умолчанию лицевой гранью является та, которая получается при обходе вершин против часовой стрелки.

    Треугольники

    Библиотека реализует три примитива для рисования треугольников: несвязанные треугольники, треугольники с общей гранью, треугольники с общей вершиной.

    Несвязанные треугольники

    Для задания этого примитива используется константа GL_TRIANGLES в Object Pascal и значение Triangles перечисления BeginMode в библиотеке OpenTK на C# в качестве значения параметра команды Begin. Для построения каждого очередного треугольника требуется три вершины. Если задано количество вершин не кратное трем, то последние вершины игнорируются и не используются для построения треугольника.

    Порядок обхода вершин совпадает с порядком задания их вершин.

    В (C#) и (Object Pascal) приведены примеры формирования двух треугольников, имеющих различные направления обхода вершин. Последняя седьмая вершина игнорируется, так как для построения третьего треугольника необходимо еще две вершины. На рис 4.3" type="example"/> (C#) и (Object Pascal) приведены примеры формирования двух треугольников, имеющих различные направления обхода вершин. Последняя седьмая вершина игнорируется, так как для построения третьего треугольника необходимо еще две вершины. На порядок обхода вершин каждого треугольника. Для первого треугольника (расположен слева на рис 4.5) порядок обхода совпадает с направлением часовой стрелки и видимая сторона треугольника считается обратной. Для второго треугольника (расположен справа на рис 4.5) порядок обхода соответствует направлению против часовой стрелки и видимая сторона треугольника является лицевой.

    GL.Begin(BeginMode.Triangles);
      // треугольник 1
      GL.Vertex3(-0.7, -0.7, 0);
      GL.Vertex3(-0.7, 0.7, 0);
      GL.Vertex3(-0.1, -0.7, 0);
      // треугольник 2
      GL.Vertex3(0.0, 0.2, 0);
      GL.Vertex3(0.5, -0.5, 0);
      GL.Vertex3(0.7, 0.3, 0);
      // отдельная вершина игнорируется
      GL.Vertex3(0.9, -0.9, 0);
    GL.End()
    
    glBegin(GL_TRIANGLES);
      // треугольник 1
      glVertex3f(-0.7, -0.7, 0);
      glVertex3f(-0.7, 0.7,  0);
      glVertex3f(-0.1, -0.7, 0);
      // треугольник 2
      glVertex3f(0.0, 0.2, 0);
      glVertex3f(0.5, -0.5, 0);
      glVertex3f(0.7, 0.3,  0);
      // отдельная вершина игнорируется
      glVertex3f(0.9, -0.9, 0);
    glEnd;
    
    (рис 4.5) Схема формирования отдельных треугольников

    Треугольники с общей гранью

    Для задания этого примитива в качестве параметра команды Begin используется константа GL_TRIANGLE_STRIP в Object Pascal и значение TriangleStrip перечисления BeginMode в библиотеке OpenTK на C#. Первые три вершины определяют первый треугольник. Каждая следующая вершина создает очередной треугольник на основе последних двух вершин предыдущего треугольника и новой вершины.

    На рис 4.6 приведен результат формирования треугольников для фрагментов программ из и , если в листинге 4.3 (фрагмент программы на C#) значение параметра метода GL.Begin заменить на BeginMode.TriangleStrip, а в листинге 4.4 (фрагмент программы на Object Pascal) значение параметра процедуры glBegin заменить на константу GL_TRIANGLE_STRIP.

    (рис 4.6) Схема формирования треугольников с общей гранью

    Обратите внимание, что для второго и последующих треугольников порядок обхода вершин отличается от первого треугольника. Порядок обхода для них следующий: от последней вершины предыдущего треугольника к предпоследней вершине того же треугольника, затем к новой вершине и снова к последней вершине предыдущего треугольника.

    Причиной такого порядка обхода вершин является применение этого примитива. Он обычно используется для формирования фрагмента поверхности сложной фигуры в виде "ленты". Для этого в программе обычно используются циклические конструкции, которые поочередно задают вершины на верхней и нижней стороне такой "ленты". В результате используемого порядка обхода вершин (рис 4.6) направление обхода вершин всех треугольников совпадает с направлением обхода первого треугольника.

    Треугольники с общей вершиной

    Для формирования этого примитива значение параметра команды Begin при использовании библиотеки OpenTK должно быть равно значению TriangleFan перечисления BeginMode, в Object Pascal значение параметра должно быть равно константе GL_TRIANGLE_FAN.

    Каждая следующая вершина создает очередной треугольник на основе первой вершины первого треугольника и последних двух вершин.

    В и приведены фрагменты программы на C# с использованием библиотеки

    GL.Begin(BeginMode.TriangleFan);
      GL.Vertex3(-0.5, -0.5, 0);
      GL.Vertex3(0, 0.8, 0);
      GL.Vertex3(0.5, 0.6, 0);
      GL.Vertex3(0.8, 0.1, 0);
      GL.Vertex3(0.7, -0.6, 0);
    GL.End();
    
    glBegin(GL_TRIANGLE_FAN);
      glVertex3f(-0.5, -0.5, 0);
      glVertex3f(0, 0.8,  0);
      glVertex3f(0.5, 0.6, 0);
      glVertex3f(0.8, 0.1, 0);
      glVertex3f(0.7, -0.6, 0);
    glEnd;
    
    (рис 4.7) Схема формирования треугольников с общей вершиной

    Порядок обхода вершин треугольников соответствует порядку задания вершин, используемых для их построения: от первой вершины первого треугольника к последней вершине последнего сформированного треугольника, затем к новой вершине, и затем к первой вершине первого треугольника. В результате направление обхода вершин каждого треугольника совпадает с направлением обхода вершин первого треугольника.

    Четырехугольники

    Библиотека OpenGL реализуется два примитива для рисования четырехугольников: несвязанные четырехугольники и четырехугольники с общей гранью.

    Несвязанные четырехугольники

    Для формирования данного примитива с использованием библиотеки OpenTK на C# в качестве параметра метода Begin класса GL необходимо указать значение Quads перечисления BeginMode. В Object Pascal для этого необходимо процедуре glBegin в качестве параметра указать константу GL_QUADS.

    Для формирования каждого очередного четырехугольника используются четыре вершины. Если задано меньше 4 вершин, то четырехугольник не формируется. Порядок обхода вершин определяется последовательностью задания вершин. На рис 4.8 отображены два четырехугольника, которые заданы с помощью 8 вершин ( и ).

    GL.Begin(BeginMode.Quads);
      // первый четырехугольник
      GL.Vertex3(-0.9, -0.6, 0);
      GL.Vertex3(-0.9, 0.6, 0);
      GL.Vertex3(-0.3, 0.6, 0);
      GL.Vertex3(-0.3, -0.6, 0);
      // второй четырехугольник
      GL.Vertex3(0.3, -0.6, 0);
      GL.Vertex3(0.3, 0.6, 0);
      GL.Vertex3(0.9, 0.6, 0);
      GL.Vertex3(0.9, -0.6, 0);
    GL.End(); 
    
    glBegin(GL_QUADS);
      // первый четырехугольник
      glVertex3f(-0.9, -0.6, 0);
      glVertex3f(-0.9, 0.6,  0);
      glVertex3f(-0.3, 0.6, 0);
      glVertex3f(-0.3, -0.6,  0);
      // второй четырехугольник
      glVertex3f(0.3, -0.6, 0);
      glVertex3f(0.3, 0.6,  0);
      glVertex3f(0.9, 0.6, 0);
      glVertex3f(0.9, -0.6,  0);
    glEnd;
    
    (рис 4.8) Схема формирования несвязанных четырехугольников

    В плоскости XY четырехугольники могут формироваться с помощью команды

    Rect [d f I s][v]. 
    

    В C# команда реализуется с помощью набора статических методов класса GL, например:

    void Rect(double x1, double y1, double x2, double y2);
    void Rect(ref double v1, ref double v2);
    

    В Object Pascal эта команда реализуется с помощью ряда процедур, таких как:

    procedure glRectd (x1, y1, x2, y2: GLdouble);
    procedure glRectfv (v1, v2: PGLfloat);
    

    Четырехугольники с общей гранью

    Данный примитив задается на C# с помощью значения QuadStrip перечисления BeginMode и константы GL_QUAD_STRIP на Object Pascal, которые передаются в качестве параметра команде Begin.

    Для построения первого четырехугольника требуется 4 вершины. Для формирования каждого последующего четырехугольника требуется задать очередные две вершины. Если вершин недостаточно для построения очередного четырехугольника, то они игнорируются.

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

    GL.Begin(BeginMode.QuadStrip);
    for (int i = 0; i <= 2; ++i)
    {
      GL.Vertex3(-0.9 + 0.8 * i, -0.6, 0);
      GL.Vertex3(-0.9 + 0.8 * i, 0.6, 0);
    }
    GL.End();
    
    glBegin(GL_QUAD_STRIP);
      for I := 0 to 2 do
      begin
        glVertex3f(-0.9 + 0.8*i, -0.6, 0);
        glVertex3f(-0.9 + 0.8*i,  0.6, 0);
      end;
    glEnd;
    
    (рис 4.9) Схема формирования четырехугольников с общей гранью

    Порядок обхода вершин не совпадает с порядком задания вершин. Порядок обхода вершин первого четырехугольника ): от предпоследней вершины (3) к последней вершине (4) последнего четырехугольника, затем к последней новой вершине (6) и к предпоследней новой вершине (5). В результате направление обхода вершин всех четырёхугольников совпадает с направлением обхода вершин первого четырехугольника.

    Многоугольники

    Данный примитив задается на Object Pascal с помощью константы GL_POLYGON и на C# - с помощью значения Polygon перечисления BeginMode, которые передаются в качестве значения параметра команде Begin.

    Порядок обхода вершин многоугольника для определения лицевой и обратной граней соответствует порядку задания вершин. Примитив может использоваться только для создания выпуклых многоугольников.

    Пример формирования многоугольника приведен в (C#) и (Object Pascal). На рис 4.11" type="example"/> (C#) и (Object Pascal). На

    GL.Begin(BeginMode.Polygon);
    GL.Vertex3(-0.9, -0.6, 0);
    GL.Vertex3(-0.9, 0.6, 0);
    GL.Vertex3(0, 0.6, 0.3);
    GL.Vertex3(0.9, 0, 0);
    GL.Vertex3(0, -0.6, -0.3);
    GL.End();
    
    glBegin(GL_POLYGON);
      glVertex3f(-0.9, -0.6, 0);
      glVertex3f(-0.9,  0.6, 0);
      glVertex3f(0,  0.6, 0.3);
      glVertex3f(0.9,  0, 0);
      glVertex3f(0, -0.6, -0.3);
    glEnd;
    
    (рис 4.10) Схема формирования многоугольника

    Параметры многоугольников

    Библиотека OpenGL позволяет определять ряд параметров для всех типов многоугольников.

    Режим отображения граней многоугольника

    Библиотека OpenGL позволяет для многоугольников определять режим отображения лицевых и обратных граней. Для этого используется команда

    PolygonMode (face, mode: GLenum);
    

    В библиотеке OpenTK на C# команда реализуется с помощью статического метода класса GL:

    void PolygonMode(MaterialFace face, PolygonMode mode);
    

    В Object Pascal команда реализуется с помощью процедуры:

    procedure glPolygonMode (face, mode: GLenum);
    

    Первый параметр команды face определяет грани, для которых определяется режим отображения. Параметр может принимать значения, перечисленные в таблица 4.1.

    Возможные значения параметра face команды PolygonMode
    Назначение параметраЗначение параметра
    Библиотека OpenTK, C#Object Pascal
    Лицевые грани многоугольниковMaterialFace.FrontGL_FRONT
    Обратные грани многоугольниковMaterialFace.BackGL_BACK
    Лицевые и обратные грани многоугольниковMaterialFace.FrontAndBackGL_FRONT_AND_BACK

    Второй параметр ).

    Возможные значения параметра mode команды PolygonMode
    Назначение параметраЗначение параметра
    Библиотека OpenTK, C#Object Pascal
    Выбранная грань будет отображаться в виде точекPolygonMode.PointGL_POINT
    Выбранная грань будет отображаться в виде отрезков, соединяющих вершиныPolygonMode.LineGL_LINE
    Выбранная грань будет закрашиватьсяPolygonMode.FillGL_FILL

    По умолчанию для лицевых и обратных граней используется режим закрашивания поверхности (GL_FILL в Object Pascal, PolygonMode.Fill – на C#).

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

    Режим отбора граней

    Режим отбора граней позволяет определить, какие из граней (лицевые, обратные) будут отображаться. Для включения и выключения режима используются команды Enable и Disable с параметром GL_CULL_FACE на Object Pascal и значением CullFace перечисления EnableCap на C#.

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

    CullFace (mode: GLenum);
    

    В C# команда реализуется с помощью статического метода класса GL библиотеки OpenTK:

    void CullFace(CullFaceMode mode);
    

    В Object Pascal команда реализуется с помощью процедуры:

    procedure glCullFace (mode: GLenum);
    

    В качестве параметра ).

    Возможные значения параметра mode команды CullFace
    Назначение параметраЗначение параметра
    Библиотека OpenTK, C#Object Pascal
    Не будут отображаться только лицевые граниMaterialFace.FrontGL_FRONT
    Не будут отображаться только обратные граниMaterialFace.BackGL_BACK
    Не будут отображаться и лицевые и обратные грани, т.е. многоугольник отображаться не будетMaterialFace.FrontAndBackGL_FRONT_AND_BACK

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

    Краткие итоги

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

    Упражнения. Построение графиков функций

    Пример выполнения задания

    Задание: Постройте график функции $$y=z sin(x n)$$. Реализуйте возможность интерактивного изменения коэффициентов $$z$$ и $$n$$ с помощью клавиатуры.

    Выполнение задания

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

    Построение графика можно выполнить в обработчике события OnPaint формы на Object Pascal и события Paint компонента GLControl на C#. Код для построения графика функции можно расположить непосредственно после фрагмента кода, выполняющего построение осей координат. Для реализации также необходимо будет использовать глобальные переменные:

    C#:
    double n = 1;
    double z = 1;
    
    Object Pascal:
    var
      n: double =1;
      z: double =1;
    

    График функции может быть построен с помощью фрагмента, приведенного ниже:

    C#:
    GL.Color3(1f, 0, 0);
    GL.Begin(BeginMode.LineStrip);
    for (int i = -50; i <= 50; ++i)
    {
    double x = i * 0.1;
    GL.Vertex2(x, Math.Sin(x*n)*z);
    }
    GL.End();
    Object Pascal:
    glColor3f(1, 0, 0);
    glBegin (GL_LINE_STRIP);
      for i:=-50 to 50 do
      begin
        x := i*0.1;
        glVertex2f (x, Sin(x*n)*z);
      end;
    glEnd;
    

    В этом фрагменте кода предполагается, что область видимости вдоль направления оси OX будет не менее 10 единиц. Изменить размер сцены можно в обработчике события изменения размеров формы в программе на Object Pascal и изменения размеров графического компонента GLControl на C#.

    C#:
    private void glControl1_Resize(object sender, EventArgs e)
    {
    GL.Viewport(0, 0, glControl1.Width, glControl1.Height);
    GL.MatrixMode(MatrixMode.Projection);
    GL.LoadIdentity();
    GL.Ortho(-6, 6, -6, 6, -6, 6);
    GL.MatrixMode(MatrixMode.Modelview);
    glControl1.Invalidate();
    }
    
    Object Pascal:
    procedure TfrmMain.FormResize(Sender: TObject);
    begin
     glViewport(0, 0, ClientWidth, ClientHeight);
     glMatrixMode(GL_PROJECTION);
     glLoadIdentity;
     glOrtho(-6, 6, -6, 6, -6, 6);
     glMatrixMode(GL_MODELVIEW);
     InvalidateRect(Handle, nil, False);
    end;
    

    Для уменьшения и увеличения значения переменной n можно использовать клавиши со стрелками влево и вправо. Уменьшение и увеличение значения параметра z можно реализовать с помощью клавиш со стрелками вниз и вверх.

    C#:
    private void glControl1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
    {
      switch (e.KeyCode)
      {
        case Keys.Insert: AngleX += AngleDl; break;
        case Keys.Delete: AngleX -= AngleDl; break;
    case Keys.Home: AngleY += AngleDl; break;
    case Keys.End: AngleY -= AngleDl; break;
    case Keys.Prior: AngleZ += AngleDl; break;
    case Keys.Next: AngleZ -= AngleDl; break;
    case Keys.Up: z += 0.1; break;
    case Keys.Down: z -= 0.1; break;
    case Keys.Left: n -= 0.1; break;
    case Keys.Right: n += 0.1; break;
    }
    glControl1.Invalidate();
    Text =
      String.Format("График функции y = {0}*Sin(x*{1})", z, n);
    }
    Object Pascal:
    procedure TfrmMain.FormKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    begin
     If Key = VK_ESCAPE then Close;
     case Key of
      VK_INSERT: inc(AngleX, AngleDl);
      VK_DELETE: dec(AngleX, AngleDl);
      VK_HOME:   inc(AngleY, AngleDl);
      VK_END:    dec(AngleY, AngleDl);
      VK_PRIOR:  inc(AngleZ, AngleDl);
      VK_NEXT:   dec(AngleZ, AngleDl);
      VK_UP:     z := z + 0.1;
      VK_DOWN:   z := z - 0.1;
      VK_LEFT:   n := n - 0.1;
      VK_RIGHT:  n := n + 0.1;
     end;
     InvalidateRect(Handle, nil, False);
    Caption := 'График функции y = '+ FloatToStr(z) +'    *Sin(x*'+FloatToStr(n)+')';
    end;
    

    Задания

  • Постройте график функции $$y=zcos(xn)$$. Реализуйте возможность интерактивного изменения коэффициентов $$z$$ и $$n$$ с помощью клавиатуры.
  • Постройте график функции кохлеоиды, уравнение которой в полярной системе координат: $$\rho=a\frac{\sin(\phi)}{\phi}$$. Реализуйте возможность интерактивного изменения коэффициента $$a$$ с помощью клавиатуры.
  • Постройте график функции лемниската Бернулли, уравнение которой в полярной системе координат: $$\rho^2=2a^2cos(2\phi)$$. Реализуйте возможность интерактивного изменения коэффициента $$a$$ с помощью клавиатуры.
  • Постройте график функции синусоидальной спирали, уравнение которой в полярной системе координат $$\rho^m=a^msin(m\phi)$$, при $$m=4$$ и $$m=-4$$. Реализуйте возможность интерактивного изменения коэффициента $$a$$ с помощью клавиатуры.
  • Постройте график функции овала Кассини, уравнение которой в полярных координатах $$\rho^2=c^2cos(2\phi)\pm\sqrt{c^2cos(2\phi)+a^4-c^4}$$. Реализуйте возможность интерактивного изменения коэффициентов $$a$$ и $$c$$ с помощью клавиатуры.
  • Постройте график функции розы, уравнение которой в полярных координатах $$\rho=a\sin(r\phi)$$. Реализуйте возможность интерактивного изменения коэффициента $$a$$ с помощью клавиатуры.
  • Постройте график спирали Ферма, уравнение которой в полярных координатах $$\rho^2=a^2\phi$$. Реализуйте возможность интерактивного изменения коэффициента $$a$$ с помощью клавиатуры.
  • Постройте график функции, уравнение которой $$F(x,y)=x^6-x^4+y^2=0$$.
  • Постройте график функции эллипса $$\frac{x^2}{a^2}+\frac{y^2}{b^2}=1$$. Реализуйте возможность интерактивного изменения коэффициентов $$a$$ и $$c$$ с помощью клавиатуры.
  • Страницы:

    Цель лекции: Научиться использовать основные примитивы OpenGL для формирования изображения.

    Примеры на C# (WinForms) и Object Pascal (Delphi)

    Пример № 4.1 Пример демонстрирует формирование несвязанных отрезков.

    Пример № 4.2 Пример демонстрирует формирование замкнутой ломаной.

    Пример № 4.3 Пример демонстрирует формирование несвязанных треугольников.

    Пример № 4.4 Пример демонстрирует формирование треугольников с общими гранями.

    Пример № 4.5 Пример демонстрирует формирование треугольников с общей вершиной.

    Пример № 4.6 Пример демонстрирует формирование несвязанных четырехугольников.

    Пример № 4.7 Пример демонстрирует формирование четырехугольников с общими гранями.

    Пример № 4.8 Пример демонстрирует формирование многоугольника.

    Пример № 4.9 Пример демонстрирует построение графика функции y = z*Sin(x*n) с возможностью изменения коэффициентов n и z с помощью клавиатуры.

    В OpenGL все изображения формируются из графических примитивов, основными из которых являются точки, отрезки, треугольники, четырехугольники, многоугольники. Базовым графическим примитивом для построения сложных объёмных изображений является треугольник. Такие примитивы как четырехугольники и многоугольники хотя и создаются с помощью отдельных команд, но формируются библиотекой OpenGL из набора треугольников. Необходимая детализация сложных изображений достигается за счет уменьшения размера примитивов, из которых строится изображение, и увеличения их количества.

    Формирование примитива начинается с вызова команды Begin и передачи ей в качестве значения параметра mode целочисленной константы, которая определяет формируемый примитив:

    Begin (mode: GLenum);
    

    В Object Pascal для реализации команды Begin используется процедура

    procedure glBegin (mode: GLenum);
    

    Например, для формирования точек используется константа GL_POINTS, для формирования отрезков –константа GL_LINES.

    В библиотеке OpenTK для реализации команды Begin используется статический метод класса GL (C#):

    void Begin(BeginMode mode);
    

    Формируемый примитив определяется с помощью элемента перечисления BeginMode. Для формирования точек используется значение этого перечисления Points, для формирования отрезков – значение Lines.

    Формирование примитивов завершается с помощью команды End.

    В Object Pascal эта команда реализуется с помощью процедуры:

    procedure glEnd;
    

    В библиотеке OpenTK для реализации команды End используется статический метод класса GL (C#):

    void End();
    

    Все примитивы состоят из набора вершин. Для задания расположения вершин в пространстве используются нормализованные координаты.

    Координаты каждой вершины определяются с помощью команды OpenGL:

    Vertex[2 3 4][s i f d]
    

    Вызов этой команды должен выполняться межу вызовами команд Begin и End, иначе команда Vertex будет проигнорирована библиотекой.

    Команда может иметь от 2 до 4 параметров. Если команда имеет три параметра, то ей передаются декартовые координаты вершин. В варианте команды с двумя параметрами они соответствуют декартовым координатам по оси OX и OY, координата по оси OZ принимается равной нулю.

    В Object Pascal команда Vertex реализована набором процедур, объявление двух из них приведено ниже:

    procedure glVertex2d (x,y: GLdouble);
    procedure glVertex3d (x,y, z: GLdouble);
    

    В библиотеке OpenTK команда реализуется с помощью статических методов класса GL с различным числом параметров и типами данных. Объявление двух из них приведено ниже.

    void Vertex2(double x, double y);
    void Vertex3(double x, double y, double z);
    

    Необходимо обратить внимание на то, что для каждого вызова команды Begin должен быть соответствующий вызов команды End. Их можно сравнить с парой операторов begin и end языка Pascal, которые так же являются парными. Но в отличие от них для команд Begin и End компилятор не проверяет их парность, и эта задача ложится полностью на программиста. При несоблюдении парности вызовов команд Begin и End часто возникают довольно сложно выявляемые ошибки в формировании изображения.

    Точки

    Для формирования точек в качестве значения параметра mode команды Begin необходимо указать значение:

  • константу GL_POINTS в программе на Object Pascal;
  • значение Points перечисления BeginMode в программе на C#.
  • Перед формированием точек могут быть указаны их размер и определён режим сглаживания, который будет использоваться при их отображении. Эти параметры должны быть установлены перед вызовом команды Begin.

    Размер точки

    Размер точки может быть установлен с помощью команды PointSize. В Object Pascal команда реализуется с помощью процедуры:

    procedure glPointSize (size: GLfloat);
    

    В библиотеке OpenTK для этого используется статический метод класса GL (C#):

    void PointSize(float size);
    

    По умолчанию размер точки равен одному пикселу. Необходимо обратить внимание на то, что хотя координаты точки задаются в масштабе сцены, но размер точки задается в координатах устройства вывода изображения. Связано это с тем, что точки формируются на одном из последних этапов формирования изображения – на этапе растеризации.

    Режим сглаживания точки

    Управление режимом сглаживания точки осуществляется с помощью команд Enable и Disable. Команда Enable включает режим сглаживания точек, команда Disable отключает его. По умолчанию режим сглаживания точек отключен.

    В Object Pascal эти команды реализованы с помощью процедур:

    procedure glEnable(cap: GLenum);
    procedure glDisable(cap: GLenum);
    

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

    В библиотеке OpenTK для реализации этих команд используются статические методы класса GL (C#):

    void Enable(EnableCap cap);
    void Disable(EnableCap cap);
    

    Для управления режимом сглаживания необходимо указать значение PointSmooth перечисления EnableCap.

    При отключенном режиме сглаживания точка рисуется в виде квадрата. При включенном режиме сглаживания библиотека пытается изобразить точку в виде круга. На рис 4.1 приведены изображения точки разных размеров с выключенным (изображения слева) и включенным (изображения справа) режимом сглаживания.

    (рис 4.1) Изображения точки разного размера с использованием режима сглаживания и без него

    Отрезки

    Библиотека реализует три примитива для формирования отрезков.

    Несвязанные отрезки

    Для формирования несвязанных отрезков значение параметра команды Begin должно быть равно значению Lines перечисления BeginMode при использовании библиотеки OpenTK (C#) и константе GL_LINES в программе на Object Pascal.

    Каждая пара вершин определяет отрезок. Если задано нечетное количество вершин, то последняя вершина игнорируется и не будет использоваться для построения отрезков. Если будет задана всего одна вершина, то библиотека не сможет построить ни одного отрезка, так как для создания отрезка требуется как был приведен пример формирования несвязанных отрезков. В и формируется три несвязанных отрезка, хотя вершин задано ).

    GL.Begin(BeginMode.Lines);
      GL.Vertex3(-0.5, -0.5, 0);
      GL.Vertex3(-0.5, 0.5, 0);
      GL.Vertex3(0, -0.5, 0);
      GL.Vertex3(0, 0.5, 0);
      GL.Vertex3(0.5, -0.5, 0);
      GL.Vertex3(0.5, 0.5, 0);
      GL.Vertex3(0.7, -0.5, 0);
    GL.End()
    
    glBegin(GL_LINES);
      glVertex3f(-0.5, -0.5, 0);
      glVertex3f(-0.5, 0.5,  0);
      glVertex3f(0, -0.5, 0);
      glVertex3f(0, 0.5,  0);
      glVertex3f(0.5, -0.5, 0);
      glVertex3f(0.5, 0.5,  0);
      glVertex3f(0.7, -0.5, 0);
    glEnd
    
    (рис 4.2) Схема формирования несвязанных отрезков

    Ломаные линии

    Для формирования ломаных линий значение параметра команды Begin должно быть равно значению LineStrip перечисления BeginMode при использовании библиотеки OpenTK (C#) и константе GL_LINE_STRIP в программе на языке Object Pascal.

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

    Если в листинге 4.1 заменить параметр процедуры ).

    (рис 4.3) Схема формирования ломаной линии (рис 4.4) Схема формирования замкнутой ломаной

    Замкнутая ломаная

    Для формирования замкнутой ломаной необходимо задать значение параметра mode команды Begin:

  • константу GL_LINE_LOOP в программе на языке Object Pascal;
  • значение LineLoop перечисления BeginMode в программе на языке C#.
  • Примитив формируется аналогично ломаной линии с одним дополнением: отрезком так же соединяются первая и последняя вершина. Таким образом, в результате получается замкнутая ломаная.

    Если в листинге 4.1 программы на C# заменить параметр метода ). По сравнению с рис 4.1" type="example">листинге 4.1 программы на C# заменить параметр метода ). По сравнению с Чтобы получить аналогичный результат в программе на Object Pascal, фрагмент которой приведен в листинге 4.2, необходимо заменить параметр процедуры glBegin на константу GL_LINE_LOOP.

    Ширина и сглаживание линии

    Для всех типов отрезков могут быть заданы такие параметры как ширина и сглаживание линий.

    Для установки ширины линии используется команда LineWidth. В качестве параметра команде передается ширина линии в пикселах. В библиотеке OpenTK команда LineWidth реализуется с помощью статического метода класса GL (C#):

    void LineWidth(float width);
    

    В Object Pascal команда реализуется с помощью процедуры:

    procedure glLineWidth (width: GLfloat);
    

    Библиотека OpenGL имеет ограничение на максимальную ширину линии. Это значение, как и для размера точки, зависит от конкретной реализации библиотеки OpenGL. Алгоритм измерения ширины линии зависит от установленного режима сглаживания линий. По умолчанию режим сглаживания линий отключён. В этом случае ширина линии вычисляется вдоль оси OX или оси OY проекции в зависимости от угла наклона линии. Если координаты концов отрезка на проекции (x1, y1) и (x2, y2), то ширина отрезка будет вычисляться вдоль оси OY если |y1 – y2| < |x1 – x2| и в направлении оси OX если |y1 – y2| > |x1 – x2|. Поэтому при отключенном режиме сглаживания (режим по умолчанию) визуальная ширина линии может меняться в зависимости от угла наклона проекции линии.

    При включенном режиме сглаживания линий толщина отрезка измеряется перпендикулярно линии. Для включения и отключения режима сглаживания линий используются соответственно команды Enable и Disable со значением параметра GL_LINE_SMOOTH на Object Pascal и значением LineSmooth перечисления EnableCap на C#.

    Многоугольники

    Многоугольник представляет собой замкнутую форму, поверхность которой может быть закрашена и границы которой определяются набором вершин, соединённых отрезками. Многоугольники являются основными компонентами, из которых формируются трехмерные закрашенные объекты. Библиотека позволяет формировать несколько типов многоугольников: треугольники, четырехугольники, многоугольники.

    У всех многоугольников выделяют лицевую и обратную грани. Библиотека позволяет для каждой из граней задавать различные свойства, например, такие как: свойства материала, видимость, освещённость, режим отображения. По умолчанию лицевой гранью является та, которая получается при обходе вершин против часовой стрелки.

    Треугольники

    Библиотека реализует три примитива для рисования треугольников: несвязанные треугольники, треугольники с общей гранью, треугольники с общей вершиной.

    Несвязанные треугольники

    Для задания этого примитива используется константа GL_TRIANGLES в Object Pascal и значение Triangles перечисления BeginMode в библиотеке OpenTK на C# в качестве значения параметра команды Begin. Для построения каждого очередного треугольника требуется три вершины. Если задано количество вершин не кратное трем, то последние вершины игнорируются и не используются для построения треугольника.

    Порядок обхода вершин совпадает с порядком задания их вершин.

    В (C#) и (Object Pascal) приведены примеры формирования двух треугольников, имеющих различные направления обхода вершин. Последняя седьмая вершина игнорируется, так как для построения третьего треугольника необходимо еще две вершины. На рис 4.3" type="example"/> (C#) и (Object Pascal) приведены примеры формирования двух треугольников, имеющих различные направления обхода вершин. Последняя седьмая вершина игнорируется, так как для построения третьего треугольника необходимо еще две вершины. На порядок обхода вершин каждого треугольника. Для первого треугольника (расположен слева на рис 4.5) порядок обхода совпадает с направлением часовой стрелки и видимая сторона треугольника считается обратной. Для второго треугольника (расположен справа на рис 4.5) порядок обхода соответствует направлению против часовой стрелки и видимая сторона треугольника является лицевой.

    GL.Begin(BeginMode.Triangles);
      // треугольник 1
      GL.Vertex3(-0.7, -0.7, 0);
      GL.Vertex3(-0.7, 0.7, 0);
      GL.Vertex3(-0.1, -0.7, 0);
      // треугольник 2
      GL.Vertex3(0.0, 0.2, 0);
      GL.Vertex3(0.5, -0.5, 0);
      GL.Vertex3(0.7, 0.3, 0);
      // отдельная вершина игнорируется
      GL.Vertex3(0.9, -0.9, 0);
    GL.End()
    
    glBegin(GL_TRIANGLES);
      // треугольник 1
      glVertex3f(-0.7, -0.7, 0);
      glVertex3f(-0.7, 0.7,  0);
      glVertex3f(-0.1, -0.7, 0);
      // треугольник 2
      glVertex3f(0.0, 0.2, 0);
      glVertex3f(0.5, -0.5, 0);
      glVertex3f(0.7, 0.3,  0);
      // отдельная вершина игнорируется
      glVertex3f(0.9, -0.9, 0);
    glEnd;
    
    (рис 4.5) Схема формирования отдельных треугольников

    Треугольники с общей гранью

    Для задания этого примитива в качестве параметра команды Begin используется константа GL_TRIANGLE_STRIP в Object Pascal и значение TriangleStrip перечисления BeginMode в библиотеке OpenTK на C#. Первые три вершины определяют первый треугольник. Каждая следующая вершина создает очередной треугольник на основе последних двух вершин предыдущего треугольника и новой вершины.

    На рис 4.6 приведен результат формирования треугольников для фрагментов программ из и , если в листинге 4.3 (фрагмент программы на C#) значение параметра метода GL.Begin заменить на BeginMode.TriangleStrip, а в листинге 4.4 (фрагмент программы на Object Pascal) значение параметра процедуры glBegin заменить на константу GL_TRIANGLE_STRIP.

    (рис 4.6) Схема формирования треугольников с общей гранью

    Обратите внимание, что для второго и последующих треугольников порядок обхода вершин отличается от первого треугольника. Порядок обхода для них следующий: от последней вершины предыдущего треугольника к предпоследней вершине того же треугольника, затем к новой вершине и снова к последней вершине предыдущего треугольника.

    Причиной такого порядка обхода вершин является применение этого примитива. Он обычно используется для формирования фрагмента поверхности сложной фигуры в виде "ленты". Для этого в программе обычно используются циклические конструкции, которые поочередно задают вершины на верхней и нижней стороне такой "ленты". В результате используемого порядка обхода вершин (рис 4.6) направление обхода вершин всех треугольников совпадает с направлением обхода первого треугольника.

    Треугольники с общей вершиной

    Для формирования этого примитива значение параметра команды Begin при использовании библиотеки OpenTK должно быть равно значению TriangleFan перечисления BeginMode, в Object Pascal значение параметра должно быть равно константе GL_TRIANGLE_FAN.

    Каждая следующая вершина создает очередной треугольник на основе первой вершины первого треугольника и последних двух вершин.

    В и приведены фрагменты программы на C# с использованием библиотеки

    GL.Begin(BeginMode.TriangleFan);
      GL.Vertex3(-0.5, -0.5, 0);
      GL.Vertex3(0, 0.8, 0);
      GL.Vertex3(0.5, 0.6, 0);
      GL.Vertex3(0.8, 0.1, 0);
      GL.Vertex3(0.7, -0.6, 0);
    GL.End();
    
    glBegin(GL_TRIANGLE_FAN);
      glVertex3f(-0.5, -0.5, 0);
      glVertex3f(0, 0.8,  0);
      glVertex3f(0.5, 0.6, 0);
      glVertex3f(0.8, 0.1, 0);
      glVertex3f(0.7, -0.6, 0);
    glEnd;
    
    (рис 4.7) Схема формирования треугольников с общей вершиной

    Порядок обхода вершин треугольников соответствует порядку задания вершин, используемых для их построения: от первой вершины первого треугольника к последней вершине последнего сформированного треугольника, затем к новой вершине, и затем к первой вершине первого треугольника. В результате направление обхода вершин каждого треугольника совпадает с направлением обхода вершин первого треугольника.

    Четырехугольники

    Библиотека OpenGL реализуется два примитива для рисования четырехугольников: несвязанные четырехугольники и четырехугольники с общей гранью.

    Несвязанные четырехугольники

    Для формирования данного примитива с использованием библиотеки OpenTK на C# в качестве параметра метода Begin класса GL необходимо указать значение Quads перечисления BeginMode. В Object Pascal для этого необходимо процедуре glBegin в качестве параметра указать константу GL_QUADS.

    Для формирования каждого очередного четырехугольника используются четыре вершины. Если задано меньше 4 вершин, то четырехугольник не формируется. Порядок обхода вершин определяется последовательностью задания вершин. На рис 4.8 отображены два четырехугольника, которые заданы с помощью 8 вершин ( и ).

    GL.Begin(BeginMode.Quads);
      // первый четырехугольник
      GL.Vertex3(-0.9, -0.6, 0);
      GL.Vertex3(-0.9, 0.6, 0);
      GL.Vertex3(-0.3, 0.6, 0);
      GL.Vertex3(-0.3, -0.6, 0);
      // второй четырехугольник
      GL.Vertex3(0.3, -0.6, 0);
      GL.Vertex3(0.3, 0.6, 0);
      GL.Vertex3(0.9, 0.6, 0);
      GL.Vertex3(0.9, -0.6, 0);
    GL.End(); 
    
    glBegin(GL_QUADS);
      // первый четырехугольник
      glVertex3f(-0.9, -0.6, 0);
      glVertex3f(-0.9, 0.6,  0);
      glVertex3f(-0.3, 0.6, 0);
      glVertex3f(-0.3, -0.6,  0);
      // второй четырехугольник
      glVertex3f(0.3, -0.6, 0);
      glVertex3f(0.3, 0.6,  0);
      glVertex3f(0.9, 0.6, 0);
      glVertex3f(0.9, -0.6,  0);
    glEnd;
    
    (рис 4.8) Схема формирования несвязанных четырехугольников

    В плоскости XY четырехугольники могут формироваться с помощью команды

    Rect [d f I s][v]. 
    

    В C# команда реализуется с помощью набора статических методов класса GL, например:

    void Rect(double x1, double y1, double x2, double y2);
    void Rect(ref double v1, ref double v2);
    

    В Object Pascal эта команда реализуется с помощью ряда процедур, таких как:

    procedure glRectd (x1, y1, x2, y2: GLdouble);
    procedure glRectfv (v1, v2: PGLfloat);
    

    Четырехугольники с общей гранью

    Данный примитив задается на C# с помощью значения QuadStrip перечисления BeginMode и константы GL_QUAD_STRIP на Object Pascal, которые передаются в качестве параметра команде Begin.

    Для построения первого четырехугольника требуется 4 вершины. Для формирования каждого последующего четырехугольника требуется задать очередные две вершины. Если вершин недостаточно для построения очередного четырехугольника, то они игнорируются.

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

    GL.Begin(BeginMode.QuadStrip);
    for (int i = 0; i <= 2; ++i)
    {
      GL.Vertex3(-0.9 + 0.8 * i, -0.6, 0);
      GL.Vertex3(-0.9 + 0.8 * i, 0.6, 0);
    }
    GL.End();
    
    glBegin(GL_QUAD_STRIP);
      for I := 0 to 2 do
      begin
        glVertex3f(-0.9 + 0.8*i, -0.6, 0);
        glVertex3f(-0.9 + 0.8*i,  0.6, 0);
      end;
    glEnd;
    
    (рис 4.9) Схема формирования четырехугольников с общей гранью

    Порядок обхода вершин не совпадает с порядком задания вершин. Порядок обхода вершин первого четырехугольника ): от предпоследней вершины (3) к последней вершине (4) последнего четырехугольника, затем к последней новой вершине (6) и к предпоследней новой вершине (5). В результате направление обхода вершин всех четырёхугольников совпадает с направлением обхода вершин первого четырехугольника.

    Многоугольники

    Данный примитив задается на Object Pascal с помощью константы GL_POLYGON и на C# - с помощью значения Polygon перечисления BeginMode, которые передаются в качестве значения параметра команде Begin.

    Порядок обхода вершин многоугольника для определения лицевой и обратной граней соответствует порядку задания вершин. Примитив может использоваться только для создания выпуклых многоугольников.

    Пример формирования многоугольника приведен в (C#) и (Object Pascal). На рис 4.11" type="example"/> (C#) и (Object Pascal). На

    GL.Begin(BeginMode.Polygon);
    GL.Vertex3(-0.9, -0.6, 0);
    GL.Vertex3(-0.9, 0.6, 0);
    GL.Vertex3(0, 0.6, 0.3);
    GL.Vertex3(0.9, 0, 0);
    GL.Vertex3(0, -0.6, -0.3);
    GL.End();
    
    glBegin(GL_POLYGON);
      glVertex3f(-0.9, -0.6, 0);
      glVertex3f(-0.9,  0.6, 0);
      glVertex3f(0,  0.6, 0.3);
      glVertex3f(0.9,  0, 0);
      glVertex3f(0, -0.6, -0.3);
    glEnd;
    
    (рис 4.10) Схема формирования многоугольника

    Параметры многоугольников

    Библиотека OpenGL позволяет определять ряд параметров для всех типов многоугольников.

    Режим отображения граней многоугольника

    Библиотека OpenGL позволяет для многоугольников определять режим отображения лицевых и обратных граней. Для этого используется команда

    PolygonMode (face, mode: GLenum);
    

    В библиотеке OpenTK на C# команда реализуется с помощью статического метода класса GL:

    void PolygonMode(MaterialFace face, PolygonMode mode);
    

    В Object Pascal команда реализуется с помощью процедуры:

    procedure glPolygonMode (face, mode: GLenum);
    

    Первый параметр команды face определяет грани, для которых определяется режим отображения. Параметр может принимать значения, перечисленные в таблица 4.1.

    Возможные значения параметра face команды PolygonMode
    Назначение параметраЗначение параметра
    Библиотека OpenTK, C#Object Pascal
    Лицевые грани многоугольниковMaterialFace.FrontGL_FRONT
    Обратные грани многоугольниковMaterialFace.BackGL_BACK
    Лицевые и обратные грани многоугольниковMaterialFace.FrontAndBackGL_FRONT_AND_BACK

    Второй параметр ).

    Возможные значения параметра mode команды PolygonMode
    Назначение параметраЗначение параметра
    Библиотека OpenTK, C#Object Pascal
    Выбранная грань будет отображаться в виде точекPolygonMode.PointGL_POINT
    Выбранная грань будет отображаться в виде отрезков, соединяющих вершиныPolygonMode.LineGL_LINE
    Выбранная грань будет закрашиватьсяPolygonMode.FillGL_FILL

    По умолчанию для лицевых и обратных граней используется режим закрашивания поверхности (GL_FILL в Object Pascal, PolygonMode.Fill – на C#).

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

    Режим отбора граней

    Режим отбора граней позволяет определить, какие из граней (лицевые, обратные) будут отображаться. Для включения и выключения режима используются команды Enable и Disable с параметром GL_CULL_FACE на Object Pascal и значением CullFace перечисления EnableCap на C#.

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

    CullFace (mode: GLenum);
    

    В C# команда реализуется с помощью статического метода класса GL библиотеки OpenTK:

    void CullFace(CullFaceMode mode);
    

    В Object Pascal команда реализуется с помощью процедуры:

    procedure glCullFace (mode: GLenum);
    

    В качестве параметра ).

    Возможные значения параметра mode команды CullFace
    Назначение параметраЗначение параметра
    Библиотека OpenTK, C#Object Pascal
    Не будут отображаться только лицевые граниMaterialFace.FrontGL_FRONT
    Не будут отображаться только обратные граниMaterialFace.BackGL_BACK
    Не будут отображаться и лицевые и обратные грани, т.е. многоугольник отображаться не будетMaterialFace.FrontAndBackGL_FRONT_AND_BACK

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

    Краткие итоги

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

    Упражнения. Построение графиков функций

    Пример выполнения задания

    Задание: Постройте график функции $$y=z sin(x n)$$. Реализуйте возможность интерактивного изменения коэффициентов $$z$$ и $$n$$ с помощью клавиатуры.

    Выполнение задания

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

    Построение графика можно выполнить в обработчике события OnPaint формы на Object Pascal и события Paint компонента GLControl на C#. Код для построения графика функции можно расположить непосредственно после фрагмента кода, выполняющего построение осей координат. Для реализации также необходимо будет использовать глобальные переменные:

    C#:
    double n = 1;
    double z = 1;
    
    Object Pascal:
    var
      n: double =1;
      z: double =1;
    

    График функции может быть построен с помощью фрагмента, приведенного ниже:

    C#:
    GL.Color3(1f, 0, 0);
    GL.Begin(BeginMode.LineStrip);
    for (int i = -50; i <= 50; ++i)
    {
    double x = i * 0.1;
    GL.Vertex2(x, Math.Sin(x*n)*z);
    }
    GL.End();
    Object Pascal:
    glColor3f(1, 0, 0);
    glBegin (GL_LINE_STRIP);
      for i:=-50 to 50 do
      begin
        x := i*0.1;
        glVertex2f (x, Sin(x*n)*z);
      end;
    glEnd;
    

    В этом фрагменте кода предполагается, что область видимости вдоль направления оси OX будет не менее 10 единиц. Изменить размер сцены можно в обработчике события изменения размеров формы в программе на Object Pascal и изменения размеров графического компонента GLControl на C#.

    C#:
    private void glControl1_Resize(object sender, EventArgs e)
    {
    GL.Viewport(0, 0, glControl1.Width, glControl1.Height);
    GL.MatrixMode(MatrixMode.Projection);
    GL.LoadIdentity();
    GL.Ortho(-6, 6, -6, 6, -6, 6);
    GL.MatrixMode(MatrixMode.Modelview);
    glControl1.Invalidate();
    }
    
    Object Pascal:
    procedure TfrmMain.FormResize(Sender: TObject);
    begin
     glViewport(0, 0, ClientWidth, ClientHeight);
     glMatrixMode(GL_PROJECTION);
     glLoadIdentity;
     glOrtho(-6, 6, -6, 6, -6, 6);
     glMatrixMode(GL_MODELVIEW);
     InvalidateRect(Handle, nil, False);
    end;
    

    Для уменьшения и увеличения значения переменной n можно использовать клавиши со стрелками влево и вправо. Уменьшение и увеличение значения параметра z можно реализовать с помощью клавиш со стрелками вниз и вверх.

    C#:
    private void glControl1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
    {
      switch (e.KeyCode)
      {
        case Keys.Insert: AngleX += AngleDl; break;
        case Keys.Delete: AngleX -= AngleDl; break;
    case Keys.Home: AngleY += AngleDl; break;
    case Keys.End: AngleY -= AngleDl; break;
    case Keys.Prior: AngleZ += AngleDl; break;
    case Keys.Next: AngleZ -= AngleDl; break;
    case Keys.Up: z += 0.1; break;
    case Keys.Down: z -= 0.1; break;
    case Keys.Left: n -= 0.1; break;
    case Keys.Right: n += 0.1; break;
    }
    glControl1.Invalidate();
    Text =
      String.Format("График функции y = {0}*Sin(x*{1})", z, n);
    }
    Object Pascal:
    procedure TfrmMain.FormKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    begin
     If Key = VK_ESCAPE then Close;
     case Key of
      VK_INSERT: inc(AngleX, AngleDl);
      VK_DELETE: dec(AngleX, AngleDl);
      VK_HOME:   inc(AngleY, AngleDl);
      VK_END:    dec(AngleY, AngleDl);
      VK_PRIOR:  inc(AngleZ, AngleDl);
      VK_NEXT:   dec(AngleZ, AngleDl);
      VK_UP:     z := z + 0.1;
      VK_DOWN:   z := z - 0.1;
      VK_LEFT:   n := n - 0.1;
      VK_RIGHT:  n := n + 0.1;
     end;
     InvalidateRect(Handle, nil, False);
    Caption := 'График функции y = '+ FloatToStr(z) +'    *Sin(x*'+FloatToStr(n)+')';
    end;
    

    Задания

  • Постройте график функции $$y=zcos(xn)$$. Реализуйте возможность интерактивного изменения коэффициентов $$z$$ и $$n$$ с помощью клавиатуры.
  • Постройте график функции кохлеоиды, уравнение которой в полярной системе координат: $$\rho=a\frac{\sin(\phi)}{\phi}$$. Реализуйте возможность интерактивного изменения коэффициента $$a$$ с помощью клавиатуры.
  • Постройте график функции лемниската Бернулли, уравнение которой в полярной системе координат: $$\rho^2=2a^2cos(2\phi)$$. Реализуйте возможность интерактивного изменения коэффициента $$a$$ с помощью клавиатуры.
  • Постройте график функции синусоидальной спирали, уравнение которой в полярной системе координат $$\rho^m=a^msin(m\phi)$$, при $$m=4$$ и $$m=-4$$. Реализуйте возможность интерактивного изменения коэффициента $$a$$ с помощью клавиатуры.
  • Постройте график функции овала Кассини, уравнение которой в полярных координатах $$\rho^2=c^2cos(2\phi)\pm\sqrt{c^2cos(2\phi)+a^4-c^4}$$. Реализуйте возможность интерактивного изменения коэффициентов $$a$$ и $$c$$ с помощью клавиатуры.
  • Постройте график функции розы, уравнение которой в полярных координатах $$\rho=a\sin(r\phi)$$. Реализуйте возможность интерактивного изменения коэффициента $$a$$ с помощью клавиатуры.
  • Постройте график спирали Ферма, уравнение которой в полярных координатах $$\rho^2=a^2\phi$$. Реализуйте возможность интерактивного изменения коэффициента $$a$$ с помощью клавиатуры.
  • Постройте график функции, уравнение которой $$F(x,y)=x^6-x^4+y^2=0$$.
  • Постройте график функции эллипса $$\frac{x^2}{a^2}+\frac{y^2}{b^2}=1$$. Реализуйте возможность интерактивного изменения коэффициентов $$a$$ и $$c$$ с помощью клавиатуры.
  • Вернуться к учебному плану