Более совершенные технологии рендеринга в DirectX
Создание нескольких вращающихся кубов с применением буфера глубины
Буфер глубины ( depth buffer, Z-буфер, W-буфер ) применяется в приложениях Direct3D для хранения информации о глубине отображаемого объекта. Эта информация используется в процессе растеризации (из векторной в растровую) для определения того, насколько пикселы перекрывают друг друга. На данном этапе наше приложение не имеет буфера глубины, но в этом разделе мы его введем и используем.
- Через контекстное меню панели Solution Explorer получите копию файла Form2 и переименуйте ее в Form3.cs
- Через контекстное меню панели Solution Explorer откройте файл Form3.cs в режиме View Code
- В панели Solution Explorer двойным щелчком на файле Form3.designer.cs откройте его в рабочей области редактора оболочки
- Строго проследите, чтобы открытыми были только файлы Form3.cs Form3.designer.cs и замените в них все вхождения Form2 на Form3 через окно замены, которое можно вызвать комбинацией клавиш Ctrl-H. Предварительно настройте опции окна замены по приведенному снимку
- Выполните команду меню Project/RenderBest Properties и установите форму Form3 стартовой
- Запустите проект на выполнение и убедитесь, что Form3 работает в прежнем варианте Form2, т.е. целостность кода сохранилась и мы можем продолжать развитие проекта
Вначале вместо одного куба создадим 9 кубов и для правильного их отображения отодвинем камеру.
- Добавьте в класс Form3 новую функцию DrawCubes() для рисования 9 кубов
private void DrawCubes() { // // Средний ряд // device.Transform.World = Matrix.RotationYawPitchRoll( angle / (float)Math.PI, angle / (float)Math.PI * 2.0f, angle / (float)Math.PI / 4.0f) * Matrix.Translation(0.0f, 0.0f, 0.0f); // По центру device.DrawIndexedPrimitives( PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3); 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.DrawIndexedPrimitives( PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3); 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.DrawIndexedPrimitives( PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3); // // Нижний ряд // device.Transform.World = Matrix.RotationYawPitchRoll( angle / (float)Math.PI, angle / (float)Math.PI * 2.0f, angle / (float)Math.PI / 4.0f) * Matrix.Translation(0.0f, -4.0f, 0.0f); // По центру device.DrawIndexedPrimitives( PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3); 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, -4.0f, 0.0f); // Сдвиг вправо device.DrawIndexedPrimitives( PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3); 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, -4.0f, 0.0f); // Сдвиг влево device.DrawIndexedPrimitives( PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3); // // Верхний ряд // device.Transform.World = Matrix.RotationYawPitchRoll( angle / (float)Math.PI, angle / (float)Math.PI * 2.0f, angle / (float)Math.PI / 4.0f) * Matrix.Translation(0.0f, 4.0f, 0.0f); // По центру device.DrawIndexedPrimitives( PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3); 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, 4.0f, 0.0f); // Сдвиг вправо device.DrawIndexedPrimitives( PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3); 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, 4.0f, 0.0f); // Сдвиг влево device.DrawIndexedPrimitives( PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3); if (flagRotate) angle += 0.02F; }Листинг 13.19. Код функции рисования 9 кубов DrawCubes()
- Удалите из функции SetupCamera() две последних секции, ответственные за вращение куба, поскольку эту функциональность мы включили в новую функцию DrawCubes()
// Установка камеры в сцену 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.02F; }Листинг 13.20. Удаляем код в конце функции SetupCamera()
- Замените в секции формирования сцены функции OnPaint() вызов функции DrawIndexedPrimitives() на вызов функции DrawCubes()
// Сформировать сцену с учетом параметров // "положение-нормаль-цвет" device.BeginScene(); // Установить формат обработки вершин при отображении device.VertexFormat = CustomVertex.PositionColored.Format; // Нарисовать треугольник по данным из буфера device.SetStreamSource(0, vb, 0); device.Indices = ib; DrawCubes(); device.EndScene();Листинг 13.21. Код формирования сцены в функции OnPaint()
- В функции SetupCamera() отодвиньте камеру, чтобы была видна вся сцена с 9 кубами
// Установка камеры в сцену 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; }Листинг 13.22. Увеличение расстояния установки камеры в функции SetupCamera()
- Запустите приложение и получите примерно следующее
Настало время перейти к использованию буферов глубины объектов. Давайте добавим еще три куба к нашей сцене вращающихся кубов.
- Добавьте в конец функции DrawCubes() код для рисования еще трех кубов
private void DrawCubes() { // // Средний ряд // //................................................. // // Дополнительные кубы device.Transform.World = Matrix.RotationYawPitchRoll( angle / (float)Math.PI, angle / (float)Math.PI * 2.0f, angle / (float)Math.PI / 4.0f) * Matrix.Translation( 0.0f, (float)Math.Cos(angle), (float)Math.Sin(angle)); device.DrawIndexedPrimitives( PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3); 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, (float)Math.Sin(angle), (float)Math.Cos(angle)); device.DrawIndexedPrimitives( PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3); device.Transform.World = Matrix.RotationYawPitchRoll( angle / (float)Math.PI, angle / (float)Math.PI * 2.0f, angle / (float)Math.PI / 2.0f) * Matrix.Translation( -4.0f, (float)Math.Cos(angle), (float)Math.Sin(angle)); device.DrawIndexedPrimitives( PrimitiveType.TriangleList, 0, 0, 8, 0, indices.Length / 3); if (flagRotate) angle += 0.02F; }Листинг 13.23. Добавление в конец функции DrawCubes() кода рисования трех новых кубов
- Запустите приложение и увидите по горизонтальному среднему ряду еще дополнительных три куба
Здесь мы хотим получить вращение дополнительных кубов вокруг трех центральных кубов, расположенных по горизонтали. Чтобы этого достичь, следует добавить буфер глубины в устройство device.
Существуют разные форматы буферов глубины, но большинство современных графических плат поддерживают 16-разрядные буферы глубины. Поэтому используем именно такую глубину.
- Добавьте в функцию InitializeGraphics() перед кодом создания устройства параметры настройки буфера глубины
public void InitializeGraphics() { // Создание объекта и настройка параметров представления // Создать объект параметров представления PresentParameters presentParams = new PresentParameters(); // Установить оконный режим presentParams.Windowed = true; // Сбрасывать содержимое буфера, если он не готов к представлению presentParams.SwapEffect = SwapEffect.Discard; // Настройка буфера глубины для параметров устройства presentParams.EnableAutoDepthStencil = true; presentParams.AutoDepthStencilFormat = Microsoft.DirectX.Direct3D.DepthFormat.D16; // Создать объект устройства и сохранить ссылку на него device = new Device(0, DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, presentParams); // Создать вершинный буфер // ................................................. }Листинг 13.24. Параметры настройки буфера глубины в функции InitializeGraphics()
- Запускаем приложение и видим, что вместо кубов появились их осколки
Исчезновение сцены после добавления параметров буфера глубины произошло потому, что этот буфер не был очищен
- Добавьте к первой секции кода функции OnPaint() дополнительный флаг очистки буфера глубины
protected override void OnPaint(PaintEventArgs e) { // Очистить цветом клиентскую область формы device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, System.Drawing.Color.CornflowerBlue, 1.0F, 0); .......................................... }Листинг 13.25. Добавление флага сброса буфера глубины устройства device в функцию OnPaint()
- Запустите приложение и мы увидим фантастическую картину, иллюстрирующую использование буфера глубины
Краткие выводы
В данной лабораторной работе мы познакомились с более совершенными методами рендеринга, а именно
- Посмотрели на работу различных типов примитивов, приминяемых для рендеринга объектов.
- Попробовали использовать индексные буферы, позволяющие существенно сократить объем данных за счет устранения дублирования описания параметров вершин.
- Применили буфер глубины устройства для более реалистичного (или фантастического) отображения объектов.