ФЕДЕРАЛЬНОЕ АГЕНТСТВО ПО ОБРАЗОВАНИЮ
ГОСУДАРСТВЕННОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ
ВЫСШЕГО ПРОФЕССИОНАЛЬНОГО ОБРАЗОВАНИЯ
ЛИПЕЦКИЙ ГОСУДАРСТВЕННЫЙ ТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ
КАФЕДРА АВТОМАТИЗИРОВАННЫХ СИСТЕМ УПРАВЛЕНИЯ
Лабораторная работа №4
по дисциплине
«Компьютерная графика»
на тему:
«Реализация базовых алгоритмов трехмерной графики»
|
Студент |
|
|
|
Филатов А.А. |
|
||||||||
|
|
|
подпись, дата |
|
фамилия, инициалы |
|
||||||||
|
Группа |
|
АС-09-1 |
|
|
|
|
|||||||
|
|
|
|
|
|
|
||||||||
|
Принял |
|
|
|
|
|
||||||||
|
|
|
|
|
Назаркин О.А. |
|
||||||||
|
ученая степень, звание |
|
подпись, дата |
|
фамилия, инициалы |
|
Липецк 2010
1.Задание
Реализовать приложение, выполняющее построение полуцилиндра, освещенного точечным источником света.
2.Теория
В 3D-графике применяется понятие камеры, снимающей гипотетическое трехмерное пространство и выдающей его плоское изображение на экран. Камеру можно размещать в разных точках пространства, направлять на разные точки, изменять ее угол обзора и.т.д. Подлежащая отображению сцена создается в виде совокупности трехмерных графических примитивов, на которые разбиты все объекты сцены.
Модель трехмерного тела задана в некоторой системе координат, чаще всего связанной своим началом с какой-либо «ключевой» точкой тела (например с центром сферы). Эта локальная система координат может быть преобразована в глобальную (мировую) систему, которая и подлежит отображению. Преобразование координат задается матрицами. Отсутствие преобразования - единичная матрица, иначе матрица должна быть отличной от единичной.
Помимо мирового преобразования Direct3D применяет еще преобразование обзора и перспективное преобразование.
Преобразование обзора определяет позицию и направление камеры.
Здесь задается положение наблюдателя в пространстве, точка сцены, на которую направлен взгляд, и вектор направления вверх.
Перспективное преобразование определяет эффект перспективы - искажение параллельности линий.
3.Алгоритм
1. По заданным параметрам (количество примитивов основания цилиндра, высота, радиус, угол цилиндра (задает сектор цилиндра для отображения: для полуцилиндра угол равен 180˚)) расчет координат вершин цилиндра. Расчет нормалей.
2. Заполнение массива вершин в порядке, необходимом для вывода модели в режиме TriangleList.
3. Заполнение индексного массива, в котором номера всех вершин располагаются по порядку (ввиду учета особенности режима вывода TriangleList в первом пункте).
4. Вывод модели в режиме TriangleList.
4.Листинг программы
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
namespace DrawUserPrimitives
{
public class Game1 : Microsoft.Xna.Framework.Game
{
Matrix worldMatrix;
Matrix viewMatrix;
Matrix projectionMatrix;
BasicEffect basicEffect;
VertexDeclaration vertexDeclaration;
VertexPositionColor[] axis;
VertexPositionNormalTexture[] vertexCylinder, vertexRound1, vertexRound2;
VertexBuffer vertexBuffer;
static float aspect;
static float FOV = MathHelper.PiOver4;
Vector3 CamPosition = new Vector3(500.0f, 350.0f, 500.0f);
Vector3 CamTarget;
float CamRotation = (float)Math.PI * 7f / 6f;
bool isIzometric = false;
bool oFree = true;
bool pFree = true;
FillMode fillMode = FillMode.Solid;
int nRoundPolygon = 7;
float Angle=180f;
float Radius=100f;
float Height=200f;
short[] lineListIndices;
short[] triangleStripIndices;
short[] triangleFanIndices;
GraphicsDeviceManager graphics;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
aspect = (float)graphics.GraphicsDevice.Viewport.Width / (float)graphics.GraphicsDevice.Viewport.Height;
axis = new VertexPositionColor[6];
vertexRound1 = new VertexPositionNormalTexture[3*nRoundPolygon];
vertexRound2 = new VertexPositionNormalTexture[3*nRoundPolygon];
vertexCylinder = new VertexPositionNormalTexture[3*(2 * nRoundPolygon + 2)];
InitializeTransform(vertexRound1);
InitializeEffect(vertexRound1);
InitializePointListRound(vertexRound1, 0, Angle, Radius);
InitializePointListRound(vertexRound2, Height, Angle, Radius);
InitializePointListCylinder(vertexCylinder, vertexRound1, vertexRound2, Height, Angle, Radius);
InitializateAxis(axis);
InitIndexMassCoord(axis);
InitIndexMassCylinder(vertexRound1);
InitIndexMassRound(vertexRound1);
IsMouseVisible = true;
base.Initialize();
}
protected override void LoadContent()
{
}
protected override void UnloadContent()
{
}
private void InitializeTransform(VertexPositionNormalTexture[] pointList)
{
viewMatrix = Matrix.CreateLookAt(CamPosition, CamTarget, new Vector3(0.0f, 1.0f, 0.0f));
projectionMatrix = Matrix.CreatePerspectiveFieldOfView(FOV, aspect, 1.0f, 10000.0f);
}
private void InitializeEffect(VertexPositionNormalTexture[] pointList)
{
vertexDeclaration = new VertexDeclaration(GraphicsDevice,VertexPositionNormalTexture.VertexElements);
basicEffect = new BasicEffect(GraphicsDevice, null);
basicEffect.VertexColorEnabled = true;
worldMatrix = Matrix.CreateTranslation(GraphicsDevice.Viewport.Width / 2f - 150,GraphicsDevice.Viewport.Height / 2f - 50, 0);
basicEffect.World = worldMatrix;
basicEffect.View = viewMatrix;
basicEffect.Projection = projectionMatrix;
}
private void InitializateAxis(VertexPositionColor[] axis)
{
axis[0] = new VertexPositionColor(new Vector3(0, 0, 0) ,Color.Black);
axis[1] = new VertexPositionColor(new Vector3(10000, 0, 0), Color.Black);
axis[2] = new VertexPositionColor(new Vector3(0, 0, 0), Color.Black);
axis[3] = new VertexPositionColor(new Vector3(0, 10000, 0), Color.Black);
axis[4] = new VertexPositionColor(new Vector3(0, 0, 0), Color.Black);
axis[5] = new VertexPositionColor(new Vector3(0, 0, 10000), Color.Black);
}
private void InitializePointListCylinder(VertexPositionNormalTexture[] pointList, VertexPositionNormalTexture[] pointList1, VertexPositionNormalTexture[] pointList2, float Height, float Angle, float Radius)
{
vertexDeclaration = new VertexDeclaration(GraphicsDevice, VertexPositionNormalTexture.VertexElements);
for (int i = 0; i < nRoundPolygon-2; i++)
{
pointList[i * 6] = pointList1[3*i+1];
pointList[i * 6 + 1] = pointList2[3 * i + 1];
pointList[i * 6 + 2] = pointList1[3*i + 2];
pointList[i * 6 + 3] = pointList1[3 * i + 2];
pointList[i * 6 + 4] = pointList2[3 * i + 1];
pointList[i * 6 + 5] = pointList2[3 * i + 2];
Vector3 a = pointList2[3 * i + 1].Position, b = pointList1[3 * i + 2].Position;
a.Z = b.Z = 0;
pointList[i * 6].Normal = pointList[i * 6 + 1].Normal = pointList[i * 6 + 4].Normal = a;
pointList[i * 6 + 2].Normal = pointList[i * 6 + 3].Normal = pointList[i * 6 + 5].Normal = b;
};
pointList[6 * (nRoundPolygon - 2)] = pointList1[1];
pointList[6 * (nRoundPolygon - 2) + 1] = pointList2[1];
pointList[6 * (nRoundPolygon - 2) + 2] = pointList1[0];
pointList[6 * (nRoundPolygon - 2) + 3] = pointList1[0];
pointList[6 * (nRoundPolygon - 2) + 4] = pointList2[1];
pointList[6 * (nRoundPolygon - 2) + 5] = pointList2[0];
pointList[6 * (nRoundPolygon - 1)] = pointList1[3 * (nRoundPolygon - 3) + 2];
pointList[6 * (nRoundPolygon - 1) + 1] = pointList2[3 * (nRoundPolygon - 3) + 2];
pointList[6 * (nRoundPolygon - 1) + 2] = pointList1[0];
pointList[6 * (nRoundPolygon - 1) + 3] = pointList1[0];
pointList[6 * (nRoundPolygon - 1) + 4] = pointList2[3 * (nRoundPolygon - 3) + 2];
pointList[6 * (nRoundPolygon - 1) + 5] = pointList2[0];
for (int i = 0; i < 18; i++)
{
pointList[6 * (nRoundPolygon - 2) + i].TextureCoordinate = new Vector2(pointList[6 * (nRoundPolygon - 2) + i].Position.Y, pointList[6 * (nRoundPolygon - 2) + i].Position.Z);
pointList[6 * (nRoundPolygon - 2) + i].Normal = new Vector3(-1,0,0);
}
vertexBuffer = new VertexBuffer(GraphicsDevice, VertexPositionNormalTexture.SizeInBytes * (pointList.Length), BufferUsage.None);
vertexBuffer.SetData<VertexPositionNormalTexture>(pointList);
}
private void InitializePointListRound(VertexPositionNormalTexture[] pointList, float z, float Angle, float Radius)
{
vertexDeclaration = new VertexDeclaration(GraphicsDevice, VertexPositionNormalTexture.VertexElements);
for (int i = 0; i < nRoundPolygon-2; i++)
{
pointList[3*i] = new VertexPositionNormalTexture(new Vector3(0, 0, z), new Vector3(0, 0, 1), new Vector2(0, 0));
float angle = (float)i / (float)(nRoundPolygon - 2) * MathHelper.ToRadians(Angle);
float x = Radius * (float)Math.Sin(angle);
float y = Radius * (float)Math.Cos(angle);
pointList[3 * i + 1] = new VertexPositionNormalTexture(new Vector3(x, y, z), new Vector3(0, 0, 1), new Vector2(0, 0));
angle = (float)(i + 1) / (float)(nRoundPolygon - 2) * MathHelper.ToRadians(Angle);
x = Radius * (float)Math.Sin(angle);
y = Radius * (float)Math.Cos(angle);
pointList[3 * i + 2] = new VertexPositionNormalTexture(new Vector3(x, y, z), new Vector3(0, 0, 1), new Vector2(0, 0));
};
vertexBuffer = new VertexBuffer(GraphicsDevice, VertexPositionNormalTexture.SizeInBytes * (pointList.Length), BufferUsage.None);
vertexBuffer.SetData<VertexPositionNormalTexture>(pointList);
}
private void InitIndexMassCoord(VertexPositionColor[] pointList)
{
lineListIndices = new short[12];
for (int i = 0; i < 6; i++)
{
lineListIndices[i] = (short)(i);
}
}
private void InitIndexMassCylinder(VertexPositionNormalTexture[] pointList)
{
triangleStripIndices = new short[6 * nRoundPolygon + 6];
for (int i = 0; i < 6 * nRoundPolygon + 6; i++)
{
triangleStripIndices[i] = (short)i;
}
}
private void InitIndexMassRound(VertexPositionNormalTexture[] pointList)
{
triangleFanIndices = new short[3*nRoundPolygon];
for (int i = 0; i < 3*nRoundPolygon; i++)
{
triangleFanIndices[i] = (short)i;
}
}
protected override void Update(GameTime gameTime)
{
CamPref();
UpdateCamPosition();
Draw(gameTime);
base.Update(gameTime);
}
void UpdateCamPosition()
{
KeyboardState keyboardState = Keyboard.GetState();
//Поворот влево
if (keyboardState.IsKeyDown(Keys.NumPad7))
{
CamRotation += 0.05f;
}
//Поворот вправо
if (keyboardState.IsKeyDown(Keys.NumPad9))
{
CamRotation -= 0.05f;
}
//Передвижение влево
if (keyboardState.IsKeyDown(Keys.NumPad4))
{
CamPosition.X += 3.0f * (float)Math.Cos(CamRotation);
CamPosition.Z -= 3.0f * (float)Math.Sin(CamRotation);
}
//Передвижение вправо
if (keyboardState.IsKeyDown(Keys.NumPad6))
{
CamPosition.X -= 3.0f * (float)Math.Cos(CamRotation);
CamPosition.Z += 3.0f * (float)Math.Sin(CamRotation);
}
//Передвижение вперед
if (keyboardState.IsKeyDown(Keys.NumPad8))
{
CamPosition.X += 3.0f * (float)Math.Sin(CamRotation);
CamPosition.Z += 3.0f * (float)Math.Cos(CamRotation);
}
//Передвижение назад
if (keyboardState.IsKeyDown(Keys.NumPad5))
{
CamPosition.X -= 3.0f * (float)Math.Sin(CamRotation);
CamPosition.Z -= 3.0f * (float)Math.Cos(CamRotation);
}
//Передвижение вверх
if (keyboardState.IsKeyDown(Keys.NumPad1))
{
CamPosition.Y += 2.0f;
}
//Передвижение вниз
if (keyboardState.IsKeyDown(Keys.NumPad2))
{
CamPosition.Y -= 2.0f;
}
//Увеличение числа вершин
if (keyboardState.IsKeyDown(Keys.O) && oFree)
{
nRoundPolygon += 1;
Initialize();
}
//Уменьшение числа вершин
if (keyboardState.IsKeyDown(Keys.P) && pFree)
{
if (nRoundPolygon > 4)
nRoundPolygon -= 1;
Initialize();
}
//Увеличние угла
if (keyboardState.IsKeyDown(Keys.K))
{
if (Angle <= 359.0f)
{
Angle += 1f;
Initialize();
}
}
//Уменьшение угла
if (keyboardState.IsKeyDown(Keys.L))
{
if (Angle >= 2.0f)
{
Angle -= 1f;
Initialize();
}
}
if (keyboardState.IsKeyDown(Keys.D1))
{
fillMode = FillMode.Point;
}
if (keyboardState.IsKeyDown(Keys.D2))
{
fillMode = FillMode.WireFrame;
}
if (keyboardState.IsKeyDown(Keys.D3))
{
fillMode = FillMode.Solid;
}
//Сброс
if (keyboardState.IsKeyDown(Keys.C))
{
CamPosition = new Vector3(500.0f, 350.0f, 500.0f);
CamRotation = (float)Math.PI * 7f / 6f;
nRoundPolygon = 7;
Angle = 180f;
Radius = 100f;
Height = 200f;
fillMode = FillMode.Solid;
viewMatrix = Matrix.CreateLookAt(new Vector3(0.0f, -0.2f, 500.0f), Vector3.Zero, Vector3.Up);
projectionMatrix = Matrix.CreateOrthographicOffCenter(0, (float)GraphicsDevice.Viewport.Width, (float)GraphicsDevice.Viewport.Height, 0, 1.0f, 1000.0f);
Initialize();
}
//Изометрическая проекция
if (keyboardState.IsKeyDown(Keys.I))
{
CamPosition = new Vector3(500.0f, 500.0f, 500.0f);
CamTarget = Vector3.Zero;
isIzometric = true;
viewMatrix = Matrix.CreateLookAt(new Vector3(0.0f, -0.2f, 500.0f), Vector3.Zero, Vector3.Up);
projectionMatrix = Matrix.CreateOrthographicOffCenter(0, (float)GraphicsDevice.Viewport.Width, (float)GraphicsDevice.Viewport.Height, 0, 1.0f, 1000.0f);
Initialize();
}
if (keyboardState.IsKeyUp(Keys.I))
{
if (isIzometric)
{
CamPosition = new Vector3(500.0f, 350.0f, 500.0f);
CamRotation = (float)Math.PI * 7f / 6f;
viewMatrix = Matrix.CreateLookAt(new Vector3(0.0f, -0.2f, 500.0f), Vector3.Zero, Vector3.Up);
projectionMatrix = Matrix.CreateOrthographicOffCenter(0, (float)GraphicsDevice.Viewport.Width, (float)GraphicsDevice.Viewport.Height, 0, 1.0f, 1000.0f);
Initialize();
isIzometric = false;
}
}
oFree = keyboardState.IsKeyUp(Keys.O);
pFree = keyboardState.IsKeyUp(Keys.P);
}
void CamPref()
{
if (!isIzometric)
{
CamTarget.X = CamPosition.X + (float)Math.Sin(CamRotation);
CamTarget.Y = CamPosition.Y;
CamTarget.Z = CamPosition.Z + (float)Math.Cos(CamRotation);
}
viewMatrix = Matrix.CreateLookAt(CamPosition, CamTarget, Vector3.Up);
projectionMatrix = Matrix.CreatePerspectiveFieldOfView(FOV, aspect, 1.0f, 10000.0f);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.SteelBlue);
GraphicsDevice.VertexDeclaration = vertexDeclaration;
basicEffect.Begin();
basicEffect.EnableDefaultLighting();
basicEffect.DirectionalLight0.Direction = new Vector3(-1, 0, 0);
basicEffect.DirectionalLight0.DiffuseColor = Vector3.One;
basicEffect.DirectionalLight1.Direction = new Vector3(0, 1, 0);
basicEffect.DirectionalLight2.Direction = new Vector3(-1, -1, -1);
basicEffect.DirectionalLight0.Enabled = basicEffect.DirectionalLight1.Enabled = basicEffect.DirectionalLight2.Enabled = true;
basicEffect.View = viewMatrix;
basicEffect.World = worldMatrix;
basicEffect.Projection = projectionMatrix;
foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
{
pass.Begin();
GraphicsDevice.RenderState.FillMode = fillMode;
GraphicsDevice.RenderState.CullMode = CullMode.None;
DrawRound(vertexRound1);
DrawRound(vertexRound2);
DrawCylinder(vertexCylinder);
DrawCoord(axis);
pass.End();
}
basicEffect.End();
base.Draw(gameTime);
}
private void DrawnRoundPolygon(VertexPositionNormalTexture[] pointList)
{
GraphicsDevice.RenderState.PointSize = 10;
GraphicsDevice.DrawUserPrimitives<VertexPositionNormalTexture>(PrimitiveType.PointList,pointList,0,nRoundPolygon);
}
private void DrawCoord(VertexPositionColor[] axis)
{
GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionColor>(PrimitiveType.LineList, axis, 0, 6, lineListIndices, 0, 3);
}
private void DrawCylinder(VertexPositionNormalTexture[] pointList)
{
GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionNormalTexture>(PrimitiveType.TriangleList, pointList, 0, 6 * nRoundPolygon + 6, triangleStripIndices, 0, 2 * nRoundPolygon);
}
private void DrawRound(VertexPositionNormalTexture[] pointList)
{
GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionNormalTexture>(PrimitiveType.TriangleList, pointList, 0, 3*nRoundPolygon, triangleFanIndices, 0, nRoundPolygon);
}
}
}