1. Введение в меши
----------------------------------------------------------------------------------------------------------------------------
Приступать к изучению этого материала желательно после знакомства с уроками:
2. Работа с освещением
С исходного кода этого урока мы и начнем. Также для работы понадобятся некоторые файлы, которые можно скачать здесь.
----------------------------------------------------------------------------------------------------------------------------
В 3D графике мешем является любой рисуемый трехмерный объект. Получил он свое название от английского mesh (сетка), потому что трехмерный объект состоит из точек, вершин, соединенных линиями, в результате выглядит как сеть.
С простыми мешами мы уже работали на предыдущих уроках, создавая куб.
//Создаем куб
TEntity cube = LE.CreateCube();
Здесь созданный куб присваивался переменной, имеющей тип TEntity. Но ничто не мешает нам явно указать, что это меш, подставив тип TMesh.
//Создаем куб
TMesh cube = LE.CreateCube();
Постольку TEntity является базовым для TMesh, никаких проблем в коде такое переименование не вызовет, а мы будем точно знать, что работаем с мешем.
Помимо создания графических примитивов, вроде куба, Leadwerks engine позволяет загружать ранее созданные с помощью 3D редакторов меши из файлов или же собирать их вручную из вершин и полигонов. Изучением всех этих возможностей мы сейчас и займемся.
Leadwerks Engine умеет загружать такие форматы трехмерной графики: OBJ, MD3, 3DW, GMF. Разработчики движка рекомендуют GMF.
Распакуйте из указанной в шапке этой статьи ссылки файлы и поместите их в свой проект к исполняемому файлу.
В архиве находятся 4 файла:
normal_map.dds – текстура, содержащая карту нормалей;
color_map.dds – текстура, которая будет наложена на меш;
brick.mat – информация об используемом для меша материале, в данном случае о текстурах и шейдерах; О материалах мы еще поговорим.
box.gmf – файл, содержащий информацию о меше.
Чтобы загрузить box.gmf нужно добавить в код перед главным циклом следующую функцию:
//Загружаем меш из файла
TMesh cube2 = LE.LoadMesh("box.gmf");
Будьте осторожны. Если вы случайно поставите функцию загрузки в главный цикл, то меш будет загружаться в каждом кадре, игра начнет тормозить и, в конце концов, повиснет.
Скомпилировав и запустив программу, мы увидим следующее:
Кроме уже имеющегося вращающегося куба появился еще один. Получилась интересная фигура, но такое совмещение нам не нужно. Сдвинем немного вращающийся куб, добавив в код сразу после его создания функцию изменения позиции меша:
LE.PositionEntity(cube, LE.Vec3(-2,0,0));
Теперь объекты не накладываются друг на друга:
Хватит уже наблюдать за невзрачными серыми кубиками, пора делать их красивее. Для этого загрузим файл материалов brick.mat.
// Загружаем материал
TMaterial mat = LE.LoadMaterial("brick.mat");
Материалы в движке представлены типом TMaterial.
И применяем его на оба наших куба, не забыв при этом проверить, действительно ли материал загрузился:
if (mat!=null)
{
//Применяем материал к мещам
LE.PaintEntity(cube, mat);
LE.PaintEntity(cube2, mat);
}
Если вы все сделали правильно, то при запуске вас ждет такая картина:
Ну и весь код, который имеется на текущий момент:
class Program
{
static void Main(string[] args)
{
// подключение dll движка
LE.Initialize();
//Создание окна для вивода графики
LE.Graphics(640, 480, 0, 0, LE.GRAPHICS_BACKBUFFER + LE.GRAPHICS_DEPTHBUFFER);
//Создаем мир. Это делается в первую очередь.
LE.CreateWorld();
//Создаем камеру
TEntity cam = LE.CreateCamera();
// Устанавливаем координаты камеры
LE.MoveEntity(cam, LE.Vec3(0, 0, -5));
//Создаем куб
TMesh cube = LE.CreateCube();
LE.PositionEntity(cube, LE.Vec3(-2,0,0));
//Загружаем меш из файла
TMesh cube2 = LE.LoadMesh("box.gmf");
// Загружаем материал
TMaterial mat = LE.LoadMaterial("brick.mat");
if (mat!=null)
{
//Применяем материал к мещам
LE.PaintEntity(cube, mat);
LE.PaintEntity(cube2, mat);
}
//Создаем источник света
TEntity light = LE.CreateSpotLight();
//устанавливаем позицию
LE.PositionEntity(light, LE.Vec3(2, 2, -2));
LE.RotateEntity(light, LE.Vec3(45, 45, 0));
//создаем буфер
TBuffer buffer = LE.CreateBuffer(800, 600, LE.BUFFER_COLOR0 | LE.BUFFER_DEPTH | LE.BUFFER_NORMAL);
//Создаем куб
TMesh ground = LE.CreateCube();
//Меняем размеры и позицию
LE.ScaleMesh(ground, LE.Vec3(10, 0.1f, 10));
LE.PositionEntity(ground, LE.Vec3(0, -2, 0));
//Визуализация источников света
// LE.DebugLights(1);
//Главный цикл
while (!LE.KeyHit(Keys.KEY_ESCAPE)) // пока не нажата ESCAPE
{
//Вращаем куб
LE.TurnEntity(cube, LE.Vec3(0.5f));
//Обновляем мир
LE.UpdateWorld(1);
//Сделать созданный буфер активным.
LE.SetBuffer(buffer);
//Рисуем мир
LE.RenderWorld(LE.RENDER_ALL);
//Деактивировать свой буфер, вернув предыдущий
LE.SetBuffer(LE.BackBuffer());
//Провести расчет и отрисовку изображения на основе своего буфера
LE.RenderLights(buffer);
//Переключаем буфер
LE.Flip(1);
}
//Отключаем движок и выгружаем dll из памяти
LE.Terminate();
}
}
Ручное создание мешей
Ну а теперь приступим к довольно сложной теме – созданию мешей вручную. В реальных приложениях она не особо затребована, предпочитают пользоваться готовыми мешами, но все же бывают ситуации, что делать это приходится. Например нужно немного изменить существующий меш, также таким способом в играх создается след от взмаха оружием, генерируется случайный ландшафт и многое другое.
Как вы уже знаете, меш строится из точек называемых вершинами(или вертексами), три таких вершины образуют плоскость - полигон. У полигона имеются такие параметры как текстурные координаты, используемые при наложении текстур, и нормаль – перпендикуляр к плоскости полигона, указывающий направление отражения света. Более подробное объяснение терминов 3d графики советую поискать в интернете.
Так вот, самое сложное в ручной генерации – правильно задать все вершины с их параметрами.
Для начала сдвинем загруженный куб, чтобы он не загораживал обзов.
LE.PositionEntity(cube2, LE.Vec3(2, 0, 0));
Теперь создадим меш.
//Создаем пустой меш
TMesh cubScratch = LE.CreateMesh();
Этот меш полностью пуст, и не имеет никакой информации для отображения. Необходимо в меше создать поверхность (surface), к которой будут добавляться вершины и применятся материалы. Меш может содержать несколько поверхностей. Все полигоны одной поверхности используют один материал и рисуются одним пакетом за одно обращение к GPU.
//создаем поверхность
TSurface surf = LE.CreateSurface(cubScratch);
Теперь добавим к поверхности вершины:
//Добавляем вершины
LE.AddVertex(surf, LE.Vec3(-0.5f, 0, 0));
LE.AddVertex(surf, LE.Vec3(0.5f, 1, 0));
LE.AddVertex(surf, LE.Vec3(0.5f, 0, 0));
Вершина это всего лишь точка, поэтому она задается тремя координатами Vec3(x, y, z). Далее объединяем вершины в полигон:
// Создаем полигон
LE.AddTriangle(surf, 0, 1, 2);
Эта функция принимает номер вершины. Вершины нумеруются в порядке их создания, отсчет начинается от 0.
После редактирования меша нужно вызвать функцию UpdateMesh.
//Обновляем меш
LE.UpdateMesh(cubScratch);
Эта функция обновляет вспомогательные внутренние данные меша, например такие как ограничивающий прямоугольник.
Запустив код мы увидим невзрачный треугольник.
Применим к созданному мешу загруженный ранее материал и запустим программу.
LE.PaintEntity(cubScratch, mat);
Почти ничего не изменилось, разве что цвет треугольника, а вот красивой текстуры кирпича нет. Это потому, что меш не имеет текстурных координат и нормалей.
Добавьте этот код перед обновлением созданного меша:
//Добавляем нормали
LE.SetVertexNormal(surf, 0, LE.Vec3(0, 0, -1));
LE.SetVertexNormal(surf, 1, LE.Vec3(0, 0, -1));
LE.SetVertexNormal(surf, 2, LE.Vec3(0, 0, -1));
// Добавляем текстурные координаты
LE.SetVertexTexCoords(surf, 0, LE.Vec2(0, 1));
LE.SetVertexTexCoords(surf, 1, LE.Vec2(1, 0));
LE.SetVertexTexCoords(surf, 2, LE.Vec2(1, 1));
Нормали в движке представлены трехмерным вектором, так как размещаются в трехмерном пространстве, а текстурные координаты двухмерным, поскольку используются для наложения плоских двухмерных текстур. Вершина, для которой они применяются, задается порядковым номером, как и при создании полигона.
После внесенных изменений текстура будет наложена правильно.
Далее мы можем полностью построить куб. Для этого понадобится 24 вершины. Код довольно большой и однообразный. Главное правильно задать координаты. Его я приводить здесь не буду, скачивайте по этой ссылке.
После его запуска можно увидеть три вращающиеся кирпичных куба. Я добавил всем код вращения.
На скриншоте можно увидеть три одинаковых куба, но все они были созданы разными способами. Первый – функцией создания примитивов, второй – вручную из вершин, третий был загружен из файла, созданного в 3d редакторе.
|