Китай |
Использование 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-файла
- Сдайте работу преподавателю