Опубликован: 13.07.2010 | Уровень: специалист | Доступ: платный
Самостоятельная работа 12:

Рендеринг вращающихся кубов в 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. Класс имеет четыре версии конструктора

  1. public Texture(System.IntPtr lp, Microsoft.DirectX.Direct3D.Device device, Microsoft.DirectX.Direct3D.Pool pool )
  2. 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 )
  3. public Texture(Microsoft.DirectX.Direct3D.Device device, System.Drawing.Bitmap image, Microsoft.DirectX.Direct3D.Usage usage, Microsoft.DirectX.Direct3D.Pool pool )
  4. 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


Александр Очеретяный
Александр Очеретяный
Украина, Киев
Анастасия Балыбердина
Анастасия Балыбердина
Украина, Киев, НТУУ КПИ