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

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

Рендеринг различных типов примитивов

В качестве основы для дальнейших изменений мы взяли из лабораторной работы №12 код построения одного куба без текстурирования сторон, размещенный в файле Form1.cs. Мы уже присоединили этот файл к новому проекту (оболочка физически скопировала этот файл в каталог размещения текущего проекта). Теперь мы усовершенствуем файл Form1.cs, чтобы продемонстрировать отображение одного и того же набора вершин с помощью различных примитивов. Испытания примитивов не предполагают вращения рисуемых объектов, поэтому в функции SetupCamera() необходимо удалить вращение вершин и угол поворота.

  • В функции SetupCamera() удалите две последних секции кода, отвечающих за повороты объекта и приращение угла поворота, а также объявление самой переменной-поля angle
private float angle = 0;// Закрытый член класса
    
        // Установка камеры в сцену
        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.1F;
        }
Листинг 13.1. Удаления вращений в коде функции SetupCamera()
  • Удалите из класса Form1 сначала подписку обработчика на событие Click формы, затем обработчик Form1_Click(), а также описание ненужного теперь флага flagRotate
public Form1()
        {
            InitializeComponent();
    
            // Устраняем мерцание окна при бесконечной перерисовке
            this.SetStyle(ControlStyles.AllPaintingInWmPaint |
                ControlStyles.Opaque, true);
            
            this.Click += new EventHandler(Form1_Click);
        }
    
        bool flagRotate = true;
    
        void Form1_Click(object sender, EventArgs e)
        {
            flagRotate = !flagRotate;
        }
Листинг 13.2. Удаление обработчика остановки вращения и его регистрации
  • Добавьте в класс объявление поля-ссылки NumberItems на количество вершин случайно генерируемого объекта, который мы будем отображать с применением разных примитивов
private Device device = null;       // Графический конвейер
        private VertexBuffer vb = null;     // Вершинный буфер
        private const int NumberItems = 12; // Количество вершин объекта
Листинг 13.3. Константная переменная класса Form1 для задания количества вершин

Мы выбрали количество вершин 12 потому, что это число четное, делится на 3 (что подходит для примитивов LineList и TriangleList ) и не настолько большое, чтобы загромождать рисунок. Объявление переменной как константы поручает компилятору следить за тем, чтобы мы случайно где-нибудь в коде не попытались ее изменить.

  • Измените в функции InitializeGraphics() размерность вершинного буфера, заменив 36 на NumberItems
public void InitializeGraphics()
    {
      ..................................
      // Создать вершинный буфер
      vb = new VertexBuffer(typeof(CustomVertex.PositionColored),
                          NumberItems, 
                          device, 
                          Usage.Dynamic | Usage.WriteOnly,
                          CustomVertex.PositionColored.Format,
                          Pool.Default);
      ..................................
    }
Листинг 13.4. Новая размерность вершинного буфера в функции InitializeGraphics()

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

  • Объявите в составе класса Form1 ссылочную переменную-поле на генератор случайных чисел по равномерному закону распределения, а в конструкторе Form1() разместите код создания и инициализации этого генератора случайных чисел
public Form1()
        {
            InitializeComponent();
    
            // Устраняем мерцание окна при бесконечной перерисовке
            this.SetStyle(ControlStyles.AllPaintingInWmPaint |
                ControlStyles.Opaque, true);
    
            // Инициализация генератора случайных чисел
            rand = new System.Random();
        }
    
        private Device device = null;       // Графический конвейер
        private VertexBuffer vb = null;     // Вершинный буфер
        private const int NumberItems = 12; // Количество вершин объекта
        System.Random rand;                 // Поле для генератора случайных чисел
Листинг 13.5. Добавление генератора случайных чисел в файле Form1.cs
  • Измените код создания вершин объекта в функции vb_Created()
void vb_Created(object sender, EventArgs e)
        {
            // Определить внутреннюю ссылку на вершинный буфер
            VertexBuffer buffer = (VertexBuffer)sender; // Явное приведение типов
    
            // Создать локальный массив ссылок для вершин объекта
            CustomVertex.PositionColored[] verts = new
                CustomVertex.PositionColored[NumberItems];
    
            // Сгенерировать случайно координаты и цвета вершин
            double factor = 2.5d;// Весовой коэффициент
            for (int i = 0; i < NumberItems; i++)
            {
                float xPos = (float)((rand.NextDouble() - rand.NextDouble()) * factor);
                float yPos = (float)((rand.NextDouble() - rand.NextDouble()) * factor);
                float zPos = (float)((rand.NextDouble() - rand.NextDouble()) * factor);
                verts[i].SetPosition(new Vector3(xPos, yPos, zPos));
                verts[i].Color = System.Drawing.Color.FromArgb(rand.Next(256),
                                                               rand.Next(256),
                                                               rand.Next(256)).ToArgb();
            }
    
            // Заполнить вершинный буфер данными треугольников
            buffer.SetData(verts, 0, LockFlags.None);
        }
Листинг 13.6. Создание вершин со случайными параметрами в функции vb_Created()

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

  • Добавьте в класс Form1 объявление двух новых полей, которые для читабельности удобнее расположить перед функцией OnPaint()
// Флаг регенерации вершин перед началом
        // каждого нового цикла обхода всех примитивов.
        // Первое заполнение вершинного буфера выполнено
        // в InitializeGraphics() при запуске приложения
        private bool needRecreate = false;
        // Захват стартового времени в тактах
        private static readonly int beginTickCount =
            System.Environment.TickCount;
  
    protected override void OnPaint(PaintEventArgs e)
    {
    ........................................
    }
Листинг 13.7. Добавление новых полей в класс Form1

Переменная beginTickCount объявлена как статическая для того, чтобы ее данные хранились в объекте-типе, а не объекте-экземпляре, на случай, если будет создано несколько экземпляров класса Form1. Ключевое слово readonly делает ее доступной "только для чтения". В отличие от константных переменных, требующих немедленной инициализации при объявлении, переменные "только для чтения" допускают отложенную инициализацию в конструкторе класса.

  • В функции OnPaint() замените вызов библиотечной функции DrawPrimitives() в секции кода формирования сцены и цвет фона формы выделенным ниже блоком кода, чтобы окончательно функция выглядела так
// Флаг регенерации вершин перед началом
        // каждого нового цикла обхода всех примитивов.
        // Первое заполнение вершинного буфера выполнено
        // в InitializeGraphics() при запуске приложения
        private bool needRecreate = false;
        // Захват стартового времени в тактах
        private static readonly int beginTickCount =
            System.Environment.TickCount;
    
        protected override void OnPaint(PaintEventArgs e)
        {
            // Очистить цветом клиентскую область формы
            device.Clear(ClearFlags.Target,
                System.Drawing.Color.White,
                1.0F, 0);
    
            // Вызов нашей функции установки камеры
            SetupCamera();
    
            // Сформировать сцену с учетом параметров 
            // "положение-нормаль-цвет"
            device.BeginScene();
            // Установить формат обработки вершин при отображении
            device.VertexFormat = CustomVertex.PositionColored.Format;
            // Нарисовать треугольник по данным из буфера
            device.SetStreamSource(0, vb, 0);
    
            const int countTime = 5 * 1000; // Держать изображение 5 секунд 
            const int countIndex = 6;       // Количество вариантов показа
            int index = ((System.Environment.TickCount - beginTickCount) / countTime)
                % countIndex;
            switch (index)
            {
                case 0: // PointList
                    device.DrawPrimitives(PrimitiveType.PointList, 0, NumberItems);
                    if (needRecreate)// Выполнить только при очередном index == 0
                    {
                        vb_Created(vb, null);// Сгенерировать новый вершинный буфер
                        needRecreate = false;// Сбросить флаг регенерации вершин
                    }
                    this.Text = "Несоединенные точки вершин";
                    break;
                case 1: // LineList
                    device.DrawPrimitives(PrimitiveType.LineList, 0, NumberItems / 2);
                    needRecreate = true;// Приготовить флаг для регенерации вершин при index == 0
                    this.Text = "Соединие пар вершин линиями";
                    break;
                case 2: // LineStrip
                    device.DrawPrimitives(PrimitiveType.LineStrip, 0, NumberItems - 1);
                    this.Text = "Соединение вершин ломаной";
                    break;
                case 3: // TriangleList
                    device.DrawPrimitives(PrimitiveType.TriangleList, 0, NumberItems / 3);
                    this.Text = "Отдельные треугольники";
                    break;
                case 4: // TriangleStrip
                    device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, NumberItems - 2);
                    this.Text = "Непрерывная ферма треугольников";
                    break;
                case 5: // TriangleFan
                    device.DrawPrimitives(PrimitiveType.TriangleFan, 0, NumberItems - 2);
                    this.Text = "Треугольный веер из точки";
                    break;
            }
    
            device.EndScene();
    
            // Показать буфер кадра 
            device.Present();
    
            // Принудительно перерисовать
            this.Invalidate();
        }
Листинг 13.8. Рендеринг различных типов примитивов в функции OnPaint()
  • Добавьте в конец функции SetupCamera() код изменения состояния рендера для увеличения размера отображаемых точек вершин, чтобы их можно было различить в нулевом режиме PointList
// Установка камеры в сцену
        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.PointSize = 5.0f;
        }
Листинг 13.9. Увеличение масштаба отображения точек в функции SetupCamera()
  • Постройте приложение и убедитесь, что форма Form1 периодически генерирует новые объекты и отображает их разными примитивами