Украина, Киев |
Рендеринг вращающихся кубов в DirectX
Текстурирование сторон вращающегося куба
Нанесем на стороны куба какие-нибудь рисунки. Это могут быть любые плоские bitmap -рисунки. Для текстурирования объектов в Direct3D используется понятие тексела (texel). Тексел - сокращенное название элемента текстуры или соответствующее значение цвета для каждого адреса в текстуре. Адресом в текстуре служат координаты левого верхнего угла прямоугольника текстуры (обычно 0,0) и правого нижнего угла (обычно 1, 1).
Для того, чтобы отобразить кубический объект с использованием текстурирования, нужно изменить вершинный формат объекта, а также данные, пересылаемые в графическую карту. Мы заменим компонент цвета вершин примитивов координатами текстуры. Для того, чтобы получить куб и цветным и текстурным, будем просто использовать текстуру для определения цвета каждого примитива.
-
В панели Solution Explorer проекта получите через контекстное меню копию файла Form1.cs и переименуйте эту копию в файл Form3.cs
-
Вызовите комбинацией клавиш Ctrl-H окно замены текстового редактора для файла Form3.cs и замените все вхождения Form1 на Form3
-
Откройте файл Form3.designer.cs и выполните в нем те же самые замены
-
Через меню Project откройте окно свойств проекта и установите на вкладке Application в качестве стартового объект класса Form3
-
Запустите приложение, чтобы убедиться, что версия класса Form3 работоспособна и равнозначна по функциональности Form1
На этом подготовительная часть задачи текстурирования сторон куба завершена и можно приступать к основной части.
-
Измените тело обработчика vb_Created() так, как показано ниже
private void vb_Created(object sender, EventArgs e) { // Определить внутреннюю ссылку на вершинный буфер VertexBuffer buffer = (VertexBuffer) sender; // Явное приведение типов // Создать локальный массив структур непреобразованных координат CustomVertex.PositionTextured[] verts = new CustomVertex.PositionTextured[36]; // Задать параметры аппроксимирующих треугольников verts[0] = new CustomVertex.PositionTextured(-1.0F, 1.0F, 1.0F, 0.0F, 0.0F); verts[1] = new CustomVertex.PositionTextured(-1.0F, -1.0F, 1.0F, 0.0F, 1.0F); verts[2] = new CustomVertex.PositionTextured(1.0F, 1.0F, 1.0F, 1.0F, 0.0F); verts[3] = new CustomVertex.PositionTextured(-1.0F, -1.0F, 1.0F, 0.0F, 1.0F); verts[4] = new CustomVertex.PositionTextured(1.0F, -1.0F, 1.0F, 1.0F, 1.0F); verts[5] = new CustomVertex.PositionTextured(1.0F, 1.0F, 1.0F, 1.0F, 0.0F); verts[6] = new CustomVertex.PositionTextured(-1.0F, 1.0F, -1.0F, 0.0F, 0.0F); verts[7] = new CustomVertex.PositionTextured(1.0F, 1.0F, -1.0F, 1.0F, 0.0F); verts[8] = new CustomVertex.PositionTextured(-1.0F, -1.0F, -1.0F, 0.0F, 1.0F); verts[9] = new CustomVertex.PositionTextured(-1.0F, -1.0F, -1.0F, 0.0F, 1.0F); verts[10] = new CustomVertex.PositionTextured(1.0F, 1.0F, -1.0F, 1.0F, 0.0F); verts[11] = new CustomVertex.PositionTextured(1.0F, -1.0F, -1.0F, 1.0F, 1.0F); verts[12] = new CustomVertex.PositionTextured(-1.0F, 1.0F, 1.0F, 0.0F, 0.0F); verts[13] = new CustomVertex.PositionTextured(1.0F, 1.0F, -1.0F, 1.0F, 1.0F); verts[14] = new CustomVertex.PositionTextured(-1.0F, 1.0F, -1.0F, 0.0F, 1.0F); verts[15] = new CustomVertex.PositionTextured(-1.0F, 1.0F, 1.0F, 0.0F, 0.0F); verts[16] = new CustomVertex.PositionTextured(1.0F, 1.0F, 1.0F, 1.0F, 0.0F); verts[17] = new CustomVertex.PositionTextured(1.0F, 1.0F, -1.0F, 1.0F, 1.0F); verts[18] = new CustomVertex.PositionTextured(-1.0F, -1.0F, 1.0F, 0.0F, 0.0F); verts[19] = new CustomVertex.PositionTextured(-1.0F, -1.0F, -1.0F, 0.0F, 1.0F); verts[20] = new CustomVertex.PositionTextured(1.0F, -1.0F, -1.0F, 1.0F, 1.0F); verts[21] = new CustomVertex.PositionTextured(-1.0F, -1.0F, 1.0F, 0.0F, 0.0F); verts[22] = new CustomVertex.PositionTextured(1.0F, -1.0F, -1.0F, 1.0F, 1.0F); verts[23] = new CustomVertex.PositionTextured(1.0F, -1.0F, 1.0F, 1.0F, 0.0F); verts[24] = new CustomVertex.PositionTextured(-1.0F, 1.0F, 1.0F, 0.0F, 0.0F); verts[25] = new CustomVertex.PositionTextured(-1.0F, -1.0F, -1.0F, 1.0F, 1.0F); verts[26] = new CustomVertex.PositionTextured(-1.0F, -1.0F, 1.0F, 1.0F, 0.0F); verts[27] = new CustomVertex.PositionTextured(-1.0F, 1.0F, -1.0F, 0.0F, 1.0F); verts[28] = new CustomVertex.PositionTextured(-1.0F, -1.0F, -1.0F, 1.0F, 1.0F); verts[29] = new CustomVertex.PositionTextured(-1.0F, 1.0F, 1.0F, 0.0F, 0.0F); verts[30] = new CustomVertex.PositionTextured(1.0F, 1.0F, 1.0F, 0.0F, 0.0F); verts[31] = new CustomVertex.PositionTextured(1.0F, -1.0F, 1.0F, 1.0F, 0.0F); verts[32] = new CustomVertex.PositionTextured(1.0F, -1.0F, -1.0F, 1.0F, 1.0F); verts[33] = new CustomVertex.PositionTextured(1.0F, 1.0F, -1.0F, 0.0F, 1.0F); verts[34] = new CustomVertex.PositionTextured(1.0F, 1.0F, 1.0F, 0.0F, 0.0F); verts[35] = new CustomVertex.PositionTextured(1.0F, -1.0F, -1.0F, 1.0F, 1.0F); // Заполнить вершинный буфер данными треугольников buffer.SetData(verts, 0, LockFlags.None); }Листинг 12.14. Координаты вершин текстурированных примитивов
При отображении каждая сторона куба будет включать текстуру целиком, то есть отображаться полностью в текстуре.
-
В функции InitializeGraphics() измените код создания вершинного буфера
// Создать вершинный буфер vb = new VertexBuffer(typeof(CustomVertex.PositionTextured), 36, device, Usage.Dynamic | Usage.WriteOnly, CustomVertex.PositionTextured.Format, Pool.Default);Листинг 12.15. Код создания вершинного буфера в функции InitializeGraphics()
-
В функции SetupCamera() установите новое значение приращения угла поворота объекта, затем вырежьте из этой функции секцию кода для самого поворота объекта
// Установка камеры в сцену private void SetupCamera() { //Создание перспективы device.Transform.Projection = Matrix.PerspectiveFovLH( (float)Math.PI / 4, // Угол зрения равен 45 градусов // Форматное соотношение сторон (float)this.ClientSize.Width / (float)this.ClientSize.Height, 1.0F, // Ближний план 100.0F); // Дальний план //Добавление камеры device.Transform.View = Matrix.LookAtLH( new Vector3(0, 0, 5.0F), // Положение камеры new Vector3(), // Положение объекта текущее new Vector3(0, 1, 0)); // Направление камеры // Освещение device.RenderState.Lighting = false; // Не скрывать обратную сторону плоской фигуры // device.RenderState.CullMode = Cull.None; // Преобразование пространства для произвольных поворотов /* device.Transform.World = Matrix.RotationYawPitchRoll( angle / (float)Math.PI, angle / (float)Math.PI * 2.0F, angle / (float)Math.PI); */ if (flagRotate) angle += 0.02F; }Листинг 12.16. Код, который нужно вырезать, и приращение угла скорректировать
-
Перенесите вырезанный код в функцию OnPaint() и вставьте перед вызовом функции рисования DrawPrimitives(). Откорректируйте определения формата устройства
protected override void OnPaint(PaintEventArgs e) { // Очистить цветом клиентскую область формы device.Clear(ClearFlags.Target, System.Drawing.Color.CornflowerBlue, 1.0F, 0); // Вызов нашей функции установки камеры SetupCamera(); // Сформировать сцену с учетом параметров // "положение-нормаль-цвет" device.BeginScene(); // Установить формат обработки вершин при отображении device.VertexFormat = CustomVertex.PositionTextured.Format; // Нарисовать треугольник по данным из буфера device.SetStreamSource(0, vb, 0); // Преобразование пространства для произвольных поворотов device.Transform.World = Matrix.RotationYawPitchRoll( angle / (float)Math.PI, angle / (float)Math.PI * 2.0F, angle / (float)Math.PI); device.DrawPrimitives(PrimitiveType.TriangleList, 0, 12); device.EndScene(); // Показать буфер кадра device.Present(); // Принудительно перерисовать this.Invalidate(); }Листинг 12.17. Перенесенный код в функции OnPaint()
-
Вставьте в секцию "Сформировать сцену" опять перед функцией рисования объекта код установки текстуры его сторон
// Сформировать сцену с учетом параметров // "положение-нормаль-цвет" device.BeginScene(); // Установить формат обработки вершин при отображении device.VertexFormat = CustomVertex.PositionTextured.Format; // Нарисовать треугольник по данным из буфера device.SetStreamSource(0, vb, 0); // Преобразование пространства для произвольных поворотов device.Transform.World = Matrix.RotationYawPitchRoll( angle / (float)Math.PI, angle / (float)Math.PI * 2.0F, angle / (float)Math.PI); // Установка текстуры device.SetTexture(0, texture); device.DrawPrimitives(PrimitiveType.TriangleList, 0, 12); device.EndScene();Листинг 12.18. Установка текстуры рисования сторон в функции OnPaint()
Direct3D позволяет устанавливать до восьми типов текстур одновременно для примитива рисования. В данном коде мы устанавливаем первым параметром только одну текстуру с индексом 0. Второй параметр является ссылкой на текстуру, которую нужно еще создать. Для этого служит класс Microsoft.DirectX.Direct3D. Texture. Класс имеет четыре версии конструктора
- public Texture(System.IntPtr lp, Microsoft.DirectX.Direct3D.Device device, Microsoft.DirectX.Direct3D.Pool pool )
- public Texture(Microsoft.DirectX.Direct3D.Device device, int width, int height, int numLevels, Microsoft.DirectX.Direct3D.Usage usage, Microsoft.DirectX.Direct3D.Format format, Microsoft.DirectX.Direct3D.Pool pool )
- public Texture(Microsoft.DirectX.Direct3D.Device device, System.Drawing.Bitmap image, Microsoft.DirectX.Direct3D.Usage usage, Microsoft.DirectX.Direct3D.Pool pool )
- public Texture(Microsoft.DirectX.Direct3D.Device device, System.IO.Stream data, Microsoft.DirectX.Direct3D.Usage usage, Microsoft.DirectX.Direct3D.Pool pool )
Мы воспользуемся для создания текстуры предпоследней из приведенных версий. Данный конструктор имеет четыре параметра.
- Первый параметр определяет устройство, используемое для выполнения текстуры (все ресурсы: вершинные буферы, текстуры и т.д. связаны с устройством)
- Второй параметр определяет источник данных, используемый для текстуры. В нашем случае будет использован конструктор класса Bitmap, загружающий рисунок из внедренного ресурса
- Третий параметр определяет индекс используемой структуры
- Четвертый параметр - пул памяти, используемый для хранения текстуры. Для удобства будем использовать управляемый пул
-
Добавьте в секцию определения ссылок-членов класса перед функцией InitializeGraphics() код создания ссылки на текстуру
private Device device = null; private VertexBuffer vb = null; private Texture texture = null; public void InitializeGraphics() { .................................... }Листинг 12.19. Создание поля-ссылки класса Form3 на текстуру
-
Поместите в обработчик vb_Created() в самый его конец после заполнения вершинного буфера данными аппроксимирующих треугольников код создания управляемого пула памяти с текстурой
private void vb_Created(object sender, EventArgs e) { .......................................... // Заполнить вершинный буфер данными треугольников buffer.SetData(verts, 0, LockFlags.None); // Создать объект текстуры Bitmap bmp = new Bitmap(this.GetType(), "12_16.bmp"); texture = new Texture(device, bmp, 0, Pool.Managed); }Листинг 12.20. Создание пула памяти для текстуры и загрузка в него рисунка
Здесь в качестве текстуры используется прилагаемый к описанию рисунок "12_16.bmp" из каталога Source данной лабораторной работы (вы можете создать свой рисунок в формате bmp ).
-
В меню Project выполните команду Add Existing Item, установите фильтр открывшегося диалогового окна на Image Files и добавьте в проект файл 12_16.bmp
-
Перейдите в панель Solution Explorer, выделите файл 12_16.bmp и через панель Properties установите свойство Build Action = Embedded Resource (внедренный ресурс), чтобы компилятор размещал рисунок прямо в файле исполнимой сборки
-
Постройте проект и получите примерно такой результат вращающегося текстурированного куба
-
Для демонстрации вариантов работы преподавателю создайте меню выбора варианта, как это сделано в Lab45 - WinFormsApp