«v»
отсутствует для скалярных форм. Пример:
glVertex2f(x,y)
Вектор
B
– byte
ub – unsigned byte
s – short
us –
unsigned short
I – int
ui – unsigned int
f –
float
d – double
Тип
данных
2
– (x, y)
3 – (x, y, z)
4 – (x, y, z, w)
Число
компонент
glVertex3fv
(
v )
Команды OpenGl
1. DirectX API
Microsoft's DirectX API состоит из следующих групп:
DirectDraw - прямой доступ к видеопамяти
DirectSound - прямой доступ к звуковой карте
DirectPlay - прямой доступ к сетевым возможностям для обеспечения multiplayer mode
DirectInput - поддержка игровых устройств ввода
Все это предназначено для того, чтобы дать возможность программисту получить прямой доступ к различному (в основном игровому) железу. В данной статье дается пояснения к использованию интерфейса DirectDraw.
2. Что такое DirectDraw
DirectDraw это обычный менеджер видеопамяти. Его основное назначение предоставить программисту прямой доступ к видеопамяти. Осуществлять такие операции, как копирование видео память -> видеопамять и т.п.. При этом напрямую могут использоваться возможности видеоконтроллера и освобождать от этих операций центральный процессор. Кроме того, DirectDraw напрямую использует и другие возможности Вашей видеокарты, как то спрайты, z - буферизацию и т.п.. Начиная с 5 версии DirectX дополнительно используются (во всяком случае декларируется использование) возможностей ММX - что позволяет за один цикл обрабатывать и передавать 64 бита видеоинформации.
Основной идеей DirectDraw является копирование видеопамяти в видеопамять, хотя также может поддерживаться и режим память->видеопамять. Что, конечно, является необходимым в мире видео карт с 1 метром видеопамяти.
3. Поверхности DirectDraw - вид доступа к видеопамяти
Вашей основной задачей, как программиста, поместить в видеопамять столько изображений, необходимых для выполнения Вашей программы, как только возможно. Благо при использовании DirectDraw вся видеопамять доступна для Вас. Вы можете использовать ее для запоминания различных изображений (bitmap) , спрайтов и тп. Все эти видео элементы в терминологии DirectDraw называются поверхностями (surfaces). Основная последовательность при загрузке изображений следующая - Вы создаете поверхность(просто область видеопамяти (или системной памяти - если ресурсы видеопамяти исчерпаны)) , и загружаете в нее необходимое изображение.
DirectDraw обеспечен функциями определения доступной видео памяти - таким образом, Вы можете оптимизировать ее использование для конкретной видео карты. Минимальным для нормального использования DirectDraw представляется использование 2 M видео карт, хотя это субъективное мнение
Пример инициализации DirectDraw
LPDIRECTDRAW lpDD; // указатель на объект DirectDraw
// Функция инициализации объекта DirectDraw
// 1) Создаем Direct Draw Object
// 2) Устанавливаем вид доступа
// 3) Устанавливаем видео режим дисплея
BOOL DirectDrawInit(HWND hwnd)
{
HRESULT ddrval;
// Создание (получение) указателя на основной объект DirectDraw.
ddrval = DirectDrawCreate( NULL, &lpDD, NULL );
if( ddrval != DD_OK )
{
goto lError;
}
// DDSCL_EXCLUSIVE - полный доступ к видеопамяти и управлению видеорежимами
// требует флаг DDSCL_FULLSCREEN , т.е. Вы работаете со всем экраном сразу
// DDSCL_NORMAL - для обеспечения оконного доступа.
ddrval = lpDD->SetCooperativeLevel( hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN );
if( ddrval != DD_OK )
{
lpDD->Release();
//lError: здесь можно вставить код для оповещения пользователя о невозможности
return FALSE;
}
// Установим видео режим 800x600 256 цветов
// можно изменять, только если установлен флаг DDSCL_EXCLUSIVE
ddrval = lpDD->SetDisplayMode( 800, 600, 8);
if( ddrval != DD_OK )
{
lpDD->Release();
goto lError;
}
return TRUE;
}
Таким образом мы проинициализировали DirectDraw и изменили видео
Метод двоичного разбиения пространства
Существует другой, крайне элегантный способ упорядочивания граней.
Рассмотрим некоторую плоскость в объективном пространстве. Она разбивает множество всех граней на два непересекающихся множества (кластера), в зависимости от того, в каком полупространстве относительно плоскости эти грани лежат (будем считать, что плоскость не пересекает ни одну из этих граней).
При этом очевидно, что ни одна из граней, лежащих в полупространстве, не содержащем наблюдателя, не может закрывать собой ни одну из граней, лежащих в том же полупространстве, что и наблюдатель. Тем самым сначала необходимо вывести грани из дальнего кластера, а затем уже и из ближнего.
Примени
подобную технику для упорядочения
граней внутри каждого кластера. Для
этого построим разбиение граней каждого
кластера на два множества очередной
плоскостью; а затем для вновь полученных
граней повторим процесс разбиения, и
будем поступать так, до тех пор, пока в
каждом получившемся кластере останется
не более одной грани (рис. 9).
Обычно в качестве разбивающей плоскости рассматривается плоскость, проходящая через одну из граней (на самом деле при этом множество всех граней разбивается на 4 класса — лежащих на плоскости, пересекающих ее, лежащих в положительном полупространстве и лежащие в отрицательном полупространстве относительно этой плоскости). Все грани, пересекаемые плоскостью, разобъем вдоль этой плоскости.
В результате мы приходим к дереву разбиения пространства (Binary Space Partitioning), узлами которого являются грани. Каждый узел такого дерева можно представить в виде следующей структуры:
struct BSPNode
{
Facet *facet; // corresponding facet
Vector n; // normal to facet (plane)
Double d; // plane parameter
BSPNode *Left; // left subtree
BSPNode *Right; // right subtree
}
При этом Left указывает на вершину поддерева, содержащуюся в положительном полупространстве (p, n)>d, а Right — на поддерево, содержащееся в отрицательном полупространстве (p, n)<d.
Процесс построения дерева заключается в выборе грани, проведении через нее плоскости и разбиении множества всех граней. В этом процессе присутствует определенный произвол в выборе очередной грани. Существует два основных критерия для выбора:
получить как можно более сбалансированное дерево;
минимизировать количество разбиений.
К сожалению, эти критерии, как правило, являются взаимоисключающими, поэтому выбирается некоторый компромиссный вариант.
После того как это дерево построено, осуществляется построения изображения в зависимости от используемого проектирования. Ниже приводится процедура построения изображения для центрального проектирования с центром в точке C.
void DrawBSPTree(BSPNode *Tree)
{
if((Tree->n & c) > Tree -> d)
{
if (Tree -> Right != NULL) DrawBSPTree(Tree -> Right);
DrawFacet(Tree -> facet);
if (Tree -> Left != NULL) DrawBSPTree(Tree -> Left);
}
else
{
if (Tree ->Left != NULL) DrawBSPTree(Tree -> Left);
DrawFacet(Tree -> facet);
if (Tree -> Right != NULL) DrawBSPTree(Tree -> Right);
}
}
Одним из основных преимуществ этого метода является его полная независимость от положения центра проектирования, что делает его крайне удобным для построения серий изображений одной и той же сцены из разных точек наблюдения.
Удаление невидимых граней. Метод z-буфера
Одним из самых простых алгоритмов удаления невидимых граней и поверхностей является метод z-буфера (буфера глубины). В силу крайней простоты этого метода часто встречаются его аппаратные реализации.
Сопоставим каждому пикселу (x, y) картинной плоскости, кроме цвета, хранящегося в видеопамяти, его расстояние до картинной плоскости вдоль направления проектирования z(x, y) (его глубину).
Изначально массив глубин инициализируется +∞.
Для вывода на картинную плоскость произвольной грани она переводится в свое растровое представление на картинной плоскости и для каждого пиксела этой грани находится его глубина. В случае, если эта глубина меньше значения глубины, хранящегося в z-буфере, пиксел рисуется и его глубина заносится в z-буфер.
Замечание
Данный метод работает исключительно в пространстве картинной плоскости и не требует никакой предварительной обработки данных. Для вычисления глубины соседних пикселов при растровом разложении грани может использоваться вариант целочисленного алгоритма Брезенхейма.
Алгоритмы упорядочения
Метод, использованный ранее для построения графика функции двух переменных и заключающийся в последовательном выводе на экран всех граней в определенном порядке, может быть использован и для расчета более сложных сцен.
Подход
заключается в таком упорядочении граней,
чтобы при их выводе в этом порядке
получалось корректное изображение. Для
этого необходимо, чтобы более дальние
грани выводились раньше, чем более
близкие.
Существуют различные методы построения такого упорядочения, однако часто встречаются такие случаи, когда заданные грани нельзя упорядочить (рис. 7). В подобных случаях необходимо произвести разбиение одной или нескольких граней, чтобы получившееся после разбиения множество граней можно было упорядочить.
Метод сортировки по глубине
Наиболее простым подходом к упорядочиванию граней является их сортировка по минимальному расстоянию до картинной плоскости (вдоль направления проектирования) с последующим выводом их в порядке приближения.
Этот
метод великолепно работает для ряда
сцен, включая, например, построение
изображения нескольких непересекающихся
достаточно простых тел.
Однако возможны случаи, когда просто сортировка по расстоянию до картинной плоскости не обеспечивает правильного упорядочения граней (рис. 8); поэтому желательно после такой сортировки проверить порядок, в котором грани будут выводиться.
Предлагается следующий алгоритм этой проверки. Для простоты будем считать, что рассматривается параллельное проектирование вдоль оси Oz.
Перед выводом грани P следует убедиться, что никакая другая грань Q, проекция которой на ось Oz пересекается с проекцией грани P, не может закрываться гранью P. И если это условие выполнено, то грань P должна быть выведена раньше. Предлагаются следующие 5 тестов в порядке возрастания сложности проверки:
Пересекаются ли проекции этих граней на ось Ox?
Пересекаются ли их проекции на ось Oy?
Находится ли грань P по другую сторону от плоскости, проходящей через грань Q, чем начало координат (наблюдатель)?
Находится ли грань Q по ту же сторону от плоскости, проходящей через грань P, что и начало координат (наблюдатель)?
Пересекаются ли проекции этих граней на картинную плоскость?
Если хотя бы на один из этих вопросов получен отрицательный ответ, то считаем что эти две грани — P и Q упорядочены верно, и сравнением P со следующей гранью. В противном случае считаем, что эти грани необходимо поменять местами, для чего проверяются следующие тесты:
3’. Находится ли грань Q по другую сторону от плоскости, проходящей через грань P, чем начало координат?
4’. Находится ли грань P по ту же сторону от плоскости, проходящей через грань Q, что и начало координат?
В случае если ни один из этих тестов не позволяет с уверенностью решить, какую из этих двух граней нужно выводить раньше, то одна из них разбивается плоскостью, проходящей через другую грань. В этом случае вопрос об упорядочении оставшейся грани и частей разбитой грани легко решается.
Отсечение не лицевых граней
Рассмотрим многогранник, для каждой грани которого задан единичный вектор внешней нормали (рис. 3). Несложно заметить, что если вектор нормали грани n составляет с вектором l, задающим направление проектирование, тупой угол, то эта грань заведомо не может быть видна. Такие грани называются не лицевыми. В случае, когда соответствующий угол является острым, грань называется лицевой.
В случае параллельного проектирования условия на угол можно записать в виде
(n, l) ≤ 0,
п
оскольку
направление проектирования l
от грани не зависит.
При центральном проектировании с центром в точке C вектор проектирования для точки P будет равен
l = C – P.
Для определения того, является заданная грань лицевой или нет, достаточно взять произвольную точку P этой грани и проверить выполнение условия
(n, l) ≤ 0.
Знак этого скалярного произведения не зависит от выбора точки грани, а определяется тем, в каком полупространстве относительно плоскости, содержащей данную грань, лежит центр проектирования.
В случае, когда сцена представляет собой один выпуклый многогранник, удаление не лицевых граней полностью решает задачу удаления невидимых граней.
В общем случае предложенный подход хотя и не решает задачу полностью, но позволяет примерно вдвое сократить количество рассматриваемых граней.
