Библиотека трехмерной графики Open gl



бет11/15
Дата29.05.2016
өлшемі1.07 Mb.
#100718
1   ...   7   8   9   10   11   12   13   14   15

Модуль 2. Треугольники

Комплексной целью модуля является изучение следующих элементов OpenGL



  • Примитив "треугольник".

  • Типы примитивов, состоящих из треугольников – отдельные треугольники, треугольники, имеющие общие стороны и треугольники, имеющие общие вершины.

  • Атрибуты треугольников гораздо богаче атрибутов точек и прямых линий. Треугольники имеют грани, которые могут сплошными и контурными. К сторонам треугольников применимы атрибуты прямых линий, а к его вершинам – атрибуты точек. Сплошная грань может использовать произвольный шаблон закрашивания.

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



2.0 Примитив "TRIANGLES"


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

Код, который строит отдельные треугольники должен иметь вид.

gl.Begin(gl.TRIANGLES);

// Вершины определяются здесь

gl.End();

Константа

///

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

/// Вершины 3n - 2, 3n-1 и 3n определяют треугольник n.

/// Изображается N/3 треугольников, где N - полное число вершин.

///

public const int TRIANGLES = 0x0004;

Рисунок, иллюстрирующий порядок построения отдельных треугольников по заданным вершинам в примитиве TRIANGLES

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


Метод FrontFace


Фронтальность грани определяется правилом обхода сторон, которое, в свою очередь, задается последовательностью, в которой перечисляются вершины многоугольника и, в частности, треугольника. Правило обхода определяется в общем случае знаком выражения xwiywi+1 – xwi+1ywi, где по i производится суммирование от 0 до n-1 для n вершин, причем вершина номер n совпадает с вершиной 0, а xw, yw – оконные координаты вершин. Если знак положительный, то по умолчанию (так называемый обход против часовой стрелки – ориентация counter- clockwise, или CCW) грань является фронтальной, если отрицательный – то это задняя грань. Метод FrontFace может менять это соответствие на обратное

///

/// Устанавливает правило определения фронтальной грани многоугольника.

///

///

/// Правило обхода вершин, определяющее фронтальную грань.

/// CW либо CCW (по умолчанию).

///

[DllImport("OPENGL32.DLL", EntryPoint = "glFrontFace")]

public static extern void FrontFace(int mode);

Аргументами этого метода являются символьные постоянные

// Аргументы метода FrontFace - CW (clockwise - по часовой стрелке) и

// CCW (counter-clockwise - против часовой стрелки).

public const int CW = 0x0900;

public const int CCW = 0x0901;

Текущее значение параметра метода FrontFace возвращает метод Get в следующей форме

(int)gl.Get(gl.FRONT_FACE, 1)[0]

Здесь используется символьная константа

///

/// Аргумент Get требует возврата значения 1 параметра -

/// символьной постоянной CCW или CW, параметра метода FrontFace.

///

public const int FRONT_FACE = 0x0B46;

Для сохранения в стеке текущего правила обхода следует использовать метод PushAttrib с параметром POLYGON_BIT, уже встречавшемся выше. Этот же параметр используется для сохранения в стеке других атрибутов многоугольника, описанных в настоящем разделе, за исключением маски заполнения граней (см. ниже).

Любую из граней – заднюю или переднюю можно маскировать от воспроизведения в буфере фрейма. Для этого используется метод CullFace.

Метод CullFace


///

/// Маскирует изображение стороны многоугольника.

///

///

/// Сторона многоугольника FRONT или BACK (по умолчанию).

///

[DllImport("OPENGL32.DLL", EntryPoint = "glCullFace")]

public static extern void CullFace(int mode);

Константы, используемые в качестве параметра метода CullFace, имеют вид

// Константы, используемые для определения типа грани.

public const int FRONT = 0x0404;

public const int BACK = 0x0405;

Текущее значение этого параметра метода CullFace определяется методом Get в следующей форме

(int)gl.Get(gl.CULL_FACE_MODE, 1)[0]

Здесь параметром является константа

///

/// Аргумент Get требует возврата значения 1 параметра -

/// символьной постоянной BACK или FRONT, параметра метода CullFace.

///

public const int CULL_FACE_MODE = 0x0B45;

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

Для активации метода CullFace требуется вызов метода Enable с параметром CULL_FACE

///

/// Параметр активации метода CullFace. Используется в методах Enable, Disable, IsEnabled, Get

///

public const int CULL_FACE = 0x0B44;

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

Определить текущее состояние активности метода CullFace можно методом IsEnabled или Get с тем же параметром CULL_FACE.


Метод PolygonMode


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

///

/// Устанавливает режим отображения многоугольников.

///

///

/// Определяет, какая из граней многоугольника отображается в заданном режиме

/// FRONT, BACK или FRONT_AND_BACK.

///

///

/// Определяет режим отображения многоугольника POINT, LINE или FILL.

///

[DllImport("OPENGL32.DLL", EntryPoint = "glPolygonMode")]

public static extern void PolygonMode(int face, int mode);

Константы, являющиеся аргументами метода PolygonMode, имеют вид (константы типа грани FRONT и BACK описаны выше)

// Тип грани (по умолчанию это тип, который установлен)

public const int FRONT_AND_BACK = 0x0408;

// Константы режима воспроизведения

///

/// Отображаются только вершины выбранной грани многоугольника

/// отдельными точками с их атрибутами (размером и сглаживанием).

///

public const int POINT = 0x1B00;

///

/// Отображаются только контуры многоугольника отрезками прямых линий

/// с их атрибутами (толщина, сглаживание и прерывистость).

///

public const int LINE = 0x1B01;

///

/// Грань многоугольника заполняется сплошным образом (по умолчанию) с учетом маски,

/// регулируемой методом PolygonStipple.

///

public const int FILL = 0x1B02;

Текущие значения параметров метода PolygonMode определяются методом Get в следующей форме

(int)gl.Get(gl.POLYGON_MODE, 2)[i]

Здесь в элементе i = 0 возвращается режим заполнения фронтальной грани многоугольника, а в элементе i=1 – задней грани. Константа POLYGON_MODE имеет вид

///

/// Аргумент Get требует возврата значений двух параметров -

/// символьной константы режима заполнения фронтальной и задней граней многоугольника.

///

public const int POLYGON_MODE = 0x0B40;

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

Метод PolygonStipple


///

/// Определяет шаблон наполнителя поверхности многоугольника

///

///

/// Массив байтов, задающий маску шаблона, размером 128 байт.

/// По умолчанию все биты каждого байта заполнены единицами – сплошное заполнение.

///

[DllImport("OPENGL32.DLL", EntryPoint = "glPolygonStipple")]

public static extern void PolygonStipple(byte[] mask);

Текущее значение маски определяется методом GetPolygonStipple

///

/// Возвращает текущее значение маски заполнения при отображении граней многоугольника.

///

///

/// Массив маски объемом 128. Содержит квадрат 32x32 бита с маской заполнения.

///

[DllImport("OPENGL32.DLL", EntryPoint = "glGetPolygonStipple")]

public static extern void GetPolygonStipple(byte[] mask);

Для сохранения в стеке состояние маски заполнения грани многочлена следует использовать метод PushAttrib с параметром

///

/// Маска битов образа заполнения многоугольника.

///

public const int POLYGON_STIPPLE_BIT = 0x00000010;

Для активации метода PolygonStipple следует использовать метод Enable с параметром

///

/// Параметр активации шаблона наполнителя многоугольника.

/// Аргумент методов Enable, Disable, IsEnabled и Get.

///

public const int POLYGON_STIPPLE = 0x0B42;

Тот же аргумент используют методы IsEnabled и Get для определения текущего состояния активации PolygonStipple.


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

  1. Какая постоянная при вызове Begin определяет примитив изображения отдельных треугольников?

  2. Как определяется фронтальная грань многоугольника по умолчанию?

  3. Какой метод устанавливает правило фронтальной грани?

  4. Как возвратить текущее значение фронтальной грани?

  5. Какой параметр метода PushAttrib сохраняет значение фронтальной грани в стеке?

  6. Как активировать маскировку грани многоугольника?

  7. Изображение какой грани маскируется по умолчанию?

  8. Какой метод устанавливает маскируемую грань?

  9. Как определить текущее значение маскируемой грани?

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

  11. Какой метод устанавливает режимы воспроизведения граней?

  12. Как определить текущий режим воспроизведения граней многоугольника?

  13. Как активировать использование шаблона наполнителя грани?

  14. Какой метод устанавливает маску шаблона наполнителя грани многоугольника?

  15. Что представляет собой маска шаблона наполнителя грани многоугольника?

  16. Какой метод возвращает текущую маску наполнителя граней?

  17. Какое значение по умолчанию имеет маска наполнителей граней многоугольника?

  18. Какой параметр метода PushAttrib сохраняет в стеке маску наполнителя грани многоугольника?

2.1 Примитивы TRIANGLE STRIP и TRIANGLE FAN


Примитивы, управляемые параметрами TRIANGLE_STRIP и TRIANGLE_FAN при вызове метода Begin, строят связанные изображения треугольников. Ознакомьтесь внимательно с комментариями к определению этих постоянных

///

/// Изображает связанную группу треугольников.

/// Один треугольник определен для каждой вершины после первых двух вершин.

/// Для нечетных n, вершины n, n+1 и n+2 определяют треугольник n.

/// Для четных n, вершины n+1, n и n+2 определяют треугольник n.

/// Изображается N - 2 треугольника, где N - полное число вершин.

///

public const int TRIANGLE_STRIP = 0x0005;

///

/// Изображает связанную группу треугольников.

/// Один треугольник определен для каждой вершины после первых двух вершин.

/// Вершины 1, n+1 и n+2 определяют треугольник n.

/// Изображается N - 2 треугольника, где N - полное число вершин.

///

public const int TRIANGLE_FAN = 0x0006;

Эти константы должны являться аргументами метода Begin при построении примитивов TRIANGLE_STRIP и TRIANGLE_FAN.

Рисунки, иллюстрирующие порядок перечисления вершин при построении треугольников с помощью примитивов



TRIANGLE_STRIP TRIANGLE_FAN

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

Чем отличаются примитивы TRIANGLE_STRIP и TRIANGLE_FAN?


Проектные задания

  1. Напишите список команд, строящих треугольную пирамиду с основанием и без него.

  2. Используя примитивы "TRIANGLE_FAN", создайте метод createDiskList построения дисплейного списка диска единичного радиуса в плоскости XY и произвольным числом slices (параметр метода) треугольников.

  3. Используя примитив "TRIANGLE_FAN", создайте метод createConeList с построением дисплейного списка из slices (параметр метода) треугольников кругового конуса единичной высоты и радиуса, с вершиной в начале координат и осью, направленной в положительном направлении оси z.

2.2 Дисплейные списки сферы и тора


В этом разделе даются два примера использования примитивов TRIANGLE_STRIP и TRIANGLE_FAN. В одном строится дисплейный список сферы, в другом - тора.

Сфера


Следующий метод можно добавить в класс listMaker библиотеки GL. Обратите внимание на то, как используются примитивы TRIANGLE_STRIP и TRIANGLE_FAN при создании списка команд, изображающих сферу.

///

/// В режиме COMPILE создает команды дисплейного списка, если список не создан;

/// в режиме COMPILE_AND_EXECUTE создает и, если список создан,

/// вызывает команды дисплейного списка

/// сферы единичного радиуса с осью вдоль оси y.

/// Список строится из примитивов TRIANGLE_FAN и TRIANGLE_STRIP.

///

///

/// Режим создания списка COMPILE или COMPILE_AND_EXECUTE

///

///

/// Число сегментов вдоль оси (по долготе).

///

///

/// Число сегментов, перпендикулярных оси (по широте).

///

///

/// Альфа-компонента цвета

///

///

/// Номер возвращаемого дисплейного списка.

///

public static void Sphere(int mode, int slices, int stacks, float alpha, ref uint sphereList)

{

// Если список создан, то он вызывается



if (0 != sphereList)

{

gl.CallList(sphereList);



return;

}

// Текущий цвет (рабочая переменная)



float curColor;

// Тригонометрические функции углов по широте и долготе (рабочие переменные)

float[] sinteta = new float[stacks],

costeta = new float[stacks],

sinfi = new float[slices + 1],

cosfi = new float[slices + 1];

// Инициализация тригонометрических функций

for (int slice = 0; slice <= slices; slice++)

{

sinfi[slice] = (float)Math.Sin(2.0 * Math.PI * slice / slices);



cosfi[slice] = (float)Math.Cos(2.0 * Math.PI * slice / slices);

}

for (int stack = 0; stack < stacks; stack++)



{

sinteta[stack] = (float)Math.Sin(Math.PI * stack / stacks);

costeta[stack] = (float)Math.Cos(Math.PI * stack / stacks);

}

// Генерируется свободный номер для списка сферы



sphereList = gl.GenLists(1);

// Создается и выполняется дисплейный список

gl.NewList(sphereList, mode);

gl.PushAttrib(gl.CURRENT_BIT);

// Северный полюс

gl.Begin(gl.TRIANGLE_FAN);

// Цвет вблизи полюса - синий

gl.Color(0, 0, 1, alpha);

gl.Vertex(0, 1, 0);

for (int slice = 0; slice <= slices; slice++)

{

gl.Vertex(sinteta[1] * sinfi[slice], costeta[1], sinteta[1] * cosfi[slice]);



}

gl.End();

// средняя часть сферы

for (int stack = 1; stack < stacks - 1; stack++)

{

// Алгоритм изменения цвета с широтой: от синего до красного и назад - к синему



if (stack < stacks / 4)

gl.Color(0, curColor = 4.0f * stack / stacks, 1.0f - curColor, alpha);

else

if (stack < stacks / 2)



gl.Color(curColor = 4.0f * (stack - stacks / 4) / stacks,

1.0f - curColor, 0, alpha);

else

if (stack < 3 * stacks / 4)



gl.Color(curColor = 1 - 4.0f * (stack - stacks / 2) / stacks,

1.0f - curColor, 0, alpha);

else

gl.Color(0, curColor = 1 - 4.0f * (stack - 3 * stacks / 4) / stacks,



1.0f - curColor, alpha);

// Геометрия средней части

gl.Begin(gl.TRIANGLE_STRIP);

for (int slice = 0; slice <= slices; slice++)

{

gl.Vertex(sinteta[stack] * sinfi[slice], costeta[stack],



sinteta[stack] * cosfi[slice]);

gl.Vertex(sinteta[stack + 1] * sinfi[slice], costeta[stack + 1],

sinteta[stack + 1] * cosfi[slice]);

}

gl.End();



}
// Южный полюс

gl.Begin(gl.TRIANGLE_FAN);

gl.Vertex(0, -1, 0);

for (int slice = slices; slice >= 0; slice--)

{

gl.Vertex(sinteta[stacks - 1] * sinfi[slice], costeta[stacks - 1],



sinteta[stacks - 1] * cosfi[slice]);

}

gl.End();



gl.PopAttrib();

gl.EndList();

}

Тор


Можно добавить в класс listMaker метод, создающий дисплейный список тора.

///

/// Хранит минимально допустимый малый радиус тора

///

public const float torusMinSmallRadius = .01f;

///

/// В режиме COMPILE создает команды дисплейного списка, если список не создан;

/// в режиме COMPILE_AND_EXECUTE создает и, если список создан,

/// вызывает команды дисплейного списка тора единичного внешнего радиуса

/// и произвольного радиуса сечения из примитивов TRIANGLE_STRIP.

///

///

/// Режим создания списка COMPILE или COMPILE_AND_EXECUTE

///

///

/// Число сегментов на большой окружности тора.

///

///

/// Число сегментов на малой окружности тора.

///

///

/// Малый радиус тора. Должен быть в интервале (0; 0.5)

///

///

/// Альфа-компонента цвета

///

///

/// Номер возвращаемого дисплейного списка.

///

public static void Torus(int mode, int bigSlices, int smallSlices, float smallRadius,

float alpha, ref uint torusList)

{

if (smallRadius <= torusMinSmallRadius || smallRadius >= .5f)



throw new Exception(

String.Format("Малый радиус тора должен лежать в интервале ({0}0;0.5).", torusMinSmallRadius));

// Если список создан, то он вызывается

if (torusList != 0)

{

gl.CallList(torusList);



return;

}

// Генерируется свободный номер для списка тора



torusList = gl.GenLists(1);

// Текущий цвет (рабочая переменная)

float curColor, curRad, nextRad;

// Тригонометрические функции углов по широте и долготе (рабочие переменные)

float[] sinSmallFi = new float[smallSlices + 1],

cosSmallFi = new float[smallSlices + 1],

sinBigFi = new float[bigSlices + 1],

cosBigFi = new float[bigSlices + 1];

// Инициализация тригонометрических функций

for (int smallSlice = 0; smallSlice <= smallSlices; smallSlice++)

{

sinSmallFi[smallSlice] = (float)Math.Sin(2.0 * Math.PI * smallSlice / smallSlices);



cosSmallFi[smallSlice] = (float)Math.Cos(2.0 * Math.PI * smallSlice / smallSlices);

}

for (int bigSlice = 0; bigSlice <= bigSlices; bigSlice++)



{

sinBigFi[bigSlice] = (float)Math.Sin(2.0 * Math.PI * bigSlice / bigSlices);

cosBigFi[bigSlice] = (float)Math.Cos(2.0 * Math.PI * bigSlice / bigSlices);

}

// Создается и выполняется дисплейный список



gl.NewList(torusList, mode);

gl.PushAttrib(gl.CURRENT_BIT);

for (int smallSlice = 0; smallSlice < smallSlices; smallSlice++)

{

curRad = (1 - smallRadius * (1 - cosSmallFi[smallSlice]));



nextRad = (1 - smallRadius * (1 - cosSmallFi[smallSlice + 1]));

if (smallSlice < smallSlices / 4)

gl.Color(curColor = 1.0f - 4.0f * smallSlice / smallSlices, 1.0f - curColor, 0, alpha);

else


if (smallSlice < smallSlices / 2)

gl.Color(0, curColor = 1.0f - 4.0f * (smallSlice - smallSlices / 4) / smallSlices,

1.0f - curColor, alpha);

else


if (smallSlice < 3 * smallSlices / 4)

gl.Color(0, curColor = 4.0f * (smallSlice - smallSlices / 2) / smallSlices,

1.0f - curColor, alpha);

else


gl.Color(curColor = 4.0f * (smallSlice - 3 * smallSlices / 4) / smallSlices,

1.0f - curColor, 0, alpha);

gl.Begin(gl.TRIANGLE_STRIP);

for (int bigSlice = bigSlices; bigSlice >= 0; bigSlice--)

{

gl.Vertex(curRad * cosBigFi[bigSlice], curRad * sinBigFi[bigSlice],



smallRadius * sinSmallFi[smallSlice]);

gl.Vertex(nextRad * cosBigFi[bigSlice], nextRad * sinBigFi[bigSlice],

smallRadius * sinSmallFi[smallSlice + 1]);

}

gl.End();



}

gl.PopAttrib();

gl.EndList();

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


Проектное задание

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





Достарыңызбен бөлісу:
1   ...   7   8   9   10   11   12   13   14   15




©dereksiz.org 2024
әкімшілігінің қараңыз

    Басты бет