Сергей Ваткин - DirectX 8. Начинаем работу с DirectX Graphics Страница 3
Сергей Ваткин - DirectX 8. Начинаем работу с DirectX Graphics читать онлайн бесплатно
Согласно названию, эта функция инициализирует D3D %) Перед созданием устройства рендеринга, необходимо настроить параметры D3DPRESENT_PARAMETERS. К параметрам, которые мы использовали в предыдущем приложении добавилось лишь две строки:
p_p.EnableAutoDepthStencil = TRUE;
p_p.AutoDepthStencilFormat = D3DFMT_D16;
Первая говорит о том, что при рендеринге будет использоваться Depth Buffer ("буфер глубины"), причем D3D будет управлять им автоматически. Когда разберешься с программой, попробуй поэкспериментировать: запусти программу в исходном виде, а затем проверь, что получится, если эти две строки удалить. Depth Buffer еще называют Z-Buffer'ом.
Рассмотрим принцип его использования на примере. Представь себе человека на фоне кирпичной стены… Теперь мысленно переведи эту "сцену" в Direct3D (для этого, необходимо задать человека и стену в виде набора текстурированных полигонов) и заставь его эту сцену отрендерить. То, что мы увидим на экране — всего лишь двумерная проекция нашей сцены. Т.о., D3D спроектировал на плоскость экрана стену и человека. Причем, мы увидели человека на фоне стены, а не стену на фоне человека, т.к. он стоит ближе к нам Ж-) Как же D3D "узнает" что именно проектировать на экран раньше, чтобы не было бессмысленных перекрытий? Для этого используется Z-Buffer! Z-Buffer представляет собой массив Z-координат каждой точки этой поверхности (будем называть ее числовой составляющей (ЧС) Z-Buffer'а). Ось Z направлена в плоскость экрана (от тебя). Каждой точке сопоставлен единственный элемент ЧС. Перед началом рендеринга Z-буфер может быть заполнен в каждой точке значением равным максимально возможным расстоянием от наблюдателя до объекта сцены. После этого, для каждого полигона в сцене выполняются следующие действия:
a. определяется область, которую будет занимать полигон при проектировании на плоскость экрана
b. для каждого пикселя этой области сравнивается Z-координата точки-прообраза, принадлежащей исходному полигону, с соответствующим значением ЧС
c. если соответствующее значение ЧС оказывается меньше, значит, рассматриваемая точка полигона не отображается, т.к. перекрыта другими объектами. Иначе, в ЧС для рассматриваемой точки записывается ее Z-координата, а в цветовую область заносится пиксель соответствующего цета.
Вот и весь принцип.
Далее в программе создается устройство рендеринга:
if (FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &p_p, &g_pD3DDevice))) {
if (FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &p_p, &g_pD3DDevice))) return FALSE;
}
Здесь программа пытается выбрать наиболее оптимальный режим для работы. Сначала она пробует создать аппаратное устройство рендеринга, т.е. когда все обсчеты графики ведет исключительно видеокарта (D3DDEVTYPE_HAL), но если это не удается (причин может быть много, но чаще всего она одна — старая видеокарта) D3D работает в режиме программной эмуляции, что прискорбно отражается на скорости и качестве графики. Едем дальше: Задается режим "выбраковки" тыльных сторон полигонов (подробней мы рассмотрим это дальше — в функции InitScene()):
g_pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);
Включаем равномерное освещение всей сцены:
g_pD3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
Включаем поддержку Z-Buffer'а
g_pD3DDevice->SetRenderState(D3DRS_ZENABLE, TRUE);
Может возникнуть вопрос, для чего может понадобиться отключение Z-Buffer'а. Одно из применений — вывод на экран текста, который должен перекрывать все остальное.
Хотелось бы также подробней рассказать про функцию SetRenderState() интерфейса IDirect3DDevice8, которую мы уже встречали ранее. Эта функция вообще незаменима, т.к. с ее помощью можно задать огромное количество различных настроек, влияющих на процесс рендеринга. Вот описание этой функции:
HRESULT SetRenderState(D3DRENDERSTATETYPE State, DWORD Value);
State — тип изменяемого параметра
Value — новое его значение
Функция InitTexture()Эта функции выполняет только одно действие — загружает текстуру из файла в память для дальнейшего использования. Загрузка текстуры вручную заняла бы у нас довольно обширный кусок программного кода - этому будет посвящена отдельная статья. Поэтому, используем функцию D3DXCreateTextureFromFile(), которую программисты Microsoft написали за нас :) Префикс "D3DX-" этой функции, говорит о том, что она взята из библиотеки D3DX. Эта вспомогательная библиотека включает в себя очень много полезных функций, как для математических операций (в основном, работы с матрицами), так и для загрузки изображений, формирования стандартных геометрических объектов (сфера, куб и т.п.) и многого другого. Тем не менее, эти функции написаны для общих задач. Когда ты будешь писать конкретную программу, требующую быстродействия, советую не использовать D3DX, а писать аналоги его функций самому.
Вот описание функции D3DXCreateTextureFromFile():
HRESULT D3DXCreateTextureFromFile(LPDIRECT3DDEVICE8 pDevice, LPCSTR pSrcFile, LPDIRECT3DTEXTURE8* ppTexture);
pDevice — указатель на устройство рендеринга
pSrcFile — текстовая строка, содержащая путь к файлу-текстуре
ppTexture — адрес переменной, которая будет содержать указатель на текстуру
Функция InitScene()Здесь происходит инициализация единственного объекта в сцене — четырехугольной пирамиды.
Как известно, в основании правильной четырехугольной пирамиды (SABCD) лежит квадрат (ABCD). Если сторона квадрата a, то высота пирамиды h=a/sqrt(2). Теперь, зная a, мы можем задать в трехмерном пространстве координаты всех пяти вершин пирамиды. В DirectX используется левосторонняя (left-handed) система координат (СК). Направление оси Z в левосторонней и правосторонней СК можно определить, пользуясь правилом соответственно левой и правой руки. Вот, как с помощью этого правила найти куда направлена ось Z в левосторонней СК: вытяни левую руку ладонью вверх и собери четыре пальца (все, кроме большого) вместе в плоскости ладони. Большой палец расположи перпендикулярно остальным четырем (тоже в плоскости ладони). Отлично! (Ты смог это! - прим. редактора ) Теперь, если направить ось X вдоль четырех пальцев, а ось Y - вверх, то большой палец укажет направление оси Z.
Координаты вершин пирамиды в пространстве можно записать следующим образом (не обращай пока внимание на последние 3 параметра каждой вершины):
float a=6.0;
#define vertA {-a/2, a/2, 0.0f, 0xffffffff, 0.0f, 1.0f,}
#define vertB {-a/2, -a/2, 0.0f, 0xffffffff, 0.0f, 0.0f,}
#define vertC {a/2, -a/2, 0.0f, 0xffffffff, 1.0f, 0.0f,}
#define vertD {a/2, a/2, 0.0f, 0xffffffff, 1.0f, 1.0f,}
#define vertS {0.0f, 0.0f, (float)(a/sqrt(2)), 0xffffffff, 0.5f, 0.5f,}
В D3D трехмерную модель можно задать различными способами. В нашем случае будем использовать TRIANGLELIST для которого фигура задается последовательностью треугольников. Когда задается треугольник, Direct3D сам определяет его лицевую и тыльную стороны по порядку следования вершин в массиве. При рендеринге, D3D автоматически "выбраковывает" тыльные стороны треугольников. Это заметно повышает скорость работы приложения. Но необходимо указать D3D в каком именно порядке задаются вершины лицевой стороны треугольников. Это делается с помощью той самой волшебной функции SetRenderState().
Итак, чтобы описать culling (выбраковку) тыльных сторон треугольников, вершины которых расположены в массиве по часовой стрелке (clockwise), необходимо написать следующее:
g_pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);
Для хранения вершин в D3D используются Vertex Buffer'ы (в дальнейшем, VB. Не путать с Visual Basic'ом :)). В зависимости от конкретной программы, VB'ы могут быть разных форматов. Например, если требуется написать программу, которая рисует на экране набор одноцветных точек, то для задания любой из точек требуется три числа, содержащих ее координаты в пространстве. Если точки должны отличаться по цвету, вводим четвертый параметр — цвет точки. Вроде бы все просто… Единственная сложность — мы как-то должны "сообщить" D3D в каком именно формате хранятся вершины в массиве, чтобы в процессе рендеринга не возникло путаницы. Впервые это нужно сделать в момент создания VB, затем перед рендерингом. Формат задается в виде комбинации флагов D3DFVF_*, полный список которых приведен в документации к D3D8. Нам же понадобятся лишь 3 флага:
#define D3DFVF_MYVERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1)
D3DFVF_XYZ — вершина задается тремя координатами в пространстве (а может задаваться и четырьмя — при D3DFVF_XYZRHW)
D3DFVF_DIFFUSE — вершина содержит цвет, который влияет на рассеяние света
D3DFVF_TEX1 -—вершина содержит две текстурные координаты
Т.к. запись
D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1
интерпретируется компилятором в точности, как и
D3DFVF_XYZ | D3DFVF_TEX1 | D3DFVF_DIFFUSE
значит порядок расположения данных в памяти в этом месте программы не задается. На программиста накладываются обязательства следовать схеме расположения данных, приведенной в руководстве D3D8 (раздел "About Vertex Formats").
Жалоба
Напишите нам, и мы в срочном порядке примем меры.