Шаг 13 - Вращаем разноцветный трехмерный треугольник

Наша задача в этом шаге, нарисовать настоящий трехмерный треугольник, раскрасить грани в разные цвета и заставить его вращаться, должно получиться что-то вроде этого:

13_1.gif (798 b)
вид слева
13_2.gif (775 b)
вид спереди
13_3.gif (771 b)
вид справа
13_4.gif (671 b)
вид сзади

Переопределим тип и описание нашей структуры, подключим математическую библиотеку для наших матриц:

#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 находится в центре экрана, а не слева сверху как это было раньше, потому что мы так инициализировали матрицы. Треугольник получился "кривоватый", не все так гладко, как хотелось бы... но об этом мы поговорим в следующем шаге.


Загрузить проект | Предыдущий Шаг | Следующий Шаг | Оглавление
Автор Хавов Евгений Валерьевич - 29.08.2002