Загрузка...
 
Печать

DirectX 8 Graphics. Освещение


  • Очень важно для придачи 3D-сцене максимальной реалистичности. Многие игры грешат тем, что полностью заполняют светом сцену, делая её вид чётким, пусть и не очень реалистичным. Для придания сцене реалистичности необходимо заюзать световые эффекты, в чём нам также поможет DirectX Graphics.

Для компиляции примеров на понадобится:
  • MS Visual C++ 2010 Express Edition,
  • Microsoft DirectX SDK 8.
Всё легко гуглится + есть в разделе "Софт" нашего сайта.

Содержание



Intro

Image
Рис.1 Виды источников света

Direct3D 8 поддерживает 4 вида источников света (См. Рис.1):1
Ambient (фоновый) Представляет собой постоянный (constant) источник света, который равномерно освещает всю сцену. В свете того, что тип Ambient является частью компонента объекта устройства Direct3D, это единственный тип источника света, который работает отдельно от движка освещения (lighting engine).
Point (точечный) Освещает пространство вокруг своего источника, прямо как лампочка.
Spotlight (прожектор) Освещает пространство вокруг своего источника, прямо как лампочка. Имеет определённое направление свечения и излучает пучок света в форме конуса. Всё внутри данного конуса освещено, в то время как все объекты за пределами конуса - нет. (См. Рис.2)
Directional (направленный) Освещает пространство вокруг своего источника, прямо как лампочка. Является упрощённым вариантом прожектора. Равномерно излучает свет в заданном направлении.

Источники света размещаются в 3D-сцене точно также, как и любые другие объекты - с использованием X, Y и Z координат пространства. Некоторые виды источников (например прожектор) дополнительно имеет вектор, задающий направление свечения.
Каждый вид светового источника в DirectX также имеет:
  • Уровень интенсивности (intensity level);
  • Расстояние (range) светового луча (лучей);
  • Факторы затухания (attenuation factors);
  • Цвет.
Да, DirectX также поддерживает источники света, окрашенные в определённый цвет.

Заполняем структуру D3DLIGHT8

За исключением рассеянного света (ambient light), все источники для хранения своих данных используют структуру D3DLIGHT8 (для DirectX 8):
Определение структуры D3DLIGHT8
typedef struct _D3DLIGHT8
{
	D3DLIGHTTYPE Type; // Тип источника света
	D3DCOLORVALUE Diffuse; // Рассеянный (diffuse) компонент света.
	D3DCOLORVALUE Specular; // Самоизлучаемый (specular = бликовый) компонент света.
	D3DCOLORVALUE Ambient; // Приглушённый (ambient = теневой) компонент света.
	D3DVECTOR Position; // Расположение (позиция) источника света.
	D3DVECTOR Direction; // Направление лучей света. float Range; // Длина луча света.
	float Falloff; // Степень ослабления светового пятна от центра к краям.
	float Attenuation0; // Затухание света 0;
	float Attenuation1; // Затухание света 1;
	float Attenuation2; // Затухание света 2;
	float Theta; // Угол внутреннего конуса;
	float Phi; // Угол внешнего конуса;
} D3DLIGHT8;

Зачастую источники света используют далеко не все поля данной структуры.
Первым делом указываем тип (Type) источника света. Здесь возможны следующие значения:
  • D3DLIGHT_POINT для точечного источника;
  • D3DLIGHT_SPOT для прожектора;
  • D3DLIGHT_DIRECTIONAL для источника направленного света.
Далее идут 3 параметра цвета света:
  • Diffuse - наиболее часто применяемый параметр. Определяет цвет света, испускаемого источником.
  • Specular определяет цвет блика (highlight). Если блики не нужны, установи все компоненты в 0.0 .
  • Ambient - цвет отбрасываемой тени.
Все 3 параметра представляют собой значения типа D3DCOLORVALUE, которая, в свою очередь, также является структурой:
typedef struct _D3DCOLORVALUE
{
	float r; // Значение красного компонента. Может принимать значение от 0.0 до 1.0 .
	float g; // Значение зелёного компонента. Может принимать значение от 0.0 до 1.0 .
	float b; // Значение синего компонента. Может принимать значение от 0.0 до 1.0 .
	float a; // Значение альфа-компонента (компонента прозрачности). Зарезервировано и не используется.
} D3DCOLORVALUE;

Закрыть
noteОбрати внимание

Помимо использования источников света для освещения объектов сцены, эти объекты можно также затемнять. Для этого достаточно использовать в параметрах цвета света негативные (отрицательные) значения.

Каждый компонент цвета света выставляется в диапазоне от 0.0 (цветовой компонент выключен) до 1.0 (цветовой компонент включен на максимум). Чисто красный цвет будет иметь такие значения компонентов: r=1.0, g=0.0, b=0.0. Белый: r=1.0, g = 1.0, b=1.0. В свете того, что мы имеем дело со светом, альфа-компонент здесь не используется.
Как упоминалось выше, каждый источник света может быть размещён в 3D-пространстве с использованием XYZ-координат (мировые координаты; world coordinates), которые хранятся в векторе позиции (Position vector). Направление (direction) лучей света также задаётся специальным вектором Direction vector.
Переменная Range определяет, как далеко свет может пролететь, прежде чем начать пропадать (falling off). Никакие объекты за пределами данной дистанции не будут освещены.
Falloff - это значение, которым задаётся то, как быстро свет будет приглушаться при движении от внутреннего конуса к внешнему (См. Рис.2). То есть, насколько резко будет очерчена граница светового пятна. Обычное значение Falloff = 1.0 делает плавный переход.
Три поля затухания (Attenuation) определяют, как быстро свет будет пропадать по достижению максимальной дистанции. Обычно их всегда выставляют в 0.
Применение остальных значений напрямую зависит от типа источника света.

Point Light (точечный источник света)

  • Самый простой в применении.
Достаточно просто указать их позицию, цветовые компоненты и Range. Просто создают инстанс структуры D3DLIGHT8 и заполняют его необходимой информацией:
D3DLIGHT8 PointLight;

// Очищаем все поля данного инстанса структуры.
ZeroMemory(&PointLight, sizeof(D3DLIGHT8));

// Задаём позицию источника света в точке с координатами 0.0, 100.0, 200.0
PointLight.Position = D3DVECTOR3(0.0f, 100.0f, 200.0f);

// Устанавливаем diffuse и ambient компоненты в белый цвет.
PointLight.Diffuse.r = PointLight.Ambient.r = 1.0f;
PointLight.Diffuse.g = PointLight.Ambient.g = 1.0f;
PointLight.Diffuse.b = PointLight.Ambient.b = 1.0f;

// Устанавливаем дистанцию (range) лучей света в 1000 единиц.
PointLight.Range = 1000.0f;


Spotlight (прожекторный источник света)

Image
Рис.2 Углы Phi и Theta задают характер параметра Falloff светового пятна

  • Работает немного по-другому, т.к. излучает световые лучи в форме конуса, чья вершина расположена в центре источника (См. Рис.1).
Здесь интенсивность (brightness) света максимальна и постепенно снижается при движении к основанию конуса. Все объекты за пределами конуса остаются неосвещёнными.
Закрыть
noteОбрати внимание

Прожекторные источники света (spotlights) являются самыми ресурсозатратными. Поэтому желательно свести их число в сцене к необходимому минимуму.

Спотлайт определяется:
  • положением (Position);
  • направлением (Direction);
  • цветом каждого из световых компонентов (diffuse, highlight, ambient);
  • длинной лучей (Range);
  • величиной размытия границ светового пятна (Falloff);
  • параметрами затухания (Attenuation);
  • углом внутреннего конуса (inner cone angle);
  • углом внешнего конуса (outer cone angle).
Здесь не так важны Fallof и Attenuation. Но следует обратить внимание на углы обоих конусов. Именно здесь участвуют 2 последних параметра структуры D3DLIGHT, приведённой выше:
  • Phi - определяет угол (в радианах) внешнего конуса;
  • Theta - определяет угол (в радианах) внутреннего конуса. (См. Рис.2)
Чем дальше свет проследует от своего источника (спотлайта), тем больше будет радиус пятна проекции. Не секрет, что программеры экспериментально подбирают значения для каждого источника.
Следующий пример кода создаёт источник света типа спотлайт (=прожектор) и назначает его позицию (Position), цвет (Color), дистанцию луча (Range), показатель величины размытия границ светового пятна (Falloff) и углы обоих конусов:
D3DLIGHT8 Spotlight;

// Очищаем все поля данного инстанса структуры.
ZeroMemory(&Spotlight, sizeof(D3DLIGHT8));

// Задаём позицию источника света в точке с координатами 0.0, 100.0, 200.0
Spotlight.Position = D3DVECTOR3(0.0f, 100.0f, 200.0f);

// Устанавливаем diffuse и ambient компоненты в белый цвет.
Spotlight.Diffuse.r = Spotlight.Ambient.r = 1.0f;
Spotlight.Diffuse.g = Spotlight.Ambient.g = 1.0f;
Spotlight.Diffuse.b = Spotlight.Ambient.b = 1.0f;

// Устанавливаем дистанцию (range) лучей света в 1000 единиц.
Spotlight.Range = 1000.0f;

// Устанавливаем Falloff.
Spotlight.Falloff = 1.0f;

// Устанавливаем углы (в радианах) внешнего и внутреннего конусов.
Spotlight.Phi = 0.34 88; // Внешний конус в 20 градусов.
Spotlight.Theta = 0.1744; // Внутренний конус в 10 градусов.

Далее надо указать спотлайту направление движения лучей. Во вспомогательной библиотеке D3DX есть пара ценных функций для этого.
Первая из них перегружает (overload) значение D3DXVECTOR3 источника и позволяет указать 3 координаты (в мировом пространстве; world space). Если, например, у нас есть источник света, расположенный в любом месте 3D-пространства и мы хотим направить его вверх, на 500 единиц выше самого источника, то для этого достаточно установить следующие значения вектора направления: X=0, Y=500, Z=0. Координаты указываются относительно источника света. Вот пример кода:
D3DXVECTOR3 Direction = D3DXVECTOR(0.0f, 500.0f, 0.0f);

Единственная проблема здесь заключается в том, что DirectX предпочитает нормализованные векторы (то есть векторы, чьи координаты изменяются от 0.0 до 1.0). И здесь на помощь приходит вторая функция из D3DX - D3DXVec3Normalize. Вот её прототип:
D3DXVECTOR3 *D3DXVec3Normalize
	(
		D3DXVECTOR3 *pOut, // Нормализованный вектор.
		CONST D3DXVECTOR *pV // Вектор-источник. 
	);

Здесь, при передаче исходного вектора (например того, что рассмотрен выше, с координатами X=0, Y=500, Z=0) и указателя на новый результирующий вектор, функция D3DXVec3Normalize конвертирует координаты в значения от 0 до 1. Полученный на выходе вектор содержит значения направления, которые уже можно применять для указания направления источника света в структуре D3DLIGHT8.
Продолжая пример, приведённый выше, установим направления луча спотлайта вверх, нормализуем вектор и сохраним в структуре D3DLIGHT8:
D3DXVECTOR3 Dir = D3DXVECTOR3(0.0f, 500.0f, 0.0f);
D3DXVec3Normalize((D3DXVECTOR3*)&Spotlight.Direction, &Dir);


Directional Light (направленный источник света)

С точки зрения скорости выполнения, направленные источники света - самые быстрые. Они освещают все полигоны, на которые направлены. Для задания направленного источника света необходимо:
  • задать направление;
  • заполнить поля цветовых компонентов света.
Всё, как обычно, происходит в инстансе структуры D3DLIGHT8 (для DirectX 8).
Закрыть
noteОбрати внимание

Мы не указываем позицию направленного источника. Как ни странно, она здесь не важна. Совсем. Направление задаёт угол падения лучей света. А откуда они падают - неважно. Здесь поток света можно сравнить с рекой, чью длину можно условно принять за бесконечность. Начало реки не видно, но объекты в ней омываются её водами по направлению течения.

В следующем примере мы по аналогии создаём жёлтый свет, который падает на сцену вниз откуда-то сверху:
D3DLIGHT8 Dirlight;

// Очищаем все поля данного инстанса структуры.
ZeroMemory(&Dirlight, sizeof(D3DLIGHT8));

// Устанавливаем diffuse и ambient компоненты в жёлтый цвет.
Dirlight.Diffuse.r = Dirlight.Ambient.r = 1.0f;
Dirlight.Diffuse.g = Dirlight.Ambient.g = 1.0f;
Dirlight.Diffuse.b = Dirlight.Ambient.b = 0.0f;

D3DXVECTOR3 Dir = D3DXVECTOR3(0.0f, 500.0f, 0.0f);
D3DXVec3Normalize((D3DXVECTOR3*)&Dirlight.Direction, &Dir);


Ambient Light (фоновый источник света)

  • Свет, который настолько распределен средой (предметами, стенами и так далее), что его направление определить невозможно. Кажется, что он исходит отовсюду. Например, если объект не будет ничем освещен, то фоновый компонент определит то минимальное количество света, которое будет отражаться предметом. Ведь даже в полной темноте мы можем увидеть как минимум очертания предмета.
  • Единственный вид источника света, который не назначается в структуре D3DLIGHT8, т.к. Direct3D применяет фоновый свет ко всем полигонам сцены, вне зависимости от их положения или освещённости другими источниками.
  • Не отбрасывает тени.
  • Имеет постоянный уровень и также может быть окрашен в определённый цвет.
  • Устанавливается путём вызова функции SetRenderState на текущем объекте устройства Direct3D:
g_pD3DDevice->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_COLORVALUE(0.0f, 0.0f, 0.0f, 0.0f));

Определение макроса D3DCOLOR_COLORVALUE выглядит так:
D3DCOLOR D3DCOLOR_COLORVALUE( float r, float g, float b, float a);

Отсюда видно, что первые три компонента отвечают за долю красного, зелёного и синего цветов в окрасе света (допустимы значения от 0.0f до 1.0f). Четвёртый компонент отвечает за прозрачность и, в данном случае, всегда выставляется в 0.0f.

Прописываем созданный источник в объекте устройства Direct3D (Setting the light)

Сразу после заполнения структуры D3DLIGHT8, передаём её указатель в объект устройства Direct3D с помощью функции IDirect3DDevice8::SetLight:
HRESULT IDirect3DDevice8::SetLight(DWORD Index, CONST D3DLIGHT8 *pLight);

В данном случае указатель *pLight передаёт в функцию (заполненную) структуру D3DLIGHT8.
Разберёмся с первым параметром (Index). Direct3D позволяет установить на одной сцене несколько световых источников. Таким образом Index является индексом (номером по порядку, начинающимся с нуля) назначаемого источника света. Если, например, мы используем в сцене 4 источника света, то первый из них получит индекс 0, второй - 1, третий - 2, четвёртый -3. В теории DirectX 8 поддерживает неограниченное кол-во источников в сцене. Но спецы рекомендуют применять не больше 4-х, т.к. каждый источник повышает сложность сцены и увеличивает время рендеринга.

Применяем нормали (Using Normals)


Image
Рис.3 У каждой вершины есть нормаль, которая проведена в определённом направлении

Image
Рис.4 Угол между нормалью и плоскостью грани применяется для расчёта угла отражения света, падающего на грань


Для того, чтобы Direct3D корректно осветил грани полигональной поверхности заданным источником света, каждой вершине должна быть назначена нормаль. Нормаль представляет собой 3D-вектор, проведённый перпендикулярно поверхности грани, который задаёт лицевую (= потенциально видимую) сторону вершины или полигона. Различают нормали вершин и нормали граней (полигонов) (См. Рис.3). В Direct3D, как правило, нормали используются в сложных расчётах для определения кол-ва света, получаемого гранью/полигоном от каждого источника света (См. рис.4). Например, при наличии у каждой из вершин полигона нормали, можно рассчитать степень освещённости полигона, учтя угол между вектором падающего света и каждым из 3D-векторов нормалей. Когда луч света попадает на вершины, он отражается под определённым углом, рассчитанным по векторам нормалей вершин. Применение нормалей обеспечивает корректный расчёт освещённости граней сцены. Кроме того, нормали обеспечивают создание корректных теней, которые рассчитываются уже с учётом углов между:
  • векторами нормалей,
  • вектором взгляда наблюдателя,
  • векторами источников света.
Добавление инфы о нормалях в FVF-структуру производится при описании текстуры. Для этого достаточно создать нормаль в виде вектора типа D3DVECTOR3 и переопределить структуру VERTEXFMT, добавив в неё значение D3DFVF_NORMAL:
typedef struct
{
	D3DVECTOR3 Position; // Вектор позиции
	D3DVECTOR3 Normal; // Нормаль
	D3DCOLOR Color; // Цвет
}sVertex;

#define VERTEXFMT (D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_NORMAL)

Нормали рассчитываются тем же способом, что и вектор направления при работе с источниками света. При работе с 3D-моделями 3D-редакторы рассчитывают нормали автоматически.
В следующем примере создаём цилиндр (с помощью функции GenerateCylinder) и присваиваем каждой его вершине нормаль, направленную прочь от центра торцевых многоугольников:
// g_pD3DDevice - предварительно созданный и настроенный объект устройства Direct3D.

IDirect3DVertexBuffer8 *GenerateCylinder()
{
	IDirect3DVertexBuffer8 *pD3DVertexBuffer;
	sVertex *pVertex;
	DWORD i;
	FLOAT theta;
	
	// Создаём вершинный буфер
	if(SUCCEEDED(g_pD3DDevice->CreateVertexBuffer( 50 * 2 * sizeof(sVertex), 0,
		VERTEXFMT, D3DPOOL_DEFAULT, &pD3DVertexBuffer))
	{
		// Заполняем вершинный буфер инфой о цилиндре.
		if(SUCCEEDED(pD3DVertexBuffer->Lock(0, 0, (BYTE**)&pVertex, 0))
		{
			for(i=0; i<50; i++)
			{
				theta = (2 * D3DX_PI * i) / (50-1);
				pVertex[2*i + 0].Position = D3DXVECTOR3(sinf(theta), -1.0f, cosf (theta));
				pVertex[2*i+0].Normal = D3DXVECTOR3(sinf(theta), 0.0f, cosf(theta));
				pVertex[2*i+1].Position = D3DXVECTOR3(sinf(theta), 0.0f, cosf(theta));
				pVertex[2*i+1].Normal = D3DXVECTOR3(sinf(theta), 0.0f, cosf(theta));
			}
			pD3DVertexBuffer->Unlock();
			
			// Возвращаем указатель на только что созданный вершинный буфер.
			return pD3DVertexBuffer;
		}
	}

	// В случае ошибки возвращаем NULL.
	return NULL;
}


Включаем источник света (Turn the light On)

Теперь, когда ты определился с типом источника света и заполнил, соответствующую ему, структуру, пробил час активировать "конвейер освещения" (lighting pipeline). Для этого ставим рендерстейт D3DRS_LIGHTING в TRUE:
// g_pD3DDevice - предварительно созданный и настроенный объект устройства Direct3D.
g_pD3DDevice->SetRenderState(D3DRS_LIGHTING, TRUE);

Для деактивации (выключения) конвеера освещения снова вызываем функцию SetRenderState, выставив во втором параметре FALSE:
// g_pD3DDevice - предварительно созданный и настроенный объект устройства Direct3D.
g_pD3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);


Закрыть
noteОбрати внимание

Следует различать понятия "конвейер освещения" и "источник света". Сперва необходимо создать и активировать "конвейер освещения" определённого типа. Только после этого можно включать и выключать входящие в него световые источники, каждому из которых присвоен свой индекс, начинающийся с 0.

Световые источники включаются и выключаются с помощью одной и той же функции IDirect3DDevice8::LightEnable. Вот её прототип:
IDirect3DDevice8::LightEnable(
	DWORD LightIndex, // Индекс (= порядковый номер) источника света, начинающийся с 0.
	BOOL bEnable // TRUE - включаем источник света. FALSE - выключаем.
	);

К примеру, у нас настроен точечный источник света (point light) и ему присвоен индекс 0 (LightIndex 0). Мы можем включать и выключать его следующим образом:
// g_pD3DDevice - предварительно созданный и настроенный объект устройства Direct3D.

// Включаем источник света.
g_pD3DDevice->LightEnable(0, TRUE);

// Выключаем источник света.
g_pD3DDevice->LightEnable(0, FALSE);


Пример приложения, демонстрирующего работу со световыми источниками (Win32, DirectX 8)

Перед началом проверь, что у тебя установлены следующие программные компоненты:
  • MS Visual C++ 2010 Express Edition;
  • DirectX SDK 8.1
  • Windows SDK (для программирования под Win7/8/10).
Все эти штуки:
  • + инструкции по их установке ты найдёшь в разделе "Софт" нашего сайта;
  • бесплатны;
  • без труда гуглятся.

Создаём Проект приложения Light01

  • Создай пустой Проект с именем Light01.
Проект автоматически разместится внутри Решения с таким же именем. Весь процесс подробно расписан в статье MS Visual Cpp 2010 Express. Установка и указание путей к DirectX SDK.

Добавляем в Проект WinMain.cpp

Для чистоты эксперимента мы создали пустой Проект, т.е. без каких-либо файлов в нём. Создадим единственный файл с исходным кодом WinMain.cpp.
  • В "Обозревателе решений" главного окна MSVC++2010 щёлкни правой кнопкой мыши по папке (в терминологии Майкрософт это не папки, а фильтры!) "Файлы исходного кода" Проекта Light01.
  • Во всплывающем меню Добавить->Создать элемент...
Image
  • В появившемся окне выбери "Файл С++ (.cpp)" и в поле "Имя" введи WinMain.cpp. Жмём "Добавить".
Image
Добавленный файл сразу откроется в правой части MSVC++2010.
  • В только что созданном и открытом файле WinMain.cpp набираем следующий код:
WinMain.cpp
/**************************************************
WinMain.cpp
Chapter 6 Lights Demo

Programming Role-Playing Games with DirectX
by Jim Adams (01 Jan 2002)

Required libraries:
  WINMM.LIB, D3D8.LIB and D3DX8.LIB
**************************************************/

// Include files
#include <windows.h>
#include <stdio.h>
#include "d3d8.h"
#include "d3dx8.h"

// Window handles, class and caption text
// Дескриптор окна, имя оконного класса и текст тулбара
HWND          g_hWnd;
HINSTANCE     g_hInst;
static char   g_szClass[]   = "LightClass";
static char   g_szCaption[] = "Light Demo by Jim Adams";

// The Direct3D and Device object
IDirect3D8       *g_pD3D       = NULL;
IDirect3DDevice8 *g_pD3DDevice = NULL;

// The 3-D vertex format and descriptor
typedef struct {
  FLOAT x, y, z;     // 3-D coordinates
  FLOAT nx, ny, nz;  // Normals
  D3DCOLOR Diffuse;  // Color
} sVertex;
#define VERTEXFVF (D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE)

// Vertex buffer
IDirect3DVertexBuffer8 *g_pVB = NULL;

// Function prototypes
int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev,          \
                   LPSTR szCmdLine, int nCmdShow);
long FAR PASCAL WindowProc(HWND hWnd, UINT uMsg,              \
                           WPARAM wParam, LPARAM lParam);

BOOL DoInit();
BOOL DoShutdown();
BOOL DoFrame();
BOOL SetupMeshes();

int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev,          \
                   LPSTR szCmdLine, int nCmdShow)
{
  WNDCLASSEX wcex;
  MSG        Msg;

  g_hInst = hInst;

  // Create the window class here and register it
  // Создаём оконный класс и регистрируем его
  wcex.cbSize        = sizeof(wcex);
  wcex.style         = CS_CLASSDC;
  wcex.lpfnWndProc   = WindowProc;
  wcex.cbClsExtra    = 0;
  wcex.cbWndExtra    = 0;
  wcex.hInstance     = hInst;
  wcex.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
  wcex.hCursor       = LoadCursor(NULL, IDC_ARROW);
  wcex.hbrBackground = NULL;
  wcex.lpszMenuName  = NULL;
  wcex.lpszClassName = g_szClass;
  wcex.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);
  if(!RegisterClassEx(&wcex))
    return FALSE;

  // Create the Main Window
  g_hWnd = CreateWindow(g_szClass, g_szCaption,
        WS_CAPTION | WS_SYSMENU,
        0, 0, 400, 400,
        NULL, NULL,
        hInst, NULL );
  if(!g_hWnd)
    return FALSE;
  ShowWindow(g_hWnd, SW_NORMAL);
  UpdateWindow(g_hWnd);

  // Run init function and return on error
  if(DoInit() == FALSE)
    return FALSE;

  // Start message pump, waiting for signal to quit
  // Стартуем цикл обработки сообщений
  ZeroMemory(&Msg, sizeof(MSG));
  while(Msg.message != WM_QUIT) {
    if(PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE)) {
      TranslateMessage(&Msg);
      DispatchMessage(&Msg);
    }
    if(DoFrame() == FALSE)
      break;
  }

  // Run shutdown function
  DoShutdown();
  
  UnregisterClass(g_szClass, hInst);

  return Msg.wParam;
}

long FAR PASCAL WindowProc(HWND hWnd, UINT uMsg,              \
                           WPARAM wParam, LPARAM lParam)
{
  switch(uMsg) {
    case WM_DESTROY:
      PostQuitMessage(0);
      return 0;

  }

  return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

BOOL DoInit()
{
  D3DPRESENT_PARAMETERS d3dpp;
  D3DDISPLAYMODE        d3ddm;
  D3DXMATRIX matProj, matView;
  D3DLIGHT8 Light;
  BYTE *Ptr;
  sVertex Verts[16] = {
    { -100.0f,  100.0f, -100.0f, 0.0f,0.0f,-1.0f, D3DCOLOR_RGBA(255,255,255,255) },
    {  100.0f,  100.0f, -100.0f, 0.0f,0.0f,-1.0f, D3DCOLOR_RGBA(255,255,255,255) },
    { -100.0f, -100.0f, -100.0f, 0.0f,0.0f,-1.0f, D3DCOLOR_RGBA(255,255,255,255) },
    {  100.0f, -100.0f, -100.0f, 0.0f,0.0f,-1.0f, D3DCOLOR_RGBA(255,255,255,255) },

    {  100.0f,  100.0f, -100.0f, 1.0f,0.0f,0.0f, D3DCOLOR_RGBA(255,255,255,255) },
    {  100.0f,  100.0f,  100.0f, 1.0f,0.0f,0.0f, D3DCOLOR_RGBA(255,255,255,255) },
    {  100.0f, -100.0f, -100.0f, 1.0f,0.0f,0.0f, D3DCOLOR_RGBA(255,255,255,255) },
    {  100.0f, -100.0f,  100.0f, 1.0f,0.0f,0.0f, D3DCOLOR_RGBA(255,255,255,255) },

    {  100.0f,  100.0f,  100.0f, 0.0f,0.0f,1.0f, D3DCOLOR_RGBA(255,255,255,255) },
    { -100.0f,  100.0f,  100.0f, 0.0f,0.0f,1.0f, D3DCOLOR_RGBA(255,255,255,255) },
    {  100.0f, -100.0f,  100.0f, 0.0f,0.0f,1.0f, D3DCOLOR_RGBA(255,255,255,255) },
    { -100.0f, -100.0f,  100.0f, 0.0f,0.0f,1.0f, D3DCOLOR_RGBA(255,255,255,255) },

    { -100.0f,  100.0f,  100.0f, -1.0f,0.0f,0.0f, D3DCOLOR_RGBA(255,255,255,255) },
    { -100.0f,  100.0f, -100.0f, -1.0f,0.0f,0.0f, D3DCOLOR_RGBA(255,255,255,255) },
    { -100.0f, -100.0f,  100.0f, -1.0f,0.0f,0.0f, D3DCOLOR_RGBA(255,255,255,255) },
    { -100.0f, -100.0f, -100.0f, -1.0f,0.0f,0.0f, D3DCOLOR_RGBA(255,255,255,255) }
  };

  // Do a windowed mode initialization of Direct3D
  // Выполняем инициализацию Direct3D под оконный режим
  if((g_pD3D = Direct3DCreate8(D3D_SDK_VERSION)) == NULL)
    return FALSE;
  if(FAILED(g_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm)))
    return FALSE;
  ZeroMemory(&d3dpp, sizeof(d3dpp));
  d3dpp.Windowed = TRUE;
  d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
  d3dpp.BackBufferFormat = d3ddm.Format;
  d3dpp.EnableAutoDepthStencil = TRUE;
  d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
  if(FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hWnd,
                                  D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                  &d3dpp, &g_pD3DDevice)))
    return FALSE;

  // Set the rendering states
  g_pD3DDevice->SetRenderState(D3DRS_LIGHTING, TRUE);
  g_pD3DDevice->SetRenderState(D3DRS_ZENABLE, TRUE);

  // Create and set the projection matrix
  D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI/4.0f, 1.33333f, 1.0f, 1000.0f);
  g_pD3DDevice->SetTransform(D3DTS_PROJECTION, &matProj);

  // Create and set the view matrix
  D3DXMatrixLookAtLH(&matView,                                \
                     &D3DXVECTOR3(0.0f, 0.0f, -500.0f),       \
                     &D3DXVECTOR3(0.0f, 0.0f, 0.0f),          \
                     &D3DXVECTOR3(0.0f, 1.0f, 0.0f));
  g_pD3DDevice->SetTransform(D3DTS_VIEW, &matView);

  // Create the vertex buffer and set data
  g_pD3DDevice->CreateVertexBuffer(sizeof(sVertex)*16, 0,     \
                          VERTEXFVF, D3DPOOL_DEFAULT, &g_pVB);
  g_pVB->Lock(0,0, (BYTE**)&Ptr, 0);
  memcpy(Ptr, Verts, sizeof(Verts));
  g_pVB->Unlock();

  // Set light data, color, position, and range
  // Устанавливаем данные источника света: цвет, позиция, расстояние пучка
  ZeroMemory(&Light, sizeof(Light));
  Light.Type = D3DLIGHT_POINT;
  Light.Diffuse.r = Light.Ambient.r = 0.5f;
  Light.Diffuse.g = Light.Ambient.g = 0.5f;
  Light.Diffuse.b = Light.Ambient.b = 0.0f;
  Light.Diffuse.a = Light.Ambient.a = 1.0f;
  Light.Range = 1000.0f;
  Light.Attenuation0 = 0.5f;
  Light.Position.x = 300.0f;
  Light.Position.y = 0.0f;
  Light.Position.z = -600.0f;

  // Set and enable the light
  // Включаем источник света
  g_pD3DDevice->SetLight(0, &Light);
  g_pD3DDevice->LightEnable(0, TRUE);

  return TRUE;
}

BOOL DoShutdown()
{
  // Release vertex buffer
  // Очищаем вершинный буфер
  if(g_pVB != NULL)
    g_pVB->Release();

  // Release device and 3D objects
  if(g_pD3DDevice != NULL)
    g_pD3DDevice->Release();

  if(g_pD3D != NULL)
    g_pD3D->Release();

  return TRUE;
}

BOOL DoFrame()
{
  D3DXMATRIX matWorld;

  // Clear device backbuffer
  g_pD3DDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
                        D3DCOLOR_RGBA(0,0,0,255), 1.0f, 0);

  // Begin scene
  if(SUCCEEDED(g_pD3DDevice->BeginScene())) {

    // Create and set the world transformation matrix
    // Rotate object along Y-axis
	// Создаём и устанавливаем мировую матрицу трансформации
	// Вращаем объект вокруг оси Y
    D3DXMatrixRotationY(&matWorld, (float)timeGetTime() / 1000.0f);
    g_pD3DDevice->SetTransform(D3DTS_WORLD, &matWorld);

    // Set the vertex stream and shader
	// Назначаем поток вершин и шейдер
    g_pD3DDevice->SetStreamSource(0, g_pVB, sizeof(sVertex));
    g_pD3DDevice->SetVertexShader(VERTEXFVF);

    // Draw the vertex buffer
	// Отрисовываем содержимое вершинного буфера в задний буфер
	// (бэкбуфер)
    for(short i=0;i<4;i++)
      g_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, i*4, 2);

    // End the scene
    g_pD3DDevice->EndScene();
  }

  // Display the scene
  g_pD3DDevice->Present(NULL, NULL, NULL, NULL);

  return TRUE;
}

  • Сохрани Решение (Файл -> Сохранить все).
Данный исходный код целиком взят из примера к книге Programming Role-Playing Games with DirectX by Jim Adams (01 Jan 2002). Автор прогал на MS Visual C++ 6.0 (вышла в свет в 1998 г.) c установленным DirecX SDK 8.0. И если в нашем случае в качестве Graphic API мы юзаем практически идентичный DirectX SDK 8.1, то IDE у нас совсем другая (MS Visual C++ 2010 Express Edition). Нет, не с целью усложнения задачи. Просто MS Visual C++ 6.0 в своё время стоила 500 USD. Сейчас её, в принципе, можно поискать в торрентах. (За всё, что ты там скачаешь, администрация Igrocoder.ru ответственности не несёт!) Но использование платного/пиратского софта не соответствует концепции сайта Igrocoder.ru . Поэтому наш выбор:
  • Бесплатная IDE MS Visual C++ 2010 Express Edition;
  • Бесплатный DirectX SDK 8.1 .
К концу данной статьи мы вновь докажем, что игрокодинг (под ОС Windows) в домашних условиях возможен. И для него нужны только компьютер с ОС Windows и доступ к Интернету.
Но вернёмся к нашему Проекту Light01. С момента выхода MS Visual C++ 6.0 прошло немало времени. С MSVC++2010 Express их разделяют аж 12 лет. Из-за этого вышеприведённый код (написанный в начале 2002 г.) на данном этапе в MSVC++2010 Express компилироваться не будет, выдавая многочисленные ошибки. Даже с настроенными путями к DirectX SDK.
Сейчас мы это исправим.

Готовим Проект Light01 к компиляции

Для успешной компиляции изменим настройки (=свойства) текущего Проекта Billboard01, созданного в MSVC++2010. При этом сам код из книги 2002 года останется нетронутым.
Закрыть
noteОбрати внимание

Напомним, что такую настройку необходимо повторно проделывать при создании каждого нового Проекта. Ниже представлен алгоритм действий по настройке Проекта, созданного в MSVC++2010 Express с применением DirectX SDK. При создании приложений под платформы, отличные от Win32, либо применении более новых версий DirectX SDK, процесс конфигурирования Проекта может отличаться от приведённого ниже.


Указываем пути к DirectX SDK 8.1

Если попытаться скомпилировать Проект Light01 в таком виде, то ничего не выйдет.
В единственном файле исходного кода WinMain.cpp можно увидеть инклуды различных заголовочных файлов DirectX (весии 8):
Фрагмент WinMain.cpp
...
// Include files
#include <windows.h>
#include <stdio.h>
#include "d3d8.h"
#include "d3dx8.h"
...

На данном этапе MSVC++2010 ничего не знает об их местоположении. В статье MS Visual Cpp 2010 Express. Установка и указание путей к DirectX SDK мы указывали пути к DirectX SDK (версии 9 и выше). Для DirectX SDK 8 это делается аналогично. Начнём.
  • Убедись, что MSVC++2010 запущена и в ней открыт наш текущий Проект Light01.
  • Убедись, что DirectX SDK 8.1 установлен на компьютере и ты уверенно можешь назвать полный путь к его каталогу.
В Обозревателе решений видим: "Решение "Light01"", а строкой ниже жирным шрифтом название Проекта (тоже Light01).
  • Жмём правой кнопкой мыши по названию Проекта Light01. Во всплывающем меню выбираем пункт "Свойства".
Image
Или в Главном меню выбираем Проект->Свойства. Или нажимаем Alt+F7.
В появившемся меню свойств проекта выбираем Свойства конфигурации -> Каталоги VC+ + . В правой части этой страницы расположены пути ко всевозможным каталогам. Здесь нас интересуют только 2 строки: Каталоги включения и Каталоги библиотек.

Указываем каталог включений (include) DirectX SDK 8.1
  • В меню свойств Проекта щёлкаем левой кнопкой мыши по пункту Каталоги включения. В правой части этой строки видим кнопку с чёрным треугольником, указывающим на наличие выпадающего меню. Нажимаем на неё -> выбираем "Изменить..."
Image
В появившемся меню "Каталоги включения" жмём кнопку "Создать строку" (с жёлтой папкой) и указываем полный путь к заголовочным файлам DirectX SDK 8.1 (include). В нашем случае это C:\DXSDK8\include. Можно просто выбрать каталог из дерева каталогов, нажав кнопку с троеточием, расположенную справа от строки ввода.
Image
  • Жмём "ОК".

Указываем каталог библиотек (lib) DirectX SDK 8.1
  • В меню свойств Проекта щёлкаем левой кнопкой мыши по пункту Каталоги библиотек. В правой части этой строки видим кнопку с чёрным треугольником, указывающим на наличие выпадающего меню. Нажимаем на неё -> выбираем "Изменить..."
  • В появившемся меню "Каталоги библиотек" жмём кнопку "Создать строку" (с жёлтой папкой) и указываем полный путь к 32-разрядным версиям файлов библиотек DirectX SDK 8.1 (lib). В нашем случае это c:\DXSDK8\lib\. Можно просто выбрать каталог из дерева каталогов, нажав кнопку с троеточием, расположенную справа от строки ввода.
Image
  • Жмём "ОК".
  • На Странице свойств тоже жмём "ОК".
  • Сохрани Решение (File -> Save All).
Готово.

Указываем пути к Windows SDK

Помимо указания путей к заголовкам (include) и библиотекам (lib) DirectX SDK, для любого DirectX-Проекта также необходимо указать пути к заголовкам (include) и библиотекам (lib) Windows SDK. Самое смешное, что в MS Visual C++ 2010 эти пути указываются по умолчанию для каждого создаваемого Проекта. В этом нетрудно убедиться, если ещё раз открыть Проект -> Свойства -> Свойства конфигурации - >Каталоги VC++ -> Каталоги включения -> Изменить. В окне "Каталоги включения" в нижней (недоступной для редактирования) части видим список "Унаследованные значения", где в третьей строке стоит значение:
$(WindowsSdkDir)include

В результате видим, что добавленные пути к DirectX SDK (в обоих окнах: include и lib) расположены вверху списка, а пути к Windows SDK - в недоступной области, на несколько строк ниже.
Но! Заголовочным файлам DirectX SDK "жизненно важно", чтобы они включались после включений заголовков Windows SDK.
Закрыть
noteВажно!

Каталоги, пути к которым указаны в окнах "Каталоги включения" и "Каталоги библиотек" при компиляции считываются один за другим по списку сверху вниз. Поэтому для корректного указания путей надо разместить пути к Windows SDK выше, а к DirectX SDK - ниже по списку (чтобы они считывались последними).

В данной ситуации мы не можем поднять пути к Windows SDK, прописанные по умолчанию при создании Проекта, т.к. они расположены в специальной нередактируемой области (ограничение бесплатной версии MSVC++2010 Express). Но можем схитрить и добавить ещё раз пути к тем же самым каталогам Windows SDK, подняв эти строки выше строк DirectX SDK.
Выше мы указывали пути к DirectX SDK. Для путей к Windows SDK это делается аналогично. Начнём.
  • Убедись, что MSVC++2010 запущена и в ней открыт Проект, с которым работаешь в данный момент.
  • Убедись, что Windows SDK (в нашем случае версия 7) установлен на компьютере и ты уверенно можешь назвать полный путь к его каталогу.
В Обозревателе решений видим: "Решение "..."", а строкой ниже жирным шрифтом название Проекта с тем же названием (если не менял вручную).
  • Жмём правой кнопкой мыши по названию Проекта. Во всплывающем меню выбираем пункт "Свойства".
Image
Или в Главном меню выбираем Проект->Свойства. Или нажимаем Alt+F7.
В появившемся меню свойств проекта выбираем Свойства конфигурации -> Каталоги VC++ . В правой части этой страницы расположены пути ко всевозможным каталогам. Здесь нас, как и в прошлый раз, интересуют только 2 строки: Каталоги включения и Каталоги библиотек.

Указываем каталог включений (include) Windows SDK
  • В меню свойств Проекта щёлкаем левой кнопкой мыши по пункту Каталоги включения. В правой части этой строки видим кнопку с чёрным треугольником, указывающим на наличие выпадающего меню. Нажимаем на неё -> выбираем "Изменить..."
Image
В появившемся меню "Каталоги включения" жмём кнопку "Создать строку" (с жёлтой папкой) и указываем полный путь к заголовочным (include) файлам Windows SDK. В нашем случае (Win7 x64) это C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Include. Можно просто выбрать каталог из дерева каталогов, нажав кнопку с троеточием, расположенную справа от строки ввода.
Image
  • Меняй порядок считывания каталогов включений с помощью кнопок с чёрными стрелками в верхней части окна "Каталоги вложений".
Каталог включений DirectX SDK должен всегда стоять в самом конце списка, как на этом скриншоте:
Image
  • Жмём "ОК".

Указываем каталог библиотек (lib) Windows SDK
  • В меню свойств Проекта щёлкаем левой кнопкой мыши по пункту Каталоги библиотек. В правой части этой строки видим кнопку с чёрным треугольником, указывающим на наличие выпадающего меню. Нажимаем на неё -> выбираем "Изменить..."
  • В появившемся меню "Каталоги библиотек" жмём кнопку "Создать строку" (с жёлтой папкой) и указываем полный путь к папке с файлами библиотек (lib) Windows SDK. В нашем случае (Win7 x64) это C:\Program Files (x86)\MicrosoftSDKs\Windows\v7.0A\Lib. Можно просто выбрать каталог из дерева каталогов, нажав кнопку с троеточием, расположенную справа от строки ввода.
Image
  • Меняй порядок считывания каталогов вложений с помощью кнопок с чёрными стрелками в верхней части окна.
Каталог библиотек DirectX SDK должен всегда стоять в самом конце списка, как на этом скриншоте:
Image
  • Жмём "ОК".
  • На Странице свойств тоже жмём "ОК".
  • Сохрани Решение (File -> Save All). Готово.

Выбираем многобайтовую кодировку

Закрыть
noteПримечание

В MS Visual C++ 2010 в настройках по умолчанию стоит набор (кодировка) символов UNICODE. В MS Visual C++ 6.0 - напротив, по умолчанию стоит кодировка ANSI (многобайтовая). Данная настройка сильно влияет на типы используемых переменных, что приводит к заметным различиям в исходном коде.
Несмотря на то, что во всех случаях рекомендуется использовать кодировку UNICODE, поддерживаемую во всех современных ОС семейства MS Windows (начиная с Win 2000/XP), большинство книг по программированию игр на классическом C++ придерживаются именно многобайтовой кодировки. Чтобы сильно не переделывать исходные коды под UNICODE, данный Проект мы настроим под многобайтовую кодировку.

Чтобы сильно не переделывать исходные коды под UNICODE, все наши игровые Проекты мы настроим под многобайтовую кодировку. Для этого...
  • Убедись, что MSVC++2010 запущена и в ней открыт наш текущий Проект Light01.
В Обозревателе решений видим: "Решение "Light01"", а строкой ниже жирным шрифтом название Проекта (тоже Light01).
  • В Обозревателе решений щёлкаем правой кнопкой мыши по названию Проекта Light01.
  • Во всплывающем контекстном меню выбираем "Свойства".
  • В появившемся окне установки свойств Проекта жмём Свойства конфигурации->Общие, в правой части в строке "Набор символов" выставляем значение "Использовать многобайтовую кодировку".
Image
  • Жмём ОК.
  • Сохрани Решение (Файл->Сохранить все)

Отключаем инкрементную компоновку (incremental linking)

Инкрементная компоновка призвана сократить время компилирования. Но на деле её присутствие часто вызывает ошибки вроде этой:
Error LNK1123: сбой при преобразовании в COFF: файл недопустим или поврежден


Закрыть
warningОшибка LINK : fatal error LNK1123: failure during conversion to COFF: file invalid or corrupt

В MS Visual C++ 2010 даже компиляция консольных приложений нередко завершается неудачей, а вместо исполняемого файла программист видит сообщение:
LINK : fatal error LNK1123: failure during conversion to COFF: file invalid or corrupt.

ПРИЧИНА: MS Visual С++ 2010 "не нравится" версия .NET Framework(external link), установленная в операционной системе. В общих чертах, MS Visual С++ 2010 спрограммирована для работы под управлением .NET Framework 4.0 и сильно к нему привязана. Если точнее, к нему сильно привязана система инкрементной компоновки приложений (incremental linking), которая по умолчанию включена для всех создаваемых проектов.
Во время установки MS Visual C++ 2010 пытается установить свой "родной" .NET Framework 4.0, проверяя версию этой программной платформы, установленную в ОС на данный момент. Если версия .NET Framework ниже 4.0, то она обновляется до 4.0 и всё прекрасно компилируется. Если версия .NET Framework выше 4.0, то всё оставляется как есть: IDE успешно завершает установку, но при компиляции ВСЕХ приложений выскакивает данная ошибка. Более того, ошибка была замечена даже при наличии в системе .NET Framework версии 4.0, но отличающейся от "родной" припиской вроде "Beta" или "Release Candidate".

ВАРИАНТЫ РЕШЕНИЙ:
1. Отключить инкрементную линковку в опциях Проекта.
В главном меню MSVC++2010 выбираем: Проект->Свойства->Свойства конфигурации->Компоновщик(Linker)->Включить инкрементное построение (Incremental Linking). Данный пункт по умолчанию включен для всех новых проектов. Выставляем его в Нет (No). И жмём OK. Инкрементное построение заметно сокращает время компилирования больших проектов. Но в нашем случае его отсутствие некритично.
2. Удалить (переместить в другое место) утилиту cvtres.exe из каталога bin установленной MS Visual C++ 2010.
В нашем случае (Win7 x64) полный путь до данного файла такой: C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\cvtres.exe . Официальное название данного приложения - Microsoft® Resource File To COFF Object Conversion Utility (утилита конвертации файлов двоичных ресурсов в Component Object File Format). Опытным путём установлено, что при линковке IDE "спотыкается" именно об него.
3. Удалить из системы все версии .NET Framework (включая языковые пакеты и всякие профайлеры, если есть) и саму MS Visual C++ 2010.
Всё вышеперечисленное можно без труда найти в меню "Программы и компоненты" (MS Windows Vista/7/8). Затем заново установить MS Visual C++ 2010. При этом автоматом установится .NET Framework 4.0, идущий с ней в наборе.

Третий пункт - самый долгий. На деле почти всегда хватает выполнения первых двух. Данным вопросом озадачивались ребята здесь: https://www.cyberforum.ru/cpp-beginners/thread637174.html?ysclid=l3akpmanrq(external link). После этого линковка (=компоновка) проходит идеально. Подобные "костыли" в IDE от Майков - не редкость. Можно предположить, что команда с головой ударилась в тестирование .NET-возможностей MSVC++2010, совсем забыв о Win32-направлении (либо признав его бесперспективным).


Отключим инкрементную компоновку в свойствах открытого Проекта. Для MS Visual C++ 2010 порядок следующий:
  • Убедись, что MSVC++2010 запущена и в ней открыт Проект, с которым работаешь.
В Обозревателе решений видим: "Решение "..."", а строкой ниже жирным шрифтом название Проекта. Обычно с тем же названием (если не менял вручную).
  • В Обозревателе решений щёлкаем правой кнопкой мыши по названию Проекта.
  • Во всплывающем контекстном меню выбираем "Свойства".
  • В появившемся окне установки свойств Проекта жмём Свойства конфигурации -> Компоновщик -> Общие (Configuration Properties -> Linker -> General), в правой части в строке "Включить инкрементную компоновку" ставим значение Нет (/INCREMENTAL:NO).
  • Жмём ОК.

Удаляем файл cvtres.exe

В нашем случае (Win7 x64) полный путь до данного файла такой: C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\cvtres.exe
В Win32-кодинге он особо ни на что не влияет. Зато без него линковка идёт как по маслу.

Прописываем библиотеки d3dx8.lib, d3d8.lib и Winmm.lib в окне "Дополнительные зависимости" (Additional dependencies) компоновщика (Linker)

Данный этап проходил даже автор кода Jim Adams в 2002 году, прогая на MSVC++6.0. В начальных комментариях листинга WinMain.cpp он намекнул, что библиотеки D3D8.LIB и D3DX8.LIB необходимо явно указывать в списке дополнительных зависимостей линкера ( = компоновщика):
Фрагмент WinMain.cpp
...
Required libraries:
  WINMM.LIB, D3D8.LIB and D3DX8.LIB
...

Библиотеки D3D8.LIB и D3DX8.LIB расположены в папке с установленным DirectX SDK 8 (в нашем случае по пути C:\DXSDK8\lib\), пути к которой мы прописали выше.
Т.к. мы создаём исполняемое приложение (исполняемый .exe-файл), а не библиотеку, то после компиляции полученный объектный модуль сразу линкуется путём вызова компоновщика (=linker). Так вот, этот самый компоновщик по ранее прописанным каталогам данные библиотеки не ищет. Поэтому их необходимо указывать отдельно в окне настроек Проекта, в разделе "Компоновщик". ОК, начинаем.
  • Убедись, что MSVC++2010 запущена и в ней открыт наш текущий Проект Light01.
  • В Главном меню MS Visual C++ 2010 выбираем Проект -> Свойства (Project -> Properties).
  • В появившемся окне установки свойств Проекта последовательно щёлкаем по раскрывающимся ветвям иерархического дерева: Свойства конфигурации -> Компоновщик -> Ввод (Configuration Properties -> Linker -> Input).
  • В правой части, напротив строки "Дополнительные зависимости" жмём кнопку с чёрным треугольником.
  • В всплывающем списке жмём "Изменить".
  • В появившемся окне "Дополнительные зависимости" в верхнем поле ввода прописываем в столбик (один под другим) имена файлов трёх библиотек:
d3dx8.lib
d3d8.lib
Winmm.lib
Image
Библиотека Winmm.lib отвечает за мультимедиа-возможности приложения и расположена в каталоге с установленным Windows SDK.
  • Жмём ОК, ОК.
  • Сохрани Решение (Файл->Сохранить все)

Отключаем использование компоновщиком библиотеки libci.dll

Да, даже на данном этапе компиляция Проекта выдаст ошибку. Библиотека libci.dll использовалась в VisualStudio когда-то очень давно, и в современных версях IDE её нет. Тем не менее компоновщик почти всегда вызывает её при компиляции, ругаясь на её отсутствие. Самый простой способ это исправить - запретить использовать libci.dll по умолчанию.
ОК, начнём.
  • Убедись, что MSVC++2010 запущена и в ней открыт Проект, с которым работаешь в данный момент.
  • В Главном меню MS Visual C++ 2010 выбираем Проект -> Свойства (Project -> Properties).
  • В появившемся окне установки свойств Проекта последовательно щёлкаем по раскрывающимся ветвям иерархического дерева: Свойства конфигурации -> Компоновщик -> Командная строка (Configuration Properties -> Linker -> Command Promt).
В правой части, внизу, видим поле ввода "Дополнительные параметры".
  • Пишем в него строку: /NODEFAULTLIB:libci
Image
  • Жмём ОК.
  • Сохрани Решение (Файл->Сохранить все).

Компилируем Проект Light01

Наконец, наш тестовый Проект готов к компиляции.
  • Жми кнопку с зелёным треугольником на панели инструментов главного окна MSVC++2010 или F5 на клавиатуре.
Image
Скомпилированное .exe-приложение в нашем случае (Win7 x64) расположено по пути C:\Users\User1\Documents\Visual Studio 2010\Projects\Light01\Debug .
После компилирования приложение Light01 автоматически запустится и покажет окно с жёлтым вращающимся параллелепипедом.

Заключение

Свет - один из ключевых компонентов в любой игре. С помощью общедоступных и совершенно бесплатных программных средств мы создали графическое приложение.
Напомним, что, если видеокарта на компе игрока не поддерживает выбранный световой эффект, то последний будет сэмулирован программно (software emulation) с возложением всех расчётов на центральный процессор (ЦП). В этом нет ничего плохого, т.к. игрок всё-таки сможет играть в игру. Но программная эмуляция (в том числе) световых эффектов сильно нагружает ЦП и снижает общую производительность. В то же время, сегодня (2022 год) оч трудно найти компьютер с видеокартой, не поддерживающей световые эффекты DirectX 8. Если только это не жёсткий "китай". Поэтому юзай источники света грамотно и сведи их кол-во к необходимому минимуму. Все примеры из DirectX SDK 8 компилируются таким же образом.

Задание на дом

В папке с примерами (samples) DirectX SDK 8 есть пример "Lighting", также демонстрирующий технику работы со светом. В нашем случае путь до него такой: C:\DXSDK8\samples\Multimedia\Direct3D\Lighting.
  • Найди в папке с данным примером файл исходного кода lighting.cpp.
  • Создай новый пустой Проект Light02 в MSVC++2010 и скомпилируй исходный код, содержащийся в lighting.cpp (просто скопируй из него и вставь в исходник WinMain.cpp своего Проекта в MSVC++2010).
  • Изучи исходные коды примеров по данной теме Джима Адамса и MS DirectX SDK 8, выявив сходства и различия.

Источники


1. Adams J. Programming Role Playing Games with DirectX 8.0. - Premier Press, 2002


Последние изменения страницы Четверг 26 / Май, 2022 11:20:18 MSK

Последние комментарии wiki

No records to display

Search Wiki Page

Точное совпадение

Категории

|--> C#
|--> C++