Наша задача в этом шаге, нарисовать настоящий трехмерный треугольник, раскрасить грани в разные цвета и заставить его вращаться, должно получиться что-то вроде этого:
вид слева | вид спереди | вид справа | вид сзади |
Переопределим тип и описание нашей структуры, подключим математическую библиотеку для наших матриц:
#include <d3dx8math.h> #include <mmsystem.h> #pragma comment (lib, "d3dx8.lib") struct CUSTOMVERTEX { float x, y, z; DWORD color; }; #define D3DFVF_CUSTOMVERTEX ( D3DFVF_XYZ | D3DFVF_DIFFUSE )
Заносим в структуру новые значения вершин - это у нас будет разноцветный треугольник:
CUSTOMVERTEX g_Vertices[] = { { 0.0f, 0.0f, 0.0f, 0xff0000ff,}, { 0.0f,100.0f, 0.0f, 0xff0000ff,}, { 60.0f, 0.0f, 0.0f, 0xff0000ff,}, { 0.0f, 60.0f, 0.0f, 0xffff0000,}, { 60.0f, 0.0f, 60.0f, 0xffff0000,}, { 60.0f, 0.0f, 0.0f, 0xffff0000,}, { 0.0f, 0.0f, 0.0f, 0xff0ff0ff,}, { 60.0f, 0.0f, 60.0f, 0xff0ff0ff,}, { 0.0f,100.0f, 0.0f, 0xff0ff0ff,}, };
Чтобы нам не высчитывать каждый раз количество вершин и подставлять в функцию CreateVertexBuffer, мы сделаем иначе:
p_d3d_Device->CreateVertexBuffer (sizeof(g_Vertices), 0, D3DFVF_CUSTOMVERTEX, D3DPOOL_MANAGED, &p_VertexBuffer);
и больше сюда мы не будем заглядывать, т.к. компьютер теперь будет сам автоматически высчитывать :)
Теперь, собственно, возьмемся за настоящее 3D. Заносим в класс главного окна:
class CMainWnd : public CFrameWnd { private: . . . D3DXMATRIX matWorld; // мировая матрица D3DXMATRIX matView; // матрица вида D3DXMATRIX matProj; // матрица проецирования DECLARE_MESSAGE_MAP() public: void Transform3D(void); . . .
Первая матрица - это матрица вращения, т.е. по ней мы будем вращать всю сцену. На данный момент наша сцена состоит из одного трехмерного треугольника. Что такое матрица, смотрите в "Шаг 56 - Что такое матрица?".
Вторая матрица - это матрица вида, т.е. это можно сравнить с глазом наблюдателя или направленной камерой, где он(а) находится, куда указывает и что в итоге мы видим.
Вроде казалось бы и все, но нет, есть еще и третья матрица - матрица проецирования, т.е. отображает трехмерный мир на двумерный экран Вашего монитора.
Функция Transform3D - это наша функция выполняющая вращение треугольника и мы ее сейчас напишем:
void CMainWnd::Transform3D(void) { static float x; x = (float)(x + 0.03); D3DXMatrixRotationY (&matWorld, x); p_d3d_Device->SetTransform (D3DTS_WORLD, &matWorld); D3DXMatrixLookAtLH (&matView, &D3DXVECTOR3 (0.0f, 0.0f, -500.0f), &D3DXVECTOR3 (0.0f, 0.0f, 0.0f), &D3DXVECTOR3 (0.0f, 1.0f, 0.0f)); p_d3d_Device->SetTransform (D3DTS_VIEW, &matView); D3DXMatrixPerspectiveFovLH (&matProj, D3DX_PI/4, 1.0f, 1.0f, 10000.0f); p_d3d_Device->SetTransform (D3DTS_PROJECTION, &matProj); p_d3d_Device->SetRenderState (D3DRS_CULLMODE, D3DCULL_NONE); p_d3d_Device->SetRenderState( D3DRS_LIGHTING, FALSE ); }
D3DXMatrixRotationY - первый параметр это матрица вращения, вокруг какой оси нам вращаться. Второй параметр - на какой угол повернуть всю сцену (т.е. под каким углом мы смотрим) или в нашем случае это скорость вращения. Строка x = (float)(x + 0.03);, чтобы компилятор не ругался, что float складываем с double. Если хотите, чтобы треугольник вращался быстрее, увеличьте значение 0.03. Можно также вместо х поставить timeGetTime()/300.0f - если треугольник не будет вращаться, увеличьте 300 в два раза или более, и не забудьте подключить библиотеку winmm.lib.
Здесь Вы также можете поменять с легкостью ось вращения, замените функцию D3DXMatrixRotationY на D3DXMatrixRotationX, теперь треугольник сможет вращаться по оси X, можете выставить и ось вращения Z.
SetTransform - устанавливает мировую матрицу в устройство. Мировая матрица - это матрица, которая может преобразовывать всю сцену целиком. В нашем случае мы вращаем всю сцену вокруг координат 0,0,0.
D3DXMatrixLookAtLH получает следующие параметры:
Попробуйте изменить значение -500, скажем на -1000, и камера отдалится от треугольника или уменьшите расстояние, тогда треугольник увеличится.
D3DXMatrixPerspectiveFovLH - матрица проецирования. Второй параметр - это поле зрения, Третий параметр - соотношение геометрических размеров и последние два параметра - задают границу отсечения передней и задней плоскости. Т.е. когда объект слишком близок, он исчезает, и Вы думаете что он сзади, когда слишком далеко, то он тоже исчезает.
SetRenderState - устанавливаем режим отображения. Более подробно о параметрах Вы можете посмотреть в "Шаг 27 - Описание перечисляемого типа D3DCULL". Заодно и проверить действительно ли работают остальные перечисленные параметры (лучше начать свои эксперименты в следующем шаге). Также более подробно об этой функции Вы можете почитать в "Шаг 53 - IDirect3DDevice8::SetRenderState". Почему мы эту функцию поставили в Transform3D, да чтобы после потери устройтсва наш треугольник снова мог нормально отображаться. Попробуйте перенести эти функции в конструктор главного окна CMainWnd::CMainWnd() и нажать клавиши при запуске приложения Alt+TAB и понаблюдать за результатом. Уверяю Вас, результаты будут неутешительными...
Саму же функцию Transform3D вызовем из OnPaint() после сбрасывания потери устройства. Также нам нужно будет восстановить матрицы после потери устройств. И заодно поменяем фон очистки экрана, т.к. у нас треугольник будет черным цветом по умолчанию, в результате мы можем просто его не увидеть:
void CMainWnd::OnPaint() { HRESULT hr; hr=p_d3d_Device->TestCooperativeLevel(); if(hr==D3DERR_DEVICELOST) return; if (hr==D3DERR_DEVICENOTRESET) { p_d3d_Device->Reset(&d3dpp); Transform3D(); } SetCursor(NULL); Transform3D(); p_d3d_Device->Clear (0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB (128, 128, 0), 0.0f, 0); p_d3d_Device->BeginScene (); p_d3d_Device->SetVertexShader (D3DFVF_CUSTOMVERTEX); p_d3d_Device->SetStreamSource (0, p_VertexBuffer, sizeof(CUSTOMVERTEX)); p_d3d_Device->DrawPrimitive (D3DPT_TRIANGLELIST, 0, 3); . . .
и не забудьте в функции DrawPrimitive указать количество отображаемых треугольников, в нашем случае это 3.
Компилируем, запускаем, смотрим результат наших трудов :). Хочу заметить только то, что теперь центр 0,0,0 находится в центре экрана, а не слева сверху как это было раньше, потому что мы так инициализировали матрицы. Треугольник получился "кривоватый", не все так гладко, как хотелось бы... но об этом мы поговорим в следующем шаге.