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

Введение в DirectX

Вращение треугольника вокруг координатной оси

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

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

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

  • Добавьте в конец функции 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;
    
            // Преобразование пространства для поворота треугольника относительно оси Z
            device.Transform.World = Matrix.RotationZ((float)Math.PI / 6.0F);
        }
Листинг 10.14. Поворот треугольника на неизменный угол

Добавленный код сообщает Direct3D о типе используемого преобразования для каждого рисуемого объекта. В данном случае это вращение объекта относительно оси Z. Угол поворота задается строго в радианах. В библиотеке функций Direct3DX, которую мы добавим позже к нашему проекту, существует вспомогательная функция Geometry.DegreeToRadians(), преобразующая привычные нам градусы в радианы.

  • Постройте приложение. Экранный вывод для постоянного поворота будет таким


Мы повернули объект на постоянный произвольный угол и он непрерывно перерисовывается в этом неизменном положении в окне формы, потому что мы непрерывно генерируем событие разрушение окна вызовом Invalidate() в методе OnPaint(). Если менять угол в методе Matrix. RotationZ(), то треугольник будет непрерывно вращаться.

  • Замените вызов функции Matrix. RotationZ() на следующий код
// Установка камеры в сцену
        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;
    
            /*
            // Преобразование пространства для поворота треугольника относительно оси Z
            device.Transform.World = Matrix.RotationZ((float)Math.PI / 6.0F);
            */
    
            // Преобразование пространства для непрерывного вращения треугольника
            device.Transform.World = Matrix.RotationZ(
                (System.Environment.TickCount / 450.0F) / (float)Math.PI);
        }
Листинг 10.15. Код для вращения треугольника
  • Запустите приложение и получите вращающийся треугольник

Вращение происходит из-за непрерывно увеличивающегося встроенного в библиотеку счетчика System.Environment. TickCount. Каждые 15 миллисекунд значение счетчика увеличивается, тем самым периодически увеличивая угол вращения для новых перерисовок. Учитывая, что клиентская область формы непрерывно перерисовывается, мы можем сами организовать вращение объекта заданием своего счетчика увеличения угла.

  • Добавьте в класс Form1 поле angle для хранения значений угла и измените вызов Matrix. RotationZ() в функции SetupCamera()
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;
    
            /*
            // Преобразование пространства для поворота треугольника относительно оси Z
            device.Transform.World = Matrix.RotationZ((float)Math.PI / 6.0F);
    
            // Преобразование пространства для непрерывного вращения треугольника
            device.Transform.World = Matrix.RotationZ(
                (System.Environment.TickCount / 450.0F) / (float)Math.PI);
            */
    
            // Вращение с собственным счетчиком
            device.Transform.World = Matrix.RotationZ(angle / (float)Math.PI);
            angle += 0.1F;
        }
Листинг 10.16. Собственный счетчик для увеличения угла вращения
  • Постройте приложение и убедитесь, что треугольник вращается

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

Вращение треугольника вокруг произвольной оси

Организуем вращение треугольника вокруг произвольно заданной оси.

  • Измените процедуру преобразования пространства, чтобы код функции 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;
    
            /*
            // Преобразование пространства для поворота треугольника относительно оси Z
            device.Transform.World = Matrix.RotationZ((float)Math.PI / 6.0F);
    
            // Преобразование пространства для непрерывного вращения треугольника
            device.Transform.World = Matrix.RotationZ(
                (System.Environment.TickCount / 450.0F) / (float)Math.PI);
    
            // Вращение с собственным счетчиком
            device.Transform.World = Matrix.RotationZ(angle / (float)Math.PI);
            */
    
            // Преобразование пространства для произвольных поворотов
            device.Transform.World = Matrix.RotationAxis(
                new Vector3(angle / ((float)Math.PI * 2.0F),
                            angle / ((float)Math.PI * 4.0F),
                            angle / ((float)Math.PI * 6.0F)),
                angle / (float)Math.PI);
            angle += 0.1F;
        }
Листинг 10.17. Вращение вокруг заданной оси

В вызове функции Matrix. RotationAxis() первый аргумент конструктора Vector3 устанавливает ориентацию оси вращения треугольника, второй параметр - угол поворота вокруг этой оси. Угол поворота мы задаем непрерывным увеличением счетчика при каждом обновлении экрана, чем вызываем вращение треугольника.

  • Запустите приложение с новым изменением кода и проверьте экранный вывод

Треугольник вращается вокруг утановленной оси, но постоянно исчезает, когда должен поворачиваться обратной стороной. Это связано с порядком освещения невидимых поверхностей. В данном случае Direct3D по умолчанию считает, что отображается объемная фигура, следовательно наш плоский треугольник является только передней ее гранью, а обратная сторона должна быть закрыта другой гранью, которой нет. Такой процесс называется Back face culling (не отображать обратное).

Порядок отображения поверхностей определяется опциями параметра состояния рендера device.RenderState. CullMode, которые имеют значения перечисления

  1. Microsoft.DirectX.Direct3D.Cull. Clockwise - для вращения по часовой стрелке
  2. Microsoft.DirectX.Direct3D.Cull. CounterClockwise - для вращения против часовой стрелки
  3. Microsoft.DirectX.Direct3D.Cull. None - отключить механизм отсечения
  • Отключите в состоянии рендера механизм отсечения, добавив в настройки состояния устройства соответствующий код
// Установка камеры в сцену
        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;
    
            /*
            // Преобразование пространства для поворота треугольника относительно оси Z
            device.Transform.World = Matrix.RotationZ((float)Math.PI / 6.0F);
    
            // Преобразование пространства для непрерывного вращения треугольника
            device.Transform.World = Matrix.RotationZ(
                (System.Environment.TickCount / 450.0F) / (float)Math.PI);
    
            // Вращение с собственным счетчиком
            device.Transform.World = Matrix.RotationZ(angle / (float)Math.PI);
            */
    
            // Преобразование пространства для произвольных поворотов
            device.Transform.World = Matrix.RotationAxis(
                new Vector3(angle / ((float)Math.PI * 2.0F),
                            angle / ((float)Math.PI * 4.0F),
                            angle / ((float)Math.PI * 6.0F)),
                angle / (float)Math.PI);
            angle += 0.1F;
        }
Листинг 10.18. Отключить механизм отсечения невидимых сторон
  • Запустите приложение и убедитесь, что обратная сторона вращающегося треугольника теперь не исчезает
Иван Циферблат
Иван Циферблат
Россия, Таганрог, 36, 2000