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

Введение в DirectX

Хранение треугольника в вершинном буфере

До сих пор мы поступали слишком расточительно по отношению к временным затратам компьютера. Неизменные вершины треугольника мы задавали каждый раз при обновлении экрана. А можно это сделать один раз и для этого библиотека Direct3D имеет специальный класс VertexBuffer для создания буфера хранения вершин объекта, чем мы и воспользуемся.

Класс Microsoft.DirectX.Direct3D. VertexBuffer имеет три перегрузки конструктора, которые можно использовать для создания вершинного буфера

public VertexBuffer(System.IntPtr lp, 
                    Microsoft.DirectX.Direct3D.Device device,
                    Microsoft.DirectX.Direct3D.Usage usage, 
                    Microsoft.DirectX.Direct3D.VertexFormats vertexFormat,
                    Microsoft.DirectX.Direct3D.Pool pool)
                    
    public VertexBuffer(System.Type typeVertexType,
                    System.Int32 numVerts, 
                    Microsoft.DirectX.Direct3D.Device device, 
                    Microsoft.DirectX.Direct3D.Usage usage, 
                    Microsoft.DirectX.Direct3D.VertexFormats vertexFormat, 
                    Microsoft.DirectX.Direct3D.Pool pool)
                    
    public VertexBuffer(Microsoft.DirectX.Direct3D.Device device, 
                    System.Int32 sizeOfBufferInBytes, 
                    Microsoft.DirectX.Direct3D.Usage usage, 
                    Microsoft.DirectX.Direct3D.VertexFormats vertexFormat, 
                    Microsoft.DirectX.Direct3D.Pool pool)
Листинг 10.22.

Опишем каждый параметр:

  • lp - принимает указатель на неуправляемый интерфейс IDirect3DvertexBuffer9 для совместимости с вершинным буфером, созданным с помощью неуправляемого внешнего источника. Управляемые приложения не должны использовать эту перегрузку конструктора.
  • device - устройство, которое используется в Direct3D для создания вершинного буфера.
  • sizeOfBufferInBytes - задаваемый параметр вершинного буфера в байтах. С таким параметром буфер способен поддерживать любой тип вершин.
  • typeVertexType - задается, если мы хотим, чтобы буфер включал только один тип вершин. Это может быть тип одной из встроенных структур вершин в классе CustomVertex, либо тип вершин, определяемый пользователем. Данное значение не может быть пустым.
  • numVerts - максимальное количество вершин, которые мы хотим сохранить в буфере. Это значение должно быть большим нуля.
  • usage - определяет, как этот вершинный буфер может использоваться. При создании буфера могут использоваться все значения этого параметра, за исключением следующих значений перечисления:
    • DoNotClip - используется для того, чтобы исключить режим clipping (отсечение) для этого буфера. Используя этот флажок, необходимо при использовании рендеринга из вершинного буфера установить параметр clipping состояния рендера в значение false.
    • Dynamic - используется для сообщения приложению, что вершинный буфер требует использования динамической памяти. Если этот флажок не определен, вершинный буфер является статическим. Статические вершины буфера обычно хранятся в видеопамяти, в то время как динамические буферы хранятся в памяти AGR. Выбор данной опции необходим для драйверов, чтобы определить, где хранить данные.
    • NPatches - сообщает, что данный буфер используется для рисования патчей N-patches.
    • Points - сообщает, что данный буфер используется для рисования точек.
    • RTPatches - сообщает, что данный буфер используется для рисования примитивов более высокого порядка.
    • SoftwareProcessing - сообщает, что обработка вершин должна проводиться программным обеспечением, в противном случае обработка ведется аппаратными средствами.
    • WriteOnly - сообщает, что вершинный буфер не будет считываться в случае, когда нет необходимости считывать данные вершинного буфера. Данная опция позволяет разгрузить видеопамять.
  • vertexFormat - перечисление определяет формат вершин, которые будут сохранены в этом буфере. Можно выбрать опцию Microsoft.DirectX.Direct3D.VertexFormats. None, если требуется зарезервировать этот буфер.
  • pool - определяет пул памяти, куда будет размещен вершинный буфер. Определяется значениями перечислений
    • Microsoft.DirectX.Direct3D.Pool. Managed - ресурсы копируются автоматически в доступное устройство памяти. При сбросе устройства ресурсы автоматически в нем не восстанавливаются.
    • Microsoft.DirectX.Direct3D.Pool. Default - данные располагаются либо в памяти AGP, либо в видеопамяти. Вершинные буферы, созданные в этом пуле памяти, определяются автоматически перед сбросом устройства.
    • Microsoft.DirectX.Direct3D.Pool. SystemMemory - данные вершинного буфера помещаются в системную память, где являются недоступными для устройства.
    • Microsoft.DirectX.Direct3D.Pool. Scratch - системный пул памяти, не связанный с устройством и не используемый им. Удобен при управлении данными, при этом не привязан к специфическому формату устройства.

Далее мы воспользуемся второй из описанных перегрузок конструктора создания вершинного буфера (выделена).

  • Добавьте к классу Form1 закрытое поле-ссылку на вершинный буфер в дополнение к объявленной ранее переменной-ссылке устройства
private Device device = null;
    private VertexBuffer vb = null;
Листинг 10.23. Добавление поля ссылки на вершинный буфер в класс

Вместо того, чтобы каждый раз при обращении к функции OnPaint() создавать данные при отображении сцены, можно сделать это один раз. Использование вершинного буфера позволяет в значительной степени увеличить производительность операций отображения рисунка.

  • Перенесите код создания треугольника из OnPaint() в функцию InitializeGraphics() и разместите его сразу после кода создания устройства, после чего функция InitializeGraphics() должна стать такой
public void InitializeGraphics()
        {
            // Создание объекта и настройка параметров представления
            // Создать объект параметров представления
            PresentParameters presentParams = new PresentParameters();
            // Установить оконный режим
            presentParams.Windowed = true;
            // Сбрасывать содержимое буфера, если он не готов к представлению
            presentParams.SwapEffect = SwapEffect.Discard;
            // Создать объект устройства и сохранить ссылку на него
            device = new Device(0, DeviceType.Hardware, this,
                CreateFlags.SoftwareVertexProcessing, presentParams);
    
            // Создать вершинный буфер
            vb = new VertexBuffer(typeof(CustomVertex.PositionNormalColored),
                                  3,
                                  device,
                                  Usage.Dynamic | Usage.WriteOnly,
                                  CustomVertex.PositionNormalColored.Format,
                                  Pool.Default);
    
            // Создать массив структур непреобразованных координат
            // треугольника с возможностью определения нормали к нему
            CustomVertex.PositionNormalColored[] verts = new
                CustomVertex.PositionNormalColored[3];
    
            // Задать параметры треугольника
            verts[0].SetPosition(new Vector3(0.0F, 1.0F, 1.0F));
            verts[0].Color = System.Drawing.Color.Aqua.ToArgb();
            verts[1].SetPosition(new Vector3(-1.0F, -1.0F, 1.0F));
            verts[1].Color = System.Drawing.Color.Black.ToArgb();
            verts[2].SetPosition(new Vector3(1.0F, -1.0F, 1.0F));
            verts[2].Color = System.Drawing.Color.Purple.ToArgb();
    
            // Дополнить вершины параметрами нормалей
            verts[0].SetNormal(new Vector3(0.0F, 0.0F, -1.0F));
            verts[1].SetNormal(new Vector3(0.0F, 0.0F, -1.0F));
            verts[2].SetNormal(new Vector3(0.0F, 0.0F, -1.0F));
    
            // Заполнить вершинный буфер данными треугольника
            vb.SetData(verts, 0, LockFlags.None);
        }
Листинг 10.24. Заполнение данных треугольника в вершинный буфер

В добавленном коде мы сначала создаем вершинный буфер для хранения трех элементов массива структур verts. Мы настраиваем вершинный буфер только на запись, определяем его как динамический для хранения в заданном по умолчанию пуле памяти. Затем задаем параметры треугольника в перенесенном из функции OnPaint() коде.

Далее мы помещаем первым параметром список вершин треугольника в вершинный буфер функцией SetData(). Второй параметр задает смещение, с какой позиции нужно записывать массив verts. Мы записываем все вершины, поэтому задаем смещение равным нулю. Последний параметр определяет, как мы хотим блокировать буфер на то время, пока данные записываются. Мы сказали, что никак, поскольку используем буфер монопольно.

После записи данных о треугольнике в вершинный буфер мы не можем в функции OnPaint() рисовать треугольник как прежде функцией DrawUserPrimitives(). Для этого существует другая функция - DrawPrimitives().

  • Измените в функции OnPaint() код формирования сцены, размещенный сразу после настройки источника света, на следующий
// Сформировать сцену с учетом параметров 
            // "положение-нормаль-цвет"
            device.BeginScene();
            // Установить формат обработки вершин при отображении
            device.VertexFormat = CustomVertex.PositionNormalColored.Format;
            // Нарисовать треугольник по данным из буфера
            device.SetStreamSource(0, vb, 0);
            device.DrawPrimitives(PrimitiveType.TriangleList, 0, 1);
            device.EndScene();
Листинг 10.25. Код формирования сцены в функции OnPaint()

С помощью функции SetStreamSource() мы подключаем к устройству вершинный буфер. Первый параметр этой функции определяет номер потока, который будет использовать данные. Мы используем один поток с номером 0. Второй параметр - вершинный буфер с данными рисования. Третий параметр - смещение в байтах, с которого начнется рисование. Мы будем рисовать все данные, поэтому задали нулевое смещение.

Далее мы рисуем данные из созданного потока с помощью функции DrawPrimitives(). Первый параметр определяет примитив, с помощью которого будет рисоваться фигура - у нас это единственный треугольник будет аппроксимироваться треугольным примитивом. Второй параметр - начальная вершина в потоке. Последний параметр - число примитивов, которые мы будем рисовать.

  • Постройте приложение и убедитесь, что треугольник строится и вращается как прежде

Но здесь есть проблема: если попытаться изменить размер окна формы, то треугольник исчезает, сразу и навсегда.

Причина в том, что при изменении размеров окна графическое устройство device автоматически сбрасывается. Это означает, что пул при сбрасывании устройства автоматически устанавливается в нулевое состояние. То есть там после сброса не будет никаких данных, поэтому ничего не отобразится на экране. До введения вершинного буфера данные определялись в функции OnPaint() при каждой перерисовке, поэтому такого эффекта не наблюдалось, хотя сбрасывание при изменении размеров окна имело место.

Нужно каким-то образом вновь заполнить вершинный буфер после сброса. Вершинный буфер имеет событие Microsoft.DirectX.Direct3D.VertexBuffer. Created, которое отслеживает момент восстановления буфера и готовность его к заполнению. Нам нужно перехватить это событие в приложении и создать для него обработчик, в котором разместить код создания треугольника и заполнения вершинного буфера.

  • В функции InitializeGraphics() сразу после кода создания вершинного буфера поместите код регистрации события Created и начальный вызов обработчика для только что созданной формы
// Создать вершинный буфер
            vb = new VertexBuffer(typeof(CustomVertex.PositionNormalColored),
                                  3,
                                  device,
                                  Usage.Dynamic | Usage.WriteOnly,
                                  CustomVertex.PositionNormalColored.Format,
                                  Pool.Default);
    
            // Регистрация события Created вершинного буфера
            vb.Created += new EventHandler(vb_Created);
    
            // Принудительный вызов создания треугольника 
            // и заполнения вершинного буфера при первом 
            // создании формы
            vb_Created(vb, null);
Листинг 10.26. Регистрация события Created вершинного буфера в функции InitializeGraphics()

Нулевая ссылка на месте второго параметра в принудительном вызове обработчика vb_Created() является заглушкой для объекта типа EventArgs параметров события, поскольку никаких параметров внутри обработчика мы не используем.

  • Перенесите всю оставшуюся часть функции InitializeGraphics() (код создания вершин треугольника и заполнения буфера) в созданный обработчик, который станет таким
void vb_Created(object sender, EventArgs e)
        {
            // Создать массив структур непреобразованных координат
            // треугольника с возможностью определения нормали к нему
            CustomVertex.PositionNormalColored[] verts = new
                CustomVertex.PositionNormalColored[3];
    
            // Задать параметры треугольника
            verts[0].SetPosition(new Vector3(0.0F, 1.0F, 1.0F));
            verts[0].Color = System.Drawing.Color.Aqua.ToArgb();
            verts[1].SetPosition(new Vector3(-1.0F, -1.0F, 1.0F));
            verts[1].Color = System.Drawing.Color.Black.ToArgb();
            verts[2].SetPosition(new Vector3(1.0F, -1.0F, 1.0F));
            verts[2].Color = System.Drawing.Color.Purple.ToArgb();
    
            // Дополнить вершины параметрами нормалей
            verts[0].SetNormal(new Vector3(0.0F, 0.0F, -1.0F));
            verts[1].SetNormal(new Vector3(0.0F, 0.0F, -1.0F));
            verts[2].SetNormal(new Vector3(0.0F, 0.0F, -1.0F));
    
            // Заполнить вершинный буфер данными треугольника
            vb.SetData(verts, 0, LockFlags.None);
        }
Листинг 10.27. Обработчик события Created вершинного буфера
  • Запустите приложение и попробуйте изменить размер окна

Теперь треугольник восстанавливается в вершинном буфере и прорисовывается всякий раз после сброса устройства.

Конечно, для такого графического объекта, как наш треугольник, выигрыш от применения вершинного буфера не слишком заметен. Но для формирования сложных объектов, состоящих из огромного количества вершин, этот выигрыш существенно возрастет.