Опубликован: 13.07.2010 | Доступ: свободный | Студентов: 891 / 20 | Оценка: 4.40 / 4.20 | Длительность: 77:34:00
Самостоятельная работа 13:

Более совершенные технологии рендеринга в DirectX

Создание нескольких вращающихся кубов с применением буфера глубины

Буфер глубины ( depth buffer, Z-буфер, W-буфер ) применяется в приложениях Direct3D для хранения информации о глубине отображаемого объекта. Эта информация используется в процессе растеризации (из векторной в растровую) для определения того, насколько пикселы перекрывают друг друга. На данном этапе наше приложение не имеет буфера глубины, но в этом разделе мы его введем и используем.

  • Через контекстное меню панели Solution Explorer получите копию файла Form2 и переименуйте ее в Form3.cs
  • Через контекстное меню панели Solution Explorer откройте файл Form3.cs в режиме View Code


  • В панели Solution Explorer двойным щелчком на файле Form3.designer.cs откройте его в рабочей области редактора оболочки
  • Строго проследите, чтобы открытыми были только файлы Form3.cs Form3.designer.cs и замените в них все вхождения Form2 на Form3 через окно замены, которое можно вызвать комбинацией клавиш Ctrl-H. Предварительно настройте опции окна замены по приведенному снимку


  • Выполните команду меню Project/RenderBest Properties и установите форму Form3 стартовой

  • Запустите проект на выполнение и убедитесь, что Form3 работает в прежнем варианте Form2, т.е. целостность кода сохранилась и мы можем продолжать развитие проекта

Вначале вместо одного куба создадим 9 кубов и для правильного их отображения отодвинем камеру.

  • Добавьте в класс Form3 новую функцию DrawCubes() для рисования 9 кубов
private void DrawCubes()
        {
            //
            // Средний ряд
            //
            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);   // По центру
            device.DrawIndexedPrimitives(
                PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3);
    
            device.Transform.World = Matrix.RotationYawPitchRoll(
                angle / (float)Math.PI,
                angle / (float)Math.PI / 2.0f,
                angle / (float)Math.PI * 4.0f) *
                Matrix.Translation(4.0f, 0.0f, 0.0f);   // Сдвиг вправо
            device.DrawIndexedPrimitives(
                PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3);
    
            device.Transform.World = Matrix.RotationYawPitchRoll(
                angle / (float)Math.PI,
                angle / (float)Math.PI * 4.0f,
                angle / (float)Math.PI / 2.0f) *
                Matrix.Translation(-4.0f, 0.0f, 0.0f);   // Сдвиг влево
            device.DrawIndexedPrimitives(
                PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3);
    
            //
            // Нижний ряд
            //
            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, -4.0f, 0.0f);   // По центру
            device.DrawIndexedPrimitives(
                PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3);
    
            device.Transform.World = Matrix.RotationYawPitchRoll(
                angle / (float)Math.PI,
                angle / (float)Math.PI / 2.0f,
                angle / (float)Math.PI * 4.0f) *
                Matrix.Translation(4.0f, -4.0f, 0.0f);   // Сдвиг вправо
            device.DrawIndexedPrimitives(
                PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3);
    
            device.Transform.World = Matrix.RotationYawPitchRoll(
                angle / (float)Math.PI,
                angle / (float)Math.PI * 4.0f,
                angle / (float)Math.PI / 2.0f) *
                Matrix.Translation(-4.0f, -4.0f, 0.0f);   // Сдвиг влево
            device.DrawIndexedPrimitives(
                PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3);
    
            //
            // Верхний ряд
            //
            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, 4.0f, 0.0f);   // По центру
            device.DrawIndexedPrimitives(
                PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3);
    
            device.Transform.World = Matrix.RotationYawPitchRoll(
                angle / (float)Math.PI,
                angle / (float)Math.PI / 2.0f,
                angle / (float)Math.PI * 4.0f) *
                Matrix.Translation(4.0f, 4.0f, 0.0f);   // Сдвиг вправо
            device.DrawIndexedPrimitives(
                PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3);
    
            device.Transform.World = Matrix.RotationYawPitchRoll(
                angle / (float)Math.PI,
                angle / (float)Math.PI * 4.0f,
                angle / (float)Math.PI / 2.0f) *
                Matrix.Translation(-4.0f, 4.0f, 0.0f);   // Сдвиг влево
            device.DrawIndexedPrimitives(
                PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3);
    
            if (flagRotate)
                angle += 0.02F;
        }
Листинг 13.19. Код функции рисования 9 кубов DrawCubes()
  • Удалите из функции SetupCamera() две последних секции, ответственные за вращение куба, поскольку эту функциональность мы включили в новую функцию DrawCubes()
// Установка камеры в сцену
        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.Transform.World = Matrix.RotationYawPitchRoll(
                angle / (float)Math.PI,
                angle / (float)Math.PI * 2.0F,
                angle / (float)Math.PI);
            if (flagRotate)
                angle += 0.02F;
        }
Листинг 13.20. Удаляем код в конце функции SetupCamera()
  • Замените в секции формирования сцены функции OnPaint() вызов функции DrawIndexedPrimitives() на вызов функции DrawCubes()
// Сформировать сцену с учетом параметров 
            // "положение-нормаль-цвет"
            device.BeginScene();
            // Установить формат обработки вершин при отображении
            device.VertexFormat = CustomVertex.PositionColored.Format;
            // Нарисовать треугольник по данным из буфера
            device.SetStreamSource(0, vb, 0);
            device.Indices = ib;
            DrawCubes();
            device.EndScene();
Листинг 13.21. Код формирования сцены в функции OnPaint()
  • В функции SetupCamera() отодвиньте камеру, чтобы была видна вся сцена с 9 кубами
// Установка камеры в сцену
        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, 15.0F),    // Положение камеры
                new Vector3(),              // Положение объекта текущее
                new Vector3(0, 1, 0));      // Направление камеры
    
            // Освещение
            device.RenderState.Lighting = false;
        }
Листинг 13.22. Увеличение расстояния установки камеры в функции SetupCamera()
  • Запустите приложение и получите примерно следующее


Настало время перейти к использованию буферов глубины объектов. Давайте добавим еще три куба к нашей сцене вращающихся кубов.

  • Добавьте в конец функции DrawCubes() код для рисования еще трех кубов
private void DrawCubes()
        {
            //
            // Средний ряд
            //
            //.................................................
            //
            // Дополнительные кубы
            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,
                    (float)Math.Cos(angle),
                    (float)Math.Sin(angle));
            device.DrawIndexedPrimitives(
                PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3);
    
            device.Transform.World = Matrix.RotationYawPitchRoll(
                angle / (float)Math.PI,
                angle / (float)Math.PI / 2.0f,
                angle / (float)Math.PI * 4.0f) *
                Matrix.Translation(
                    4.0f,
                    (float)Math.Sin(angle),
                    (float)Math.Cos(angle));
            device.DrawIndexedPrimitives(
                PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3);
    
            device.Transform.World = Matrix.RotationYawPitchRoll(
                angle / (float)Math.PI,
                angle / (float)Math.PI * 2.0f,
                angle / (float)Math.PI / 2.0f) *
                Matrix.Translation(
                -4.0f,
                (float)Math.Cos(angle),
                (float)Math.Sin(angle));
            device.DrawIndexedPrimitives(
                PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3);
    
            if (flagRotate)
                angle += 0.02F;
        }
Листинг 13.23. Добавление в конец функции DrawCubes() кода рисования трех новых кубов
  • Запустите приложение и увидите по горизонтальному среднему ряду еще дополнительных три куба

Здесь мы хотим получить вращение дополнительных кубов вокруг трех центральных кубов, расположенных по горизонтали. Чтобы этого достичь, следует добавить буфер глубины в устройство device.

Существуют разные форматы буферов глубины, но большинство современных графических плат поддерживают 16-разрядные буферы глубины. Поэтому используем именно такую глубину.

  • Добавьте в функцию 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);
    
            // Создать вершинный буфер
            // .................................................
        }
Листинг 13.24. Параметры настройки буфера глубины в функции InitializeGraphics()
  • Запускаем приложение и видим, что вместо кубов появились их осколки

Исчезновение сцены после добавления параметров буфера глубины произошло потому, что этот буфер не был очищен

  • Добавьте к первой секции кода функции OnPaint() дополнительный флаг очистки буфера глубины
protected override void OnPaint(PaintEventArgs e)
    {
            // Очистить цветом клиентскую область формы
            device.Clear(ClearFlags.Target 
                | ClearFlags.ZBuffer,
                System.Drawing.Color.CornflowerBlue,
                1.0F, 0);
      ..........................................
    }
Листинг 13.25. Добавление флага сброса буфера глубины устройства device в функцию OnPaint()
  • Запустите приложение и мы увидим фантастическую картину, иллюстрирующую использование буфера глубины


Краткие выводы

В данной лабораторной работе мы познакомились с более совершенными методами рендеринга, а именно

  1. Посмотрели на работу различных типов примитивов, приминяемых для рендеринга объектов.
  2. Попробовали использовать индексные буферы, позволяющие существенно сократить объем данных за счет устранения дублирования описания параметров вершин.
  3. Применили буфер глубины устройства для более реалистичного (или фантастического) отображения объектов.