Украина, Киев |
Рендеринг вращающихся кубов в DirectX
Добавление функциональности остановки вращения
Добавим к приложению возможность останавливать и возобнавлять вращение куба, чтобы можно было полюбоваться на созданную красоту.
- В конструкторе формы Form1() подпишитесь на событие Click самой формы, начиная вводить код this.Click+=, а дальше нажимайте клавишу Tab до создания заготовки обработчика Form1_Click()
- Объявите и сразу инициализируйте в классе Form1 локальное булево поле flagRotate, а также заполните обработчик
bool flagRotate = true; void Form1_Click(object sender, EventArgs e) { flagRotate = !flagRotate; }Листинг 12.8. Флаг и обработчик остановки вращения
if (flagRotate) angle += 0.1F;Листинг 12.9. Приращение угла поворота в зависимости от состояния флага
- Запустите приложение и убедитесь, что щелчки на клиентской области формы управляют вращением куба
Создание трех вращающихся кубов
Создадим сцену с тремя вращающимися кубами.
- Откройте через панель Solution Explorer файл Program.cs, скопируйте из него код точки входа приложения Main() и разместите его в классе Form1 перед конструктором класса
public partial class Form1 : Form { [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); using (Form1 frm = new Form1()) { // Показать форму перед созданием движка frm.Show(); // Создать и инициализировать графический движок // нашей функцией frm.InitializeGraphics(); // Войти в цикл сообщений Application.Run(frm); } } public Form1() { InitializeComponent(); ..... } ..... }Листинг 12.10. Точка входа приложения, перенесенная в класс Form1
- Удалите совсем файл Program.cs
- Откройте панель Solution Explorer и щелкните правой кнопкой мыши на файле Form1.cs. Выполните команду Copy, затем щелкните правой кнопкой мыши на узле проекта RenderCube и выполните команду Paste контекстного меню.
- Измените имя полученной копии на Form2.cs
- Откройте файл Form2.cs в режиме View Code и замените все вхождения Form1 на Form2, вызвав окно замены комбинацией клавиш Ctrl-H и настроив его опцию Look in в значение Current Document
- Откройте файл Form2.designer.cs и выполните в нем предыдущую замену всех вхождений Form1 на Form2
- В меню Project оболочки выполните последнюю команду RenderCube Properties, чтобы вызвать окно свойств проекта. Установите свойство Startup object в значение начального старта второй формы
- Запустите приложение, чтобы убедиться, что в скопированной форме Form2 предыдущий код работает
Теперь мы можем приступить к усложнению кода, который будет реализован в форме Form2 приложения. Долее мы хотим отобразить три вращающихся раскрашенных куба. Было бы неэффективно создавать три вершинных буфера (один буфер для каждого объекта). Можно использовать один и тот же вершинный буфер многократно, взяв за основу первый куб, а для двух других кубов перед рисованием сдвигать координаты первого куба в мировой системе координат.
- В конце функции SetupCamera() найдите секцию кода
// Преобразование пространства для произвольных поворотов device.Transform.World = Matrix.RotationYawPitchRoll( angle / (float)Math.PI, angle / (float)Math.PI * 2.0F, angle / (float)Math.PI);Листинг 12.11. Секция, которую нужно вырезать и перенести в OnPaint()
- Перенесите этот код в функцию OnPaint() в место, которое находится перед функцией
device.DrawPrimitives(PrimitiveType.TriangleList, 0, 12);
- Добавьте после этой функции следующий код для рисования еще двух кубов, чтобы общий код секции "Сформировать сцену" выглядел так
protected override void OnPaint(PaintEventArgs e) { // Очистить цветом клиентскую область формы device.Clear(ClearFlags.Target, System.Drawing.Color.CornflowerBlue, 1.0F, 0); // Вызов нашей функции установки камеры SetupCamera(); // Сформировать сцену с учетом параметров // "положение-нормаль-цвет" device.BeginScene(); // Установить формат обработки вершин при отображении device.VertexFormat = CustomVertex.PositionColored.Format; // Заполнить устройство данными из буфера device.SetStreamSource(0, vb, 0); // Преобразование пространства для произвольных поворотов // Первый куб device.Transform.World = Matrix.RotationYawPitchRoll( angle / (float)Math.PI, angle / (float)Math.PI * 2.0F, angle / (float)Math.PI); device.DrawPrimitives(PrimitiveType.TriangleList, 0, 12); // Второй куб 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.DrawPrimitives(PrimitiveType.TriangleList, 0, 12); // Третий куб 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.DrawPrimitives(PrimitiveType.TriangleList, 0, 12); device.EndScene(); // Показать буфер кадра device.Present(); // Принудительно перерисовать this.Invalidate(); }Листинг 12.12. Добавленный код рисования кубов в функции OnPaint()
В коде перед рисованием очередного куба мы осуществляем преобразование вершин объекта в устройстве, применяя последовательно матрицу вращения Matrix.RotationYawPitchRoll() и матрицу перемещения Matrix.Translation(). Второй куб мы сдвигаем на 4 единицы влево относительно первого, третий - на 4 единицы вправо.
- Запустите приложение и увеличьте размер окна по ширине, должна получиться следующая картина
Поскольку наши текущие параметры камеры настроены на первый куб, то для охвата всей сцены нужно переместить камеру немного назад. Заодно уменьшим скорость вращения кубов.
- Внесите в код функции 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, 15.0F), // Положение камеры new Vector3(), // Положение объекта текущее new Vector3(0, 1, 0)); // Направление камеры // Освещение device.RenderState.Lighting = false; if (flagRotate) angle += 0.02F; }Листинг 12.13. Отодвинем камеру и уменьшим скорость вращения
- Постройте приложение, должен получиться примерно такой результат