| Украина, Киев |
Использование Mesh-объектов в DirectX
Загрузка Mesh-объектов из внешних файлов
Отображение заварочных чайников выглядит вполне реалистичным, но большинство Mesh -объектов создают художники и редко используются рассмотренные выше примитивы (куб, тор, сфера и т.д.). Существуют вспомогательные программы-утилиты, которые позволяют создавать и сохранять Mesh -объекты в файлах X -форматов. Затем класс Mesh с помощью своих методов может загружать в приложение эти объекты из файлов и управлять ими. В X -файл можно поместить все необходимые данные для реалистичного рендеринга объекта, включая вершинные и индексные данные, информацию о материале и текстуре.
Для быстрой и реалистичной обработки трехмерных графических объектов существуют файлы с программным кодом, который напрямую работает с графическим устройством. В этих файлах находится код, который написан на специальном языке, называемом HLSL ( High Level Shader Language - высокоуровневый язык программирования для построения теней). Программы, написанные на этом языке, называются шейдерами, а файлы, в которых хранятся шейдеры - файлами шейдеров.
Кроме статических методов создания графических примитивов (куб, тор, ...) в классе Mesh содержатся еще два статических метода Mesh.FromFile() и Mesh.FromStream(). Эти методы имеют большое количество перегрузок, но корневые перегрузки выглядят так
-
public static Microsoft.DirectX.Direct3D.Mesh FromFile ( System.String filename , // Источник данных Microsoft.DirectX.Direct3D.MeshFlags options , // Как нужно загружать данные Microsoft.DirectX.Direct3D.Device device , // Устройство Microsoft.DirectX.Direct3D.GraphicsStream adjacency , // Смежные вершины out Microsoft.DirectX.Direct3D.ExtendedMaterial[] materials , // Сохраненные материалы Microsoft.DirectX.Direct3D.EffectInstance effects // Файл шейдера для поддержки Mesh-объекта ) -
public static Microsoft.DirectX.Direct3D.Mesh FromStream ( System.IO.Stream stream , // Источник данных System.Int32 readBytes , // Сколько байт загружать из потока Microsoft.DirectX.Direct3D.MeshFlags options , // Как нужно загружать данные Microsoft.DirectX.Direct3D.Device device , // Устройство Microsoft.DirectX.Direct3D.GraphicsStream adjacency , // Смежные вершины out Microsoft.DirectX.Direct3D.ExtendedMaterial[] materials , // Сохраненные материалы Microsoft.DirectX.Direct3D.EffectInstance effects // Файл шейдера для поддержки Mesh-объекта )
Параметр options может быть представлен поразрядной комбинацией значений. Из множества перегрузок нужно выбирать такой метод загрузки данных, для которого имеется вся необходимая информация.
Для демонстрации загрузки Mesh -данных из файла создадим форму Form2
-
Закройте
все документы текущего редактирования командой оболочки Window/Close All Documents
-
Через
панель Solution Explorer скопируйте файл Form1.cs и
переименуйте его в файл Form2.cs
-
Откройте
файлы Form2.cs (в режиме View
Code )
и Form2.designer.cs и
замените в них все вхождения Form1 на Form2 с
помощью окна замены, вызываемого комбинацией клавиш Ctrl-H (окно
замены должно быть настроено в соответствии со снимком:
на поиск вхождений в открытых документах)
-
Установите
через меню Project/RenderMesh Properties форму Form2 стартовой,
постройте приложение и убедитесь, что форма Form2 сохранила
функциональность Form1
Теперь пришла пора добавить в форму Form2 код, который будет загружать сложный Mesh -объект из X-файла.
-
Добавьте
в класс Form2 объявления полей-ссылок
объектов материалов и текстур, которые будут адресовать
соответствующие данные, загруженные из X-файла
private Device device = null;
private Mesh mesh = null;
// Ссылки на массивы для загрузки из X-файла
private Material[] meshMaterials;
private Texture[] meshTextures;
public void InitializeGraphics()
{
// .....................................................
}
Листинг
14.16.
Добавление в класс Form2 ссылочных переменных
-
Создайте
функцию-член класса Form2 для загрузки Mesh -объекта
из файла (аргументом функции будет имя файла). Назовите
функцию LoadMesh(),
она будет возвращать ссылку на загруженный Mesh -объект.
// Функция загрузки Mesh-объекта из файла
private Mesh LoadMesh(string file)
{
ExtendedMaterial[] data; // Создаем ссылку на загружаемые данные
// Загружаем объект из файла
Mesh localMesh = Mesh.FromFile(file, MeshFlags.Managed, device, out data);
// Если есть данные - разделяем и сохраняем их
if ((data != null) && (data.Length > 0))
{
// Создаем массив материалов
meshMaterials = new Material[data.Length];
// Создаем массив текстур
meshTextures = new Texture[data.Length];
// Заполняем массив материалами и текстурами
for (int i = 0; i < data.Length; i++)
{
meshMaterials[i] = data[i].Material3D;
if ((data[i].TextureFilename != null)
&& (data[i].TextureFilename != string.Empty))
{
// Структура существует, попробуем ее загрузить
meshTextures[i] = TextureLoader.FromFile(
device, data[i].TextureFilename);
}
}
}
return localMesh;
}
Листинг
14.17.
Функция загрузки Mesh-объекта из файла
-
Удалите
из класса функцию DrawCubes() на ее
место вставьте новую функцию рисования объекта DrawMesh()
private void DrawMesh()
{
// Вращаем объект
device.Transform.World = Matrix.RotationYawPitchRoll(
angle / (float)Math.PI,
angle / (float)Math.PI * 2.0f,
angle / (float)Math.PI / 4.0f) *
Matrix.Translation(0.0f, 0.0f, 0.0f);
// Загружаем в устройство материал и текстуру поверхности отражения
for (int i = 0; i < meshMaterials.Length; i++)
{
device.Material = meshMaterials[i];
device.SetTexture(0, meshTextures[i]);
mesh.DrawSubset(0);
}
if (flagRotate)
angle += 0.02F;
}
Листинг
14.18.
Новая функция DrawMesh() рисования Mesh-объекта
-
Измените
в функции OnPaint() вызов функции рисования
protected override void OnPaint(PaintEventArgs e)
{
// Очистить цветом клиентскую область формы
device.Clear(ClearFlags.Target,
System.Drawing.Color.CornflowerBlue,
1.0F, 0);
// Вызов нашей функции установки камеры
SetupCamera();
// Сформировать сцену
device.BeginScene();
DrawMesh();
device.EndScene();
// Показать буфер кадра
device.Present();
// Принудительно перерисовать
this.Invalidate();
}
Листинг
14.19.
Вызов DrawMesh() в функции OnPaint()
-
Чтобы
отображаемый объект не казался слишком большим, замените
в функции SetupCamera() настройки масштаба
изображения камеры следующими значениями
// Установка камеры в сцену
private void SetupCamera()
{
//Создание перспективы
device.Transform.Projection = Matrix.PerspectiveFovLH(
(float)Math.PI / 4, // Угол зрения равен 45 градусов
// Форматное соотношение сторон
(float)this.ClientSize.Width / (float)this.ClientSize.Height,
1.0F, // Ближний план
1000.0F); // Дальний план
//Добавление камеры
device.Transform.View = Matrix.LookAtLH(
new Vector3(0, 0, 700.0F), // Положение камеры
new Vector3(), // Положение объекта текущее
new Vector3(0, 1, 0)); // Направление камеры
// Освещение
device.RenderState.Lighting = true;
// Установить красный свет
device.RenderState.Ambient = Color.Red;
// Настроить общий свет
device.Lights[0].Type = LightType.Directional;
device.Lights[0].Diffuse = Color.White;
device.Lights[0].Direction = new Vector3(0, -1, -1);
device.Lights[0].Commit();
device.Lights[0].Enabled = true;
}
Листинг
14.20.
Изменение масштаба изображения камеры в функции SetupCamera()
-
Удалите
из функции InitializeGraphics() код генерации
куба и вместо него поставьте вызов функции LoadMesh() загрузки Mesh -объекта
из файла
public void InitializeGraphics()
{
// Создание объекта и настройка параметров представления
// Создать объект параметров представления
PresentParameters presentParams = new PresentParameters();
// Установить оконный режим
presentParams.Windowed = true;
// Сбрасывать содержимое буфера, если он не готов к представлению
presentParams.SwapEffect = SwapEffect.Discard;
// Создать объект устройства и сохранить ссылку на него
device = new Device(0, DeviceType.Hardware, this,
CreateFlags.SoftwareVertexProcessing, presentParams);
// Генерация и подключение к устройству куба
// с помощью статического метода класса Mesh
mesh = Mesh.Box(device, 2.0F, 2.0F, 2.0F);
// Загрузить Mesh-объект из файла tiny.x
mesh = LoadMesh("tiny.x");
}
Листинг
14.21.
Замена генерации куба на функцию LoadMesh() загрузки Mesh-объекта из файла
-
В
панели Solution Explorer вызовите контекстное
меню для узла проекта и командой Add/Existing Item добавьте
к проекту из прилагаемого к работе каталога Source файлы tiny.x и Tiny_skin.bmp,
предварительно настроив фильтр окна подкачки
-
В
панели Solution Explorer выделите одновременно
скопированные оболочкой в проект файлы и щелчкните по пиктограмме Properties, чтобы вызвать для них панель настройки свойств
оболочки
-
Настройте
панель свойств в соответствии со снимком, чтобы оболочка
при компиляции копировала в каталог bin размещения
сборки самые свежие версии указанных файлов
-
Запустите
приложение и обратите внимание, что некоторые части объекта
не закрываются передним планом
Это происходит потому, что мы ранее удалили буфер глубины из параметров устройства. Исправим это:
-
Добавьте
буфер глубины в функцию InitializeGraphics() перед
созданием устройства
public void InitializeGraphics()
{
// Создание объекта и настройка параметров представления
// Создать объект параметров представления
PresentParameters presentParams = new PresentParameters();
// Установить оконный режим
presentParams.Windowed = true;
// Сбрасывать содержимое буфера, если он не готов к представлению
presentParams.SwapEffect = SwapEffect.Discard;
// Настройка буфера глубины для параметров устройства
presentParams.EnableAutoDepthStencil = true;
presentParams.AutoDepthStencilFormat =
Microsoft.DirectX.Direct3D.DepthFormat.D16;
// Создать объект устройства и сохранить ссылку на него
device = new Device(0, DeviceType.Hardware, this,
CreateFlags.SoftwareVertexProcessing, presentParams);
// Генерация и подключение к устройству куба
// с помощью статического метода класса Mesh
mesh = Mesh.Box(device, 2.0F, 2.0F, 2.0F);
// Загрузить Mesh-объект из файла tiny.x
mesh = LoadMesh("tiny.x");
}
Листинг
14.22.
Добавление в функцию InitializeGraphics() буфера глубины
-
Добавьте
в функцию OnPaint() флаг очистки устройства
с учетом существования буфера глубины
protected override void OnPaint(PaintEventArgs e)
{
// Очистить цветом клиентскую область формы
device.Clear(ClearFlags.Target
| ClearFlags.ZBuffer,
System.Drawing.Color.CornflowerBlue,
1.0F, 0);
// Вызов нашей функции установки камеры
SetupCamera();
// Сформировать сцену
device.BeginScene();
DrawMesh();
device.EndScene();
// Показать буфер кадра
device.Present();
// Принудительно перерисовать
this.Invalidate();
}
Листинг
14.23.
Добавление флага существования буфера глубины в функцию OnPaint()
-
Запустите
приложение и
полюбуйтесь на сложный вращающийся Mesh -объект,
который мы загрузили из внешнего X-файла
-
Сдайте
работу преподавателю





