netlib.narod.ru< Назад | Оглавление | Далее >

Визуализация различных типов примитивов

Весь код, написанный нами до сих пор, занимался визуализацией примитивов одного типа, называемого список треугольников. Однако, есть и другие типы примитивов, которые мы можем рисовать. Все они описаны в приведенном ниже списке:

Мы можем использовать одни и те же данные вершин для рисования любого типа примитивов. Direct3D будет по разному интерпретировать данные вершин в зависимости от того, какой тип примитива мы указали при рисовании. Давайте быстро напишем фрагмент кода, рисующий различные типы примитивов.

Находящийся на CD-ROM пример является прямым наследником первого примера работы с буфером вершин, который мы создали в главе 3. Здесь мы отметим сделанные в коде изменения. Поскольку данный код не предусматривает перемещения вершин, из метода SetupCamera убрано мировое преобразование и все ссылки на закрытую переменную angle. Добавлена следующая константа:

private const int NumberItems = 12;

Число 12 было выбрано произвольно, но с учетом ряда ограничений. Большое количество вершин загромождает экран; при этом нам надо, чтобы количество вершин было четным и делилось на три. Это требуется для корректного рисования типов примитивов LineList и TriangleList. Затем мы модифицируем код создания буфера вершин и функцию OnVertexBufferCreate, как показано в листинге 4.1.

Листинг 4.1. Создание случайных вершин

vb = new VertexBuffer(typeof(CustomVertex.PositionColored),
                      NumberItems, device, Usage.Dynamic | Usage.WriteOnly,
                      CustomVertex.PositionColored.Format, Pool.Default);

// Создание списка вершин для заданного количества элементов
CustomVertex.PositionColored[] verts =
                             new CustomVertex.PositionColored[NumberItems];

// Выбор случайного местоположения вершины в радиусе 5 единиц.
for(int i = 0; i < NumberItems; i++)
{
    float xPos = (float)(Rnd.NextDouble() * 5.0f) -
                 (float)(Rnd.NextDouble() * 5.0f);
    float yPos = (float)(Rnd.NextDouble() * 5.0f) -
                 (float)(Rnd.NextDouble() * 5.0f);
    float zPos = (float)(Rnd.NextDouble() * 5.0f) -
                 (float)(Rnd.NextDouble() * 5.0f);
    verts[i].Position = new Vector3(xPos, yPos, zPos);
    verts[i].Color    = RandomColor.ToArgb();
}
buffer.SetData(verts, 0, LockFlags.None);

Здесь нет ничего волшебного. Мы модифицировали наш буфер вершин, чтобы в нем хранилось необходимое количество вершин (заданное нашей константой NumberItems). Мы также изменили функцию создания вершин, чтобы она формировала заданное количество размещенных случайным образом вершин. Определения Rnd и RandomColor вы найдете в полном исходном коде примера на CD-ROM.

Теперь надо изменить вызов функции рисования. Проще всего показать различие между разными типами примитивов — написать код, который будет показывать все их один за другим. Давайте сделаем так, чтобы каждый тип примитива код показывал две секунды. Для этого проще всего проверять текущее время и сравнивать его со временем запуска. Добавьте в раздел объявлений следующие переменные:

private bool needRecreate = false;
// Информация о времени
private static readonly int InitialTickCount =
                           System.Environment.TickCount;

Логическая переменная позволяет заново создавать наш буфер вершин в начале каждого «цикла», чтобы не рассматривать снова и снова одни и те же вершины. Замените в коде единственный вызов DrawPrimitive на содержимое листинга 4.2.

Листинг 4.2. Визуализация каждого типа примитивов

// Решаем, какой тип примитивов рисовать
int index = ((System.Environment.TickCount -
                         InitialTickCount) / 2000) % 6;
switch (index)
{
    case 0: // Точки
        device.DrawPrimitives(PrimitiveType.PointList,
                              0, NumberItems);
        if (needRecreate)
        {
            // После рисования примитивов создаем новый набор
            OnVertexBufferCreate(vb, null);
            needRecreate = false;
        }
        break;
    case 1: // Список линий
        device.DrawPrimitives(PrimitiveType.LineList,
                              0, NumberItems / 2);
        needRecreate = true;
        break;
    case 2: // Ломаная линия
        device.DrawPrimitives(PrimitiveType.LineStrip,
                              0, NumberItems - 1);
        break;
    case 3: // Список треугольников
        device.DrawPrimitives(PrimitiveType.TriangleList,
                              0, NumberItems / 3);
        break;
    case 4: // Полоса треугольников
        device.DrawPrimitives(PrimitiveType.TriangleStrip,
                              0, NumberItems - 2);
        break;
    case 5: // Веер треугольников
        device.DrawPrimitives(PrimitiveType.TriangleFan,
                              0, NumberItems - 2);
        break;
}

Этот фрагмент кода самодокументируемый. В зависимости от того, какой именно тип примитива нам нужен в данном цикле, мы вызываем DrawPrimitives с указанием соответствующего типа. Обратите внимание, что для списка точек мы просто используем в качестве счетчика примитивов количество вершин, рисуя одну точку для каждой вершины. LineList и TriangleList рисуют изолированные примитивы, поэтому в вызове DrawPrimitives для этих типов указывается количество вершин, деленое на число вершин в отдельном примитиве данного типа: две или три, соответственно. В примитиве LineStrip каждая вершина, кроме первой, используется как начальная точка и как конечная точка предыдущей линии, поэтому количество рисуемых примитивов на единицу меньше количества вершин. TriangleStrip и TriangleFan ведут себя также, но поскольку в треугольнике три вершины, две из которых используются совместно со следующим примитивом, из количества вершин мы вычитаем два.

Запущенное приложение сначала покажет вам набор визуализированных точек, а следом — несколько изолированных линий. Затем эти линии будут соединены в одну ломаную линию, а потом превратятся в набор изолированных треугольников. И, наконец, треугольники станут совместно использовать вершины, превратившись сначала в полосу, а потом в веер. Вы можете заметить, что «точки» маленькие и их трудно увидеть. Вы можете искусственно «масштабировать» точки, установив режим визуализации. Добавьте к вашей функции SetupCamera следующую строку, чтобы каждая точка была увеличена в три раза по сравнению с ее обычным размером:

device.RenderState.PointSize = 3.0f;

Вы увидите, что точки стали крупнее, чем вначале. Можете поэкспериментировать с этим режимом визуализации. Теперь, когда мы научились корректно рисовать различные типы примитивов, давайте исследуем способ уменьшения объема памяти, занимаемого нашими данными.


netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz