| Невозможно пройти тесты, в окне с вопросами пусто |
Основы трехмерной графики в XNA
Цель работы: Научиться выводить трёхмерные объекты на игровой экран
23.1. Рисование трехмерных объектов средствами XNA
Рассмотрим особенности рисования трехмерных объектов средствами XNA на примере проекта P16_1. Здесь мы рассматриваем использование вершинного буфера для вывода куба. Пример подготовлен на основе кода, доступного в рамках Microsoft Permissive License (Ms-PL).
В листинге 23.1 вы можете найти код класса Game1 проекта P14_1. Код подробно прокомментирован.
Отметим, что для вывода изображения нам необходимо выполнить следующие шаги:
- Установить мировую, проекционную и видовую матрицы.
- Создать и настроить объект типа BasicEffect для вывода изображений.
- Создать наборы вершин (в нашем случае они представлены данными о координате, нормали и текстуре), которые мы будем использовать при выводе.
- Создать и заполнить вершинный буфер, установить его в объект GraphicDevice для вывода изображения.
- Вывести изображение из буфера, пользуясь инструментами объекта типа BasicEffect, который, фактически, является одной из доступных в XNA шейдерных программ.
При выводе изображения в методе Draw необходимо очищать экран от предыдущего вывода – иначе он покроется копиями изображений.
Мы используем мировую матрицу для вращения выведенной сцены при каждом проходе цикла Update.
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace P16_1
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
//Мировая матрица
Matrix worldMatrix;
//Матрица вида
Matrix viewMatrix;
//Проекционная матрица
Matrix projectionMatrix;
//Информация по вершинам объекта
VertexPositionNormalTexture[] cubeVertices;
//Объявление вершин, используется для вывода изображения
VertexDeclaration vertexDeclaration;
//Вершинный буфер
VertexBuffer vertexBuffer;
//Объект для вывода изображений
BasicEffect basicEffect;
//Количество вершин
const int number_of_vertices = 36;
GraphicsDeviceManager graphics;
//Переменные для хранения текущего значения
//поворота мировой матрицы
float wMX = 0;
float wMY = 0;
float wMZ = 0;
//Управление матрицей вида
float distanceView = 5;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
//Вывод на полный экран
graphics.IsFullScreen = true;
}
//Создание вертексного (вершинного) буфера
private void CreateVertexBuffer()
{
//Задаёт объявление вершин
//Оно содержит новый массив типа VertexElement, содержащий сведения
vertexDeclaration = new VertexDeclaration(new VertexElement[]
{
//О координате
new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),
//О нормали
new VertexElement(12, VertexElementFormat.Vector3, VertexElementUsage.Normal, 0),
//О Координате текстуры
new VertexElement(24, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0)
}
);
//Создаём вертексный буфер, используя информацию о
//графическом устройстве
//Структуре описания вершин
//Количестве вершин
vertexBuffer = new VertexBuffer(
graphics.GraphicsDevice,
vertexDeclaration,
number_of_vertices,
BufferUsage.None
);
//Новый массив вершин куба
cubeVertices = new VertexPositionNormalTexture[number_of_vertices];
//Создаём куб
InitializeCube();
//Устанавливаем данные для вертексного буфера
vertexBuffer.SetData<VertexPositionNormalTexture>(cubeVertices);
//Подключаем вертексный буфер к устройству
graphics.GraphicsDevice.SetVertexBuffer(vertexBuffer);
}
protected override void Initialize()
{
CreateVertexBuffer();
base.Initialize();
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
InitializeTransform();
InitializeEffect();
}
/// <summary>
/// Инициализирует матрицы, которые можно использовать при
/// </summary>
private void InitializeTransform()
{
//мировая матрица, содержащая 1 по диагонали
//она не влияет на состояние объекта
worldMatrix = Matrix.Identity;
//матрица вида
//При ее создании задаем следующие параметры
//1 - положение камеры
//2 - направление камеры
//3 - что считать "верхом" для камеры
viewMatrix = Matrix.CreateLookAt(new Vector3(0.0f, 0.0f, distanceView), Vector3.Zero, Vector3.Up);
//Матрица проекции
//При ее создании задаем следующие параметры:
//1 - угол зрения в радианах
//Соотношение сторон пикселей экрана
//Ближний план пространства
//Дальний план пространства
projectionMatrix = Matrix.CreatePerspectiveFieldOfView(
MathHelper.ToRadians(45),
(float)GraphicsDevice.Viewport.Width /
(float)GraphicsDevice.Viewport.Height,
1.0f, 30.0f);
}
/// <summary>
/// Инициализация базовых эффектов (настроек параметров и способов обработки)
/// используемых для работы с трехмерной моделью
/// </summary>
private void InitializeEffect()
{
//Создание объекта для вывода изображений
basicEffect = new BasicEffect(graphics.GraphicsDevice);
//Установка матриц
basicEffect.World = worldMatrix;
basicEffect.View = viewMatrix;
basicEffect.Projection = projectionMatrix;
// Цвета различных видов освещения
basicEffect.AmbientLightColor = new Vector3(0.1f, 0.1f, 0.1f);
basicEffect.DiffuseColor = new Vector3(1.0f, 1.0f, 1.0f);
basicEffect.SpecularColor = new Vector3(0.25f, 0.25f, 0.25f);
basicEffect.SpecularPower = 5.0f;
basicEffect.Alpha = 1.0f;
//Включим освещение
basicEffect.LightingEnabled = true;
if (basicEffect.LightingEnabled)
{
basicEffect.DirectionalLight0.Enabled = true; // активируем каждый источник света отдельно
if (basicEffect.DirectionalLight0.Enabled)
{
// Направление по Х
basicEffect.DirectionalLight0.DiffuseColor = new Vector3(1, 0, 0); // диапазон от 0 до 1
basicEffect.DirectionalLight0.Direction = Vector3.Normalize(new Vector3(-1, 0, 0));
// Направление от источника света к началу координат сцены
basicEffect.DirectionalLight0.SpecularColor = Vector3.One;
}
basicEffect.DirectionalLight1.Enabled = true;
if (basicEffect.DirectionalLight1.Enabled)
{
// Направление по У
basicEffect.DirectionalLight1.DiffuseColor = new Vector3(0, 0.75f, 0);
basicEffect.DirectionalLight1.Direction = Vector3.Normalize(new Vector3(0, -1, 0));
basicEffect.DirectionalLight1.SpecularColor = Vector3.One;
}
basicEffect.DirectionalLight2.Enabled = true;
if (basicEffect.DirectionalLight2.Enabled)
{
// Направление по Z
basicEffect.DirectionalLight2.DiffuseColor = new Vector3(0, 0, 0.5f);
basicEffect.DirectionalLight2.Direction = Vector3.Normalize(new Vector3(0, 0, -1));
basicEffect.DirectionalLight2.SpecularColor = Vector3.One;
}
}
}
/// <summary>
/// Инициализирует вершины и индексы 3D-модели
/// </summary>
private void InitializeCube()
{
//Вершины
Vector3 topLeftFront = new Vector3(-1.0f, 1.0f, 1.0f);
Vector3 bottomLeftFront = new Vector3(-1.0f, -1.0f, 1.0f);
Vector3 topRightFront = new Vector3(1.0f, 1.0f, 1.0f);
Vector3 bottomRightFront = new Vector3(1.0f, -1.0f, 1.0f);
Vector3 topLeftBack = new Vector3(-1.0f, 1.0f, -1.0f);
Vector3 topRightBack = new Vector3(1.0f, 1.0f, -1.0f);
Vector3 bottomLeftBack = new Vector3(-1.0f, -1.0f, -1.0f);
Vector3 bottomRightBack = new Vector3(1.0f, -1.0f, -1.0f);
//Текстуры
Vector2 textureTopLeft = new Vector2(0.0f, 0.0f);
Vector2 textureTopRight = new Vector2(1.0f, 0.0f);
Vector2 textureBottomLeft = new Vector2(0.0f, 1.0f);
Vector2 textureBottomRight = new Vector2(1.0f, 1.0f);
//Нормали
Vector3 frontNormal = new Vector3(0.0f, 0.0f, 1.0f);
Vector3 backNormal = new Vector3(0.0f, 0.0f, -1.0f);
Vector3 topNormal = new Vector3(0.0f, 1.0f, 0.0f);
Vector3 bottomNormal = new Vector3(0.0f, -1.0f, 0.0f);
Vector3 leftNormal = new Vector3(-1.0f, 0.0f, 0.0f);
Vector3 rightNormal = new Vector3(1.0f, 0.0f, 0.0f);
// Передняя стенка, 6 вершин для 2-х треугольников
//из которых она состоит
cubeVertices[0] =
new VertexPositionNormalTexture(
topLeftFront, frontNormal, textureTopLeft);
cubeVertices[1] =
new VertexPositionNormalTexture(
bottomLeftFront, frontNormal, textureBottomLeft);
cubeVertices[2] =
new VertexPositionNormalTexture(
topRightFront, frontNormal, textureTopRight);
cubeVertices[3] =
new VertexPositionNormalTexture(
bottomLeftFront, frontNormal, textureBottomLeft);
cubeVertices[4] =
new VertexPositionNormalTexture(
bottomRightFront, frontNormal, textureBottomRight);
cubeVertices[5] =
new VertexPositionNormalTexture(
topRightFront, frontNormal, textureTopRight);
// Задняя стенка
cubeVertices[6] =
new VertexPositionNormalTexture(
topLeftBack, backNormal, textureTopRight);
cubeVertices[7] =
new VertexPositionNormalTexture(
topRightBack, backNormal, textureTopLeft);
cubeVertices[8] =
new VertexPositionNormalTexture(
bottomLeftBack, backNormal, textureBottomRight);
cubeVertices[9] =
new VertexPositionNormalTexture(
bottomLeftBack, backNormal, textureBottomRight);
cubeVertices[10] =
new VertexPositionNormalTexture(
topRightBack, backNormal, textureTopLeft);
cubeVertices[11] =
new VertexPositionNormalTexture(
bottomRightBack, backNormal, textureBottomLeft);
// Верхняя стенка
cubeVertices[12] =
new VertexPositionNormalTexture(
topLeftFront, topNormal, textureBottomLeft);
cubeVertices[13] =
new VertexPositionNormalTexture(
topRightBack, topNormal, textureTopRight);
cubeVertices[14] =
new VertexPositionNormalTexture(
topLeftBack, topNormal, textureTopLeft);
cubeVertices[15] =
new VertexPositionNormalTexture(
topLeftFront, topNormal, textureBottomLeft);
cubeVertices[16] =
new VertexPositionNormalTexture(
topRightFront, topNormal, textureBottomRight);
cubeVertices[17] =
new VertexPositionNormalTexture(
topRightBack, topNormal, textureTopRight);
// Нижняя стенка
cubeVertices[18] =
new VertexPositionNormalTexture(
bottomLeftFront, bottomNormal, textureTopLeft);
cubeVertices[19] =
new VertexPositionNormalTexture(
bottomLeftBack, bottomNormal, textureBottomLeft);
cubeVertices[20] =
new VertexPositionNormalTexture(
bottomRightBack, bottomNormal, textureBottomRight);
cubeVertices[21] =
new VertexPositionNormalTexture(
bottomLeftFront, bottomNormal, textureTopLeft);
cubeVertices[22] =
new VertexPositionNormalTexture(
bottomRightBack, bottomNormal, textureBottomRight);
cubeVertices[23] =
new VertexPositionNormalTexture(
bottomRightFront, bottomNormal, textureTopRight);
// Левая стенка
cubeVertices[24] =
new VertexPositionNormalTexture(
topLeftFront, leftNormal, textureTopRight);
cubeVertices[25] =
new VertexPositionNormalTexture(
bottomLeftBack, leftNormal, textureBottomLeft);
cubeVertices[26] =
new VertexPositionNormalTexture(
bottomLeftFront, leftNormal, textureBottomRight);
cubeVertices[27] =
new VertexPositionNormalTexture(
topLeftBack, leftNormal, textureTopLeft);
cubeVertices[28] =
new VertexPositionNormalTexture(
bottomLeftBack, leftNormal, textureBottomLeft);
cubeVertices[29] =
new VertexPositionNormalTexture(
topLeftFront, leftNormal, textureTopRight);
// Правая стенка
cubeVertices[30] =
new VertexPositionNormalTexture(
topRightFront, rightNormal, textureTopLeft);
cubeVertices[31] =
new VertexPositionNormalTexture(
bottomRightFront, rightNormal, textureBottomLeft);
cubeVertices[32] =
new VertexPositionNormalTexture(
bottomRightBack, rightNormal, textureBottomRight);
cubeVertices[33] =
new VertexPositionNormalTexture(
topRightBack, rightNormal, textureTopRight);
cubeVertices[34] =
new VertexPositionNormalTexture(
topRightFront, rightNormal, textureTopLeft);
cubeVertices[35] =
new VertexPositionNormalTexture(
bottomRightBack, rightNormal, textureBottomRight);
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
//Уменьшаем градус значение поворота матрицы
wMX = wMX - 1f;
wMY = wMY - 2f;
wMZ = wMZ - 3f;
//Модифицируем мировую матрицу
worldMatrix = Matrix.CreateRotationX(MathHelper.ToRadians(wMX)) * Matrix.CreateRotationY(MathHelper.ToRadians(wMY)) *
Matrix.CreateRotationZ(MathHelper.ToRadians(wMZ));
//Меняем мировую матрицу
basicEffect.World = worldMatrix;
//Если расстояние меньше 15
if (distanceView <= 15.0f)
{
//Увеличим
distanceView = distanceView + 0.01f;
}
//Модифицируем матрицу вида
viewMatrix = Matrix.CreateLookAt(new Vector3(0.0f, 0.0f, distanceView), Vector3.Zero, Vector3.Up);
//Заменим матрицу вида
basicEffect.View = viewMatrix;
base.Update(gameTime);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
//Очистим экран
graphics.GraphicsDevice.Clear(Color.SteelBlue);
//Используется для определения того, как преобразовывать векторные данные в пиксельные
RasterizerState rasterizerState1 = new RasterizerState();
//Отключаем отсечени
rasterizerState1.CullMode = CullMode.None;
//Установим настроенное состояние прорисовки
graphics.GraphicsDevice.RasterizerState = rasterizerState1;
//Для каждого прохода в эффекте
foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
{
//Применить проход
pass.Apply();
//Вывести
graphics.GraphicsDevice.DrawPrimitives(
PrimitiveType.TriangleList,
0,
12
);
}
base.Draw(gameTime);
}
}
}
Листинг
23.1.
Код класса Game1
На рис. 23.1 вы можете видеть игровое окно
