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



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

Модуль 4. Многоугольники

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



  • Примитив многоугольник. Его изображение может существенно зависеть от порядка перечисления вершин.

  • Смешивание цветов. Прозрачность.

  • Отсечение.

  • Работа буфера трафарета.

  • Эффекты буфера аккумулятора.

  • Перспективная проекция.

  • Туман

  • Отсечение изображения в оконных координатах.



4.0 Примитив POLYGON


Для изображения примитива POLYGON параметром метода Begin должна быть символьная константа

///

/// Изображает один выпуклый многоугольник. Вершины от 1 до N

/// определяют этот многоугольник.

///

public const int POLYGON = 0x0009;

В качестве примеров можно построить дисплейный список кардиоиды. Вот так может выглядеть код метода из класса listMaker

///

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

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

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

///

///

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

///

///

/// Число вершин многоугольника

///

///

/// Возвращаемый номер списка

///

public static void Cardioid(int mode, int vertices, ref uint cardioidList)

{

double fi;



float cosfi, ro;

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

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

if (cardioidList != 0)

{

gl.CallList(cardioidList);



return;

}

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



cardioidList = gl.GenLists(1);

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

gl.NewList(cardioidList, mode);

gl.Begin(gl.POLYGON);

for (int i = 0; i < vertices; i++)

{

fi = 2.0 * Math.PI * i / vertices;



cosfi = (float)Math.Cos(fi);

ro = 1 + cosfi;

gl.Vertex(.75f * ro * (float)Math.Sin(fi), .75f * (-ro * cosfi + 1));

}

gl.End();



gl.EndList();

}

А это пример дисплейного списка пятиконечной звезды – метод Star класса listMaker.



///

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

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

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

///

///

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

///

///

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

///

public static void Star(int mode, ref uint starList)

{

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



if (starList != 0)

{

gl.CallList(starList);



return;

}

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



starList = gl.GenLists(1);

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

gl.NewList(starList, mode);

gl.Begin(gl.POLYGON);

gl.Vertex(-.5f * (float)Math.Sin(1.8 * Math.PI), .5f * (float)Math.Cos(1.8 * Math.PI));

gl.Vertex(0, 1);

gl.Vertex(-.5f * (float)Math.Sin(.2 * Math.PI), .5f * (float)Math.Cos(.2 * Math.PI));

gl.Vertex(-(float)Math.Sin(.4 * Math.PI), (float)Math.Cos(.4 * Math.PI));

gl.Vertex(-.5f * (float)Math.Sin(.6 * Math.PI), .5f * (float)Math.Cos(.6 * Math.PI));

gl.Vertex(-(float)Math.Sin(.8 * Math.PI), (float)Math.Cos(.8 * Math.PI));

gl.Vertex(0, -.5f);

gl.Vertex(-(float)Math.Sin(1.2 * Math.PI), (float)Math.Cos(1.2 * Math.PI));

gl.Vertex(-.5f * (float)Math.Sin(1.4 * Math.PI), .5f * (float)Math.Cos(1.4 * Math.PI));

gl.Vertex(-(float)Math.Sin(1.6 * Math.PI), (float)Math.Cos(1.6 * Math.PI));

gl.End();

gl.EndList();

}

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


Тест рубежного контроля

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

  2. Почему важен порядок перечисления вершин при формировании многоугольника?

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


4.1 Смешивание цветов. Прозрачность


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

Формула имеет простой вид C = fsCs + fdCd. В ней Cs, Cd – 4-компонентные цвета источника и приемника (например, красный цвет (1,0,0,1) с альфа-компонентой α = 1), fs, fd – так же 4-компонентые факторы смешивания источника и приемника, а C – результирующий цвет.

Если смешивание не активировано, результатом является замена цветов фрагмента-приемника цветами фрагмента-источника. Смешивание активируется командой Enable с параметром BLEND. После активации смешивание управляется факторами, которые устанавливаются командой BlendFunc с двумя параметрами фактором-источником и фактором-приемником. По умолчанию цвет источника управляется фактором ONE (fs = (1,1,1,1)) , а цвет приемника – фактором ZERO (fd = (0,0,0,0)). В результате по умолчанию в буфере цвета фрагмент-приемник будет заменен фрагментом-источником, как если бы смешивание не было активировано.

Всего в OpenGL существует 9 типов fs - и 8 типов fd -факторов. Все они перечислены в константах, приведенных ниже.

В частности, для достижения эффекта прозрачности обычно в качестве фактора источника используется значение SOURCE_ALPHA со значение fs = (αs, αs, αs, αs) , где αs - значение альфа-компоненты цвета фрагмента, направляемого в буфер цвета. Фактор приемника принимается равным ONE_MINUS_ SOURCE_ ALPHA со значением fd = (1-αs, 1-αs, 1-αs, 1-αs). В этом случае, к примеру, красная компонента результирующего цвета будет равна Cr = αs Csr + (1 – αs) Cdr, то есть будет представлять собой смесь красных компонент фрагмента-источника Csr и фрагмента-приемника Cdr.

Поместите следующий код в класс gl библиотеки GL.

///

/// Определяет правило смешивания цветов

///

///

/// Фактор источника

///

///

/// Фактор адресата

///

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

public static extern void BlendFunc(int sfactor, int dfactor);

Константы, определяющие факторы смешивания

// только s-факторы

public const int SRC_ALPHA_SATURATE = 0x0308;

public const int DST_COLOR = 0x0306;

public const int ONE_MINUS_DST_COLOR = 0x0307;

// только d-факторы

public const int SRC_COLOR = 0x0300;

public const int ONE_MINUS_SRC_COLOR = 0x0301;

// s и d-факторы

public const int ZERO = 0;

public const int ONE = 1;

public const int SRC_ALPHA = 0x0302;

public const int ONE_MINUS_SRC_ALPHA = 0x0303;

public const int DST_ALPHA = 0x0304;

public const int ONE_MINUS_DST_ALPHA = 0x0305;

Для получения текущих значений s- и d-факторов смешивания следует использовать метод Get в форме

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

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

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

///

/// Аргумент Get требует возврата значения 1 параметра - текущей символьной константы

/// d-фактора смешивания (аргумент BlendFunc)

///

public const int BLEND_DST = 0x0BE0;

///

/// Аргумент Get требует возврата значения 1 параметра - текущей символьной константы

/// s-фактора смешивания (аргумент BlendFunc)

///

public const int BLEND_SRC = 0x0BE1;

Для активации функции смешивания необходимо использовать метод Enable с параметром

///

/// Параметр активации смешивания цветов (функция BlendFunc)

///

public const int BLEND = 0x0BE2;

Для сохранения факторов смешивания и состояния активации смешивания необходимо использовать метод PushAttrib с параметром COLOR_BUFFER_BIT.

Читатель может добавить описанные методы в свое приложение и применить их к дисплейным спискам фигур, рассмотренным выше, на фоне осей координат. При этом альфа-компоненты цветов фигур следует задавать меньшими единицы, альфа-компоненту цветов осей оставлять равной единице и вызывать метод BlendFunc с s-фактором, равным SRC_ALPHA, а d-фактором, равным ONE_MINUS_SRC_ALPHA. Здесь важен порядок выполнения списков – вначале необходимо вызвать список осей, чтобы в буфере цвета сформировать пиксели адресата, а затем – список фигуры.

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


Тест рубежного контроля

  1. В чем принцип смешивания цветов?

  2. Чем отличается пиксель-источник от пикселя-адресата?

  3. Что определяют константы-факторы источников и адресатов?

  4. Какой метод задает функцию смешивания?

  5. Как определить текущие значения факторов смешивания цветов?

  6. Как сохранить параметры смешивания в стеке атрибутов?

  7. Как активировать смешивание цветов?

  8. Какие факторы смешивания обеспечивают эффект прозрачности?

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



4.2 Отсечение. Метод ClipPlane


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

///

/// Задает плоскость отсечения.

///

///

/// Номер плоскости, который должен иметь значение CLIP_PLANE0+i

/// (i=0 - первая плоскость и т.д.)

///

///

/// Параметры уравнения плоскости:

/// массив из 4-ех величин a, b, c, d уравнения плоскости ax+by+cz+d = 0 в объектной системе.

///

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

public static extern void ClipPlane(int plane, double[] equation);

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

///

/// Параметр метода ClipPlane, определяющий номер первой плоскости отсечения

///

public const int CLIP_PLANE0 = 0x3000;

Определять максимальное число плоскостей отсечения следует методом Get в форме

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

Здесь константа MAX_CLIP_PLANES имеет значение

///

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

/// максимального числа плоскостей отсечения.

///

public const int MAX_CLIP_PLANES = 0x0D32;

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

///

/// Возвращает текущие уравнения заданной плоскости отсечения

///

///

/// Номер плоскости отсечения в виде символьной константы CLIP_PLANE0+i

///

///

/// Массив коэффициентов уравнения плоскости.

///

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

public static extern void GetClipPlane(int plane, double[] equation);

Для активации метода ClipPlane необходимо вызвать метод Enable с параметром CLIP_PLANE0+i для соответствующей плоскости i. Текущая активность метода ClipPlane для i-ой плоскости определяется методами Get или IsEnable с тем же параметром.

Читатель, самостоятельно готовящий приложение, может внести в него описанные методы и проверить их эффект на отдельных примерах.

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


Тест рубежного контроля

  1. Какой метод позволяет отсекать изображение плоскостями?

  2. Каковы параметры метода отсечения плоскостями?

  3. Как определить максимальное число отсекающих плоскостей, которое допускает система?

  4. Как определить текущие уравнения отсекающих плоскостей?

  5. Какой метод активирует процесс отсечения плоскостью?



4.3 Трафарет. Методы StencilFunc и StencilOp


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

Для того чтобы отличать открытые области от закрытых областей, разным фрагментам объекта-трафарета присваиваются различные числа – индексы, которые сохраняются в буфере трафарета. Пусть, скажем, у открытой области некоторого объекта-трафарета индекс равен 1, а у закрытой нулю. Часть пикселей непрерывного объекта имеет, таким образом, индекс 0 (попадает на закрытую область трафарета), а та часть, которая попадает на открытую область, имеет индекс единица. Если задать условием тестирования равенство индекса трафарета нулю, то в буфер цвета попадет только та часть непрерывного объекта, которая совпадает с закрытой областью объекта-трафарета (негатив). Если требовать равество индекса единице, то воспроизведется область непрерывного объекта, попадающая в открытую область объекта-трафарета (позитив).

Активирует тест трафарета методом Enable с символьной константой

///

/// параметр активации тестирования по трафарету

///

public const int STENCIL_TEST = 0x0B90;

Тот же параметр используется методами IsEnable и Get для определения текущей активности теста трафарета.

Операция сравнения и контрольное значение индекса задается командой StencilFunc, действующей подобно командам AlphaFunc и DepthFunc.

///

/// Устанавливает функцию теста трафарета и индекс трафарета входящего пикселя.

///

///

/// Определяет функцию тестирования. Доступны 8 символьных констант:

/// NEVER, LESS, LEQUAL, GREATER, GEQUAL, EQUAL, NOTEQUAL и ALWAYS.

///

///

/// Определяет индекс входящего пикселя в интервале [0; 2n - 1],

/// где n - число битовых плоскостей в буфере трафарета.

///

///

/// Маска, на которую перед сравнением логически умножаются

/// индекс входящего пикселя refvalue

/// и индекс пикселя, находящегося в этот момент в буфере трафарета dest.

/// Например, если первый параметр GREATER,

/// то формула теста имеет вид refvalue & mask > dest & mask.

///

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

public static extern void StencilFunc(int func, int refvalue, uint mask);

Для определения текущих значений параметров метода StencilFunc следует использовать метод Get со следующими параметрами

///

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

/// символьной константы, определяющей текущий смысл

/// функции тестирования трафарета в методе StencilFunc.

///

public const int STENCIL_FUNC = 0x0B92;

///

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

/// текущего значения индекса пикселя в буфере трафарета, используемого методом

/// StencilFunc.

///

public const int STENCIL_REF = 0x0B97;

///

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

/// текущего значения маски, используемой методом StencilFunc.

///

public const int STENCIL_VALUE_MASK = 0x0B93;

По умолчанию значение func=ALWAYS, refvalue = 0 и mask = 255.


Очистка буфера трафарета проводится методом Clear с параметром в виде символьной константы

///

/// Идентифицирует буфер трафарета в методах Clear и PushAttrib.

///

public const int STENCIL_BUFFER_BIT = 0x00000400;

Та же постоянная используется методом PushAttrib при сохранении состояния буфера трафарета в стеке.

Метод ClearStencil устанавливает индекс, которым заполняется буфер трафарета при очистке

///

/// Устанавливает индекс, которым заполняется буфер трафарета при его очистке.

/// По умолчанию этот индекс равен нулю.

///

///

/// Значение индекса.

///

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

public static extern void ClearStencil(int s);

Для определения значения очищающего индекса следует применить метод Get в форме

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

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

///

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

/// значения числа, которым заполняются битовые плоскости

/// буфера трафарета при его очистке.

///

public const int STENCIL_CLEAR_VALUE = 0x0B91;
Метод StencilOP определяет действия, которые следует совершить после прохождения теста трафарета с индексами трафарета

///

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

/// Параметры StencilOp являются символьными константами.

/// Доступны шесть символьных постоянных KEEP, ZERO, REPLACE, INCR, DECR и INVERT.

///

///

/// Определяет действие, если тест трафарета не проходит.

///

///

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

///

///

/// Определяет действие, если оба теста проходят.

///

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

public static extern void StencilOp(int sFail, int zFail, int szPass);

Символьные константы, являющиеся возможными аргументами метода StencilOp, имеют вид

// Символьные константы - аргументы метода StencilOp,

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

///

/// Оставить текущее значение индекса.

///

public const int KEEP = 0x1E00;

///

/// Заменить индекс значением, указанным методом StencilFunc.

///

public const int REPLACE = 0x1E01;

///

/// Инвертировать биты индекса.

///

public const int INVERT = 0x150A;

///

/// Увеличить значение индекса на единицу, если его значение не максимально.

///

public const int INCR = 0x1E02;

///

/// Уменьшить значение индекса на единицу, если он не равен нулю.

///

public const int DECR = 0x1E03;

Возможна так же использованная ранее константа ZERO для зануления индекса.

По умолчанию все три аргумента метода StencilOp равны KEEP.

Для определения текущих значений параметров метода StencilOp следует использовать метод Get со следующими параметрами

///

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

/// текущей символьной константы, определяющей действие в методе StencilOp,

/// если тест трафарета не проходит.

///

public const int STENCIL_FAIL = 0x0B94;

///

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

/// текущей символьной константы, определяющей действие в методе StencilOp,

/// если тест трафарета проходит, но не проходит тест глубины.

///

public const int STENCIL_PASS_DEPTH_FAIL = 0x0B95;

///

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

/// текущей символьной константы, определяющей действие в методе StencilOp,

/// если тест трафарета и тест глубины проходят оба.

///

public const int STENCIL_PASS_DEPTH_PASS = 0x0B96;

Для примера можно написать метод setStencilImage, который засылает в трафарет пиксели двух типов с индексами 1 и 2. Пиксели с индексами 1 будут содержать изображение кардиоиды, а с пиксели с индексом 2 – надпись контурными символами

///

/// Создает изображение кардиоиды и надпись в буфере трафарета.

///

void setStencilImage()

{

// Комментарий в строке статуса показывает значение индекса,



// очищающего буфер трафарета

// и значения параметров методов StencilOp и StencilFunc по умолчанию

stLabel.Text = "Stencil Clear Value=" + ((int)gl.Get(gl.STENCIL_CLEAR_VALUE, 1)[0]).ToString() +

" Stencil fail=" + ((int)gl.Get(gl.STENCIL_FAIL, 1)[0]).ToString("x") +

" Stencil pass depth fail=" + ((int)gl.Get(gl.STENCIL_PASS_DEPTH_FAIL, 1)[0]).ToString("x") +

" Stencil pass depth path=" + ((int)gl.Get(gl.STENCIL_PASS_DEPTH_PASS, 1)[0]).ToString("x") +

" func=" + ((int)gl.Get(gl.STENCIL_FUNC, 1)[0]).ToString("x") +

" ref=" + ((int)gl.Get(gl.STENCIL_REF, 1)[0]).ToString() +

" mask=" + ((uint)gl.Get(gl.STENCIL_VALUE_MASK, 1)[0]).ToString();

// Очистка буфера трафарета - заполнение значениями, очищающими буфер трафарета.

gl.Clear(gl.STENCIL_BUFFER_BIT);

// Формирование трафарета

// Установка правил сравнения индексов пикселей, входящих в буфер трафарета,

// с индексами пикселей, уже находящихся в буфере.

// В данном случае задается функция ALWAYS, поэтому значение маски не важно,

// значение ref = 1 становится индексом всех пикселей, попавших далее в буфер.

gl.StencilFunc(gl.ALWAYS, 1, 0);

// В этом случае тест проходит всегда (ALWAYS),

// поэтому первый параметр StencilOp произволен.

// Второй и третий параметры установлены в REPLACE, что позволяет пикселям

// вне зависимости от результатов теста глубины, оставаться в буфере трафарета.

gl.StencilOp(gl.KEEP, gl.REPLACE, gl.REPLACE);

gl.Enable(gl.STENCIL_TEST);

gl.PushMatrix();

gl.Scale(.3f, .3f, 1);

// Изображается кардиоида

listMaker.Cardioid(gl.COMPILE_AND_EXECUTE, cardioidVertices, ref cardioidList);

gl.PopMatrix();

gl.ListBase(globalOutlineGlyphsBaseList);

// Здесь индексы трафарета следующих далее пикселей надписи принимают значения 2.

gl.StencilFunc(gl.ALWAYS, 2, 0);

gl.PushMatrix();

gl.Translate(-.13f, .1f, .1f);

gl.Scale(.1f, .1f, .1f);

gl.PushAttrib(gl.POLYGON_BIT);

// Пишется текст контурным шрифтом

gl.CallLists(7, gl.UNSIGNED_BYTE, "Stencil");

gl.PopAttrib();

gl.PopMatrix();

}

Метод setStencilImage следует вызывать один раз до начала изображения целевых объектов.



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

if (!stencilImageReady)

{

// Заполнение буфера трафарета проводится только один раз



setStencilImage();

stencilImageReady = true;

}

// Активация тестирования по трафарету



gl.Enable(gl.STENCIL_TEST);

// Функция формирования изображения:

// пропускать только пиксели с индексом, определенным curRef

gl.StencilFunc(gl.EQUAL, curRef, 255);

// Значения параметров StencilOp по умолчанию – нет изменений в буфере трафарета.

listMaker.Sphere(gl.COMPILE_AND_EXECUTE, sphereSlices,

sphereStacks, 1, ref constSphereList);

Здесь флаг stencilImageReady первоначально равен false, что позволяет использовать метод setStencilImage при первом прогоне, но не при каждом последующем. Поле curRef является переменным и в авторском приложении его значение определяется интерфейсом пользователя. Требование теста, установленные методом StencilFunc, таковы, что



  • Если значение curRef равно 1, то будут изображаться только те пиксели сферы, которые попали внутрь кардиоиды, но не внутрь надписи.

  • Если curRef = 0, то, наоборот, только те пиксели сферы, которые не попали ни внутрь кардиоиды, ни внутрь надписи.

  • Наконец, если curRef = 2, то будут изображаться только пиксели, попавшие внутрь надписи.

В этом коде на стадии получения изображения никаких изменений в буфере трафарета не производится.

Читатель, самостоятельно готовящий приложение, может внести в него описанные методы и проверить их эффект на отдельных примерах.

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


  1. Как очищается буфер трафарета?

  2. Как задается индекс очистки буфера трафарета?

  3. Какое значение имеет индекс очистки буфера трафарета по умолчанию?

  4. Как определяется текущее значение индекса очистки буфера трафарета?

  5. Какой метод определяет действия с индексами пикселей в буфере трафарета в процессе тестирования трафарета?

  6. Какие возможные значения имеют параметры метода StencilOp?

  7. Как определить текущие значения параметров метода StencilOp?

  8. Какие значения имеют параметры метода StencilOp по умолчанию?

  9. Какой метод задает функцию тестирования?

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

  11. Какие значения имеют параметры метода, управляющего функцией тестирования, по умолчанию?

  12. Какой параметр следует использовать в методе PushAttrib для сохранения в стеке состояния параметров всех методов, работающих с буфером трафарета?

  13. Какой метод активирует тестирование трафарета?


Примечание

В OpenGL существует метод StencilMask с параметром mask, позволяющий маскировать запись в буфер трафарета произвольного набора битов. Фактически метод может блокировать отдельные битовые плоскости. По умолчанию все плоскости буфера трафарета открыты для записи.



4.4 Эффекты буфера-аккумулятора. Метод Accum


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

Буфер-аккумулятор служит для обмена информацией с буфером цвета в целом, а не с каждым пикселем в отдельности. Метод, регулирующий работу с буфером-аккумулятором Accum, позволяет загрузить все содержимое буфера цвета, произвести отдельные действия со всеми пикселями сразу, вернуть содержимое назад в буфер цвета.

Для очистки буфера-аккумулятора используется метод Clear с параметром

///

/// Идентифицирует буфер-аккумулятор в методах Clear и PushAttrib.

///

public const int ACCUM_BUFFER_BIT = 0x00000200;

Значение цвета, которым очищается буфер-аккумулятор задается методом

///

/// Задает цвет очистки буфера-аккумулятора

///

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

public static extern void ClearAccum(float red, float green, float blue, float alpha);

Для определения текущего значения цвета очистки буфера-аккумулятора следует использовать метод Get с параметром

///

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

/// цвета очистки буфера-аккумулятора.

///

public const int ACCUM_CLEAR_VALUE = 0x0B80;

Для сохранения текущего значения цвета очистки буфера-аккумулятора в стеке следует использовать метод PushAttrib с параметром ACCUM_BUFFER_BIT.

Основной метод для работы с буфером-аккумулятором

///

/// Оперирует с содержимым буфера аккумулятора.

///

///

/// Символьная константа указывает тип операции.

/// Доступны значения ACCUM, LOAD, ADD, MULT и RETURN.

///

///

/// Величина, участвующая в операциях в буфере аккумулятора.

/// Смысл value зависит от типа операции – константы op.

///

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

public static extern void Accum(int op, float value);

Символьные константы, определяющие тип операции с буфером-аккумулятором, имеют значения

///

/// Цвета всех пикселей из буфера цвета умножаются на второй аргумент value метода Accum

/// и добавляет к содержимому аккумулятора

///

public const int ACCUM = 0x0100;

///

/// Цвета всех пикселей из буфера цвета умножаются на второй аргумент value метода Accum

/// и загружаются в буфер-аккумулятор

///

public const int LOAD = 0x0101;

///

/// Цвета всех пикселей из буфера-аккумулятора умножаются на второй аргумент value метода

/// Accum и загружаются в буфер цвета

///

public const int RETURN = 0x0102;

///

/// Цвета всех пикселей в буфере-аккумуляторе просто умножаются

/// на второй аргумент value метода Accum

///

public const int MULT = 0x0103;

///

/// Цвета всех пикселей в буфере-аккумуляторе складываются

/// со вторым аргументом value метода Accum.

///

public const int ADD = 0x0104;

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

// Цвет фона в буфере цвета устанавливается черным

gl.ClearColor(0, 0, 0, 1);

// Очищается буфер цвета

gl.Clear(gl.COLOR_BUFFER_BIT);

// Инициализируется значение угла трансляции при выключенном таймере

if (!timerEnabled)

angleTranslate = 0;

// Изображается сфера в текущем положении на окружности в плоскости XY

gl.PushMatrix();

gl.LoadIdentity();

gl.Translate(.8f * (float)Math.Cos(angleTranslate * Math.PI / 180),

.8f * (float)Math.Sin(angleTranslate * Math.PI / 180), 0);

gl.Scale(.02f, .02f, .02f);

listMaker.Sphere(gl.COMPILE_AND_EXECUTE, sphereSlices, sphereStacks, 1,

ref constSphereList);

gl.PopMatrix();

// В состоянии движения (при включенном таймере)

if (timerEnabled)

{

// Интенсивность цветов пикселей в буфере-аккумуляторе уменьшается на 5%



gl.Accum(gl.MULT, .95f);

// К содержимому буфера-аккумулятора добавляется содержимое

// буфера цвета без искажения цветов пикселей

gl.Accum(gl.ACCUM, 1);

// Содержимое буфера-аккумулятора переносится без искажений в буфер цвета

gl.Accum(gl.RETURN, 1);

}

else


// При выключенном таймере (перед началом движения)

{

// Очищается буфер аккумулятора



gl.Clear(gl.ACCUM_BUFFER_BIT);

// Буфер цвета копируется в аккумулятор без искажений цветов пикселей

gl.Accum(gl.LOAD, 1);

// Включается таймер

translateEnabled = timerEnabled = true;

}

Читатель, самостоятельно готовящий приложение, может внести в него приведенный код, или написать свой, иллюстрирующий работу метода Accum.



В авторском приложении можно проверить работу описанного кода. Комментарий к коду этой части авторского приложения можно найти по ссылке.
Тест рубежного контроля

  1. Какой метод очищает буфер-аккумулятор?

  2. Какой метод задает цвет очистки буфера-аккумулятора?

  3. Как определить текущий цвет очистки буфера-аккумулятора?

  4. С каким параметром следует обратиться к методу PushAttrib, чтобы сохранить текущее значение цвета очистки буфера-аккумулятора в стеке?

  5. Какие операции может осуществлять метод Accum?

4.5 Перспективная проекция

Методы Frustum и Perspective


До сих пор в изображении использовалось только одно преобразование от координат наблюдения к координатам отсечения – ортографическая проекция. Матрица ортографической проекции определяется методом Ortho, который вызывается в методе InitProjection формы f3D. Однако довольно часто используется другая, перспективная проекция, при которой удаляющийся объект уменьшает свои видимые размеры. В перспективной проекции наблюдатель видит объект, находясь в большом основании усеченного конуса (frustum) как у входа в тоннель, дальний вход в который соответствует малому основанию конуса. Координаты отсечения отсекают видимость наблюдателя стенками тоннеля, и его входами. Перед ближним входом и за дальним входом объект не виден. При перемещении по тоннелю видимые размеры объекта зависят от его удаления от ближнего входа.

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

///

/// Задает матрицу перспективной проекции с параметрами отсекающих плоскостей

/// left, right, bottom, top, -near и -far. Параметры near и far должны быть больше нуля.

/// При этом, far > near.

///

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

public static extern void Frustum(double left, double right, double bottom, double top,

double near, double far);

///

/// Задает матрицу перспективной проекции.

///

///

/// Угол обзора в градусах в направлении оси y (field of view angle).

///

///

/// Отношение ширины порта наблюдения к его высоте.

///

///

/// Положение ближней отсекающей плоскости с минусом.

/// Параметр обязательно положителен.

///

///

/// Положение дальней отсекающей плоскости с минусом.

/// Параметр обязательно положителен и больше near

///

[DllImport("GLU32.DLL", EntryPoint = "gluPerspective")]

public static extern void Perspective(double fovy, double aspect, double near, double far);

Матрица преобразования, отвечающая команде Frustum, имеет вид



Здесь l – left, r – right, t – top, b – bottom, n – near, f – far.


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

float z;


if (!timerEnabled)

angleTranslate = 0;

// Установка режима ввода матриц преобразования от координат наблюдения

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

gl.MatrixMode(gl.PROJECTION);

// Ввод единичной матрицы

gl.LoadIdentity();

// Выбор типа проекционной матрицы

switch (curMatrix)

{

case 0: //ortho



// Ввод матрицы ортогонального преобразования

gl.Ortho(prjLeft, prjRight, prjBottom, prjTop,

-(curNear = prjNear), -(curFar = prjFar));

break;


case 1: // Ввод матрицы перспективной проекции frustum

gl.Frustum(prjLeft, prjRight, prjBottom, prjTop,-curNear, -curFar);

break;

case 2: //Perspective



gl.Perspective(curfovAngle, vpWidth / vpHeight,-curNear, -curFar);

break;


}

// Установка режима ввода матриц преобразования от объектных координат

// к координатам наблюдения

gl.MatrixMode(gl.MODELVIEW);

gl.LoadIdentity();

// Изображается приближающаяся и удаляющаяся по оси z сфера

// Координата z колеблется между значениями near и far

gl.Translate(0, 0, z = .5f * (curNear - curFar) *

(float)Math.Cos(angleTranslate * Math.PI / 180) + .5f * (curNear + curFar));

listMaker.Sphere(gl.COMPILE_AND_EXECUTE, sphereSlices,

sphereStacks, 1, ref constSphereList);

// тип преобразования и z-координата сферы выводятся в строку статуса

stLabel.Text = (curMatrix == 0 ? "ortho " :curMatrix == 1 ? "frustum " :

" perspective ") + z.ToString("f");

translateEnabled = timerEnabled = true;

Здесь поля с префиксом cur могут регулироваться либо с пользовательского интерфейса (как это делается в авторском приложении), либо иным способом.


Тест рубежного контроля

  1. Какие методы обеспечивают преобразование к перспективной проекции?

  2. Объясните смысл параметров методов преобразования к перспективной проекции.

Туман. Метод Fog.


В этом разделе рассматривается эффект изображения объекта, который называется "туманом" (fog). Никакого отношения к перспективной проекции эффект тумана и методы, которые его обеспечивают, не имеет. В то же время, внешний вид объекта в тумане зависит от расстояния до объекта от нуля системы координат наблюдения. Поэтому иллюстрация работы метода Fog приводится в настоящем разделе.

Эффект тумана обеспечивается эффектом смешивания цвета тумана и цвета объекта. По умолчанию альфа-компонента цвета тумана нулевая. Факторы смешивания зависят от режима тумана. Режим определяется методом Fog.

Здесь будут рассматриваться две версии метода Fog со следующими заголовками

///

/// Определяет параметры тумана.

///

///

/// Определяет тип параметра тумана со значением типа float.

/// возможны значения FOG_MODE, FOG_DENSITY, FOG_START и FOG_END.

///

///

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

/// Если pname=FOG_MODE, то значения должны быть из числа LINEAR, EXP и EXP2.

/// По умолчанию EXP.

///

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

public static extern void Fog(int pname, float param);

///

/// Определяет параметры тумана.

///

///

/// Определяет тип параметра тумана со значением типа float[].

/// Возможны значения FOG_MODE, FOG_DENSITY, FOG_START, FOG_END и FOG_COLOR.

///

///

/// Численное значение параметра pname.

/// Если pname=FOG_MODE, то значения должны быть из числа LINEAR, EXP и EXP2.

/// По умолчанию EXP.

///

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

public static extern void Fog(int pname, float[] fogparams);

Символьные константы-параметры метода Fog имеют вид

///

/// Плотность тумана. Используется только в экспоненциальных режимах. По умолчанию 1.

///

public const int FOG_DENSITY = 0x0B62;

///

/// Ближайшее расстояние от наблюдателя, с которого начинается туман.

/// Используется только в режиме линейного изменения. По умолчанию 0.

///

public const int FOG_START = 0x0B63;

///

/// Дальнее расстояние от наблюдателя, где туман обрывается.

/// Используется только в режиме линейного изменения. По умолчанию 1.

///

public const int FOG_END = 0x0B64;

///

/// Тип режима.

///

public const int FOG_MODE = 0x0B65;

///

/// Цвет тумана. Массив из 4-ех значений. По умолчанию 0,0,0,0.

///

public const int FOG_COLOR = 0x0B66;

///

/// Линейный тип тумана. Фактор смешивания f своего цвета с цветом объекта определяется

/// формулой f = (e - c)/(e - s).

/// Здесь c - расстояние от начала координат наблюдения

/// до центра изображаемого фрагмента,

/// s-значение параметра FOG_START (по умолчанию 0),

/// а e-значение параметра FOG_END (по умолчанию 1).

///

public const int LINEAR = 0x2601;

///

/// Экспоненциальный тип тумана. Фактор смешивания f меняется по закону f = Exp(-d*c).

/// Здесь c - расстояние от начала координат наблюдения

/// до центра изображаемого фрагмента, d - значение параметра FOG_DENSITY

///

public const int EXP = 0x0800;

///

/// Экспоненциальный тип тумана. Фактор смешивания f меняется по закону f = Exp(-(d*c)^2).

/// Здесь c - расстояние от начала координат наблюдения до центра изображаемого

/// фрагмента, d - значение параметра FOG_DENSITY

///

public const int EXP2 = 0x0801;

Для определения текущих значений параметров метода Fog следует использовать метод Get с уже перечисленными параметрами в форме

gl.Get(gl.FOG_COLOR, 4)[i], где i = 0,1,2,3 – цвета красный, зеленый, синий и альфа-компонента

gl.Get(gl.FOG_DENSITY, 1)[0]

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

gl.Get(gl.FOG_START, 1)[0]

gl.Get(gl.FOG_END, 1)[0]

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

///

/// Параметр метода PushAttrib

///

public const int FOG_BIT = 0x00000080;

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

///

/// Параметр методов Enable, Get, IsEnabled, Disable.

///

public const int FOG = 0x0B60;

Для определения текущей активности тумана – метод IsEnabled или Get с тем же параметром.

Читатель, самостоятельно готовящий приложение, может использовать в нем приведенные методы по регулировке и активизации тумана.

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


Тест рубежного контроля

  1. Какой метод регулирует эффект тумана?

  2. Опишите параметры метода, регулирующего эффект тумана.

  3. Как определить текущие значения параметров метода, регулирующего эффект тумана?

  4. Какой параметр необходимо использовать в методе PushAttrib, чтобы сохранить в стеке параметры метода Fog?

  5. Какой метод активирует эффект тумана?

4.6 Scissor Test


Существует возможность выделять (вырезать – scissor) прямоугольник изображения в оконных координатах, отсекая от попадания в буфер фрейма той части объекта, которая лежит вне границ прямоугольника. Это похоже на действие ClipPlane. Разница в том, что уравнения ClipPlane задаются в объектных координатах, тогда как в данном случае отсечение происходит в оконных координатах. Управляет этим отсечением метод Scissor с заголовком

///

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

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

/// не попадает в буфер фрейма.

///

///

/// Оконная координата левой границы.

///

///

/// Оконная координата нижней границы.

///

///

/// Ширина в пикселях.

///

///

/// Высота в пикселях.

///

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

public static extern void Scissor(int x, int y, int width, int height);

Для определения текущих значений параметров метода Scissor следует использовать метод Get в следующей форме

(int)gl.Get(gl.SCISSOR_BOX, 4)[i],

где i = 0,1,2,3 – возвращает соответственно x, y, width и height параметры метода Scissor.

По умолчанию x=0, y=0, а ширина и высота определяется размерами окна вывода.

Символьная константа SCISSOR_BOX имеет вид

///

/// Аргумент Get требует возврата значений 4 параметров метода Scissor (x,y,width и height).

///

public const int SCISSOR_BOX = 0x0C10;

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

///

/// Маска атрибутов метода Scissor

///

public const int SCISSOR_BIT = 0x00080000;

Для активации метода Scissor и, собственно, самого теста необходим вызов метода Enable с параметром

///

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

///

public const int SCISSOR_TEST = 0x0C11;

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

Читатель, самостоятельно готовящий приложение, может использовать в нем приведенные методы по регулировке и активизации scissor-теста.

Авторское приложение позволяет наблюдать и регулировать эффект scissor-теста с помощью интерфейсных элементов управления. Комментарий к коду этой части авторского приложения можно найти по ссылке.


Тест рубежного контроля

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

  2. Опишите параметры метода отсечения в оконных кординатах.

  3. Как определить текущие значения параметров метода отсечения?

  4. Как сохранить в стеке текущее состояние и параметры scissor-теста?

  5. Как активировать тест отсечения?


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




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

    Басты бет