#3d #xna #directx #monogame #xna-4.0
#3D #xna #directx #моногейм #xna-4.0
Вопрос:
Я пытаюсь правильно вывести на экран несколько 3D-моделей с помощью подвижной камеры, но сталкиваюсь с двумя проблемами. Первая проблема видна сразу: модели должны быть кубами, но рисуются в виде прямоугольных призм. Похоже, что они сжимаются, но я не могу найти причину для этого.
Вторая проблема проявляется при взгляде сверху вниз на «кубы». Хотя они должны просто вращаться, кажется, что при вращении они растягиваются странным образом.
Все еще смотрит вниз, но повернут на 90 градусов CW
Вот код моей камеры:
public class Camera
{
private Driver Driver;
private int ScreenWidth { get { return Driver.GraphicsDevice.Viewport.Width; } }
private int ScreenHeight { get { return Driver.GraphicsDevice.Viewport.Height; } }
public Matrix ViewMatrix { get; private set; }
public Matrix ProjectionMatrix { get; private set; }
private const float NearClippingPlane = 0.1f;
private const float FarClippingPlane = 200.0f;
public Vector3 Position { get; private set; }
private Vector3 _direction; // Do not use _direction!
private Vector3 Direction
{
get { return _direction; }
set
{
if (value == Vector3.Zero) { }
else
{
var normalizedTemp = value;
normalizedTemp.Normalize();
if (normalizedTemp.Y > -.9999f) _direction = normalizedTemp;
}
}
}
private Vector3 Up;
private const float MovementSpeed = 10f;
private const float PanSpeed = 10f;
private MouseState previousMouseState;
private Vector3 HorizontalPlaneDirection
{
get
{
var direction = new Vector3(Direction.X, 0, Direction.Z);
if (direction != Vector3.Zero)
direction.Normalize();
return direction;
}
}
public Camera(Driver game, Vector3 position, Vector3 target, Vector3 up)
{
Driver = game;
// Build camera view matrix
Position = position;
Direction = target - position;
Up = up;
CreateLookAt();
ProjectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(60), ScreenWidth / ScreenHeight, NearClippingPlane, FarClippingPlane);
}
public void Initialize()
{
// Set mouse position and do initial get state
Mouse.SetPosition(ScreenWidth / 2, ScreenHeight / 2);
previousMouseState = Mouse.GetState();
}
public void Update(GameTime gameTime, KeyboardState keyboard, MouseState mouse)
{
float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
float moveSpeed = MovementSpeed * elapsed;
float panSpeed = PanSpeed * elapsed;
if (keyboard.IsKeyDown(Keys.W))
Position = HorizontalPlaneDirection * moveSpeed;
if (keyboard.IsKeyDown(Keys.S))
Position -= HorizontalPlaneDirection * moveSpeed;
if (keyboard.IsKeyDown(Keys.A))
Position = Vector3.Cross(Up, HorizontalPlaneDirection) * moveSpeed;
if (keyboard.IsKeyDown(Keys.D))
Position -= Vector3.Cross(Up, HorizontalPlaneDirection) * moveSpeed;
if (keyboard.IsKeyDown(Keys.Space))
Position = Up * moveSpeed;
if (keyboard.IsKeyDown(Keys.LeftShift))
Position -= Up * moveSpeed;
// Rotation in the world
Direction = Vector3.Transform(Direction, Matrix.CreateFromAxisAngle(Up, -MathHelper.ToRadians(1) * (mouse.X - previousMouseState.X) * panSpeed));
Direction = Vector3.Transform(Direction, Matrix.CreateFromAxisAngle(Vector3.Cross(Up, HorizontalPlaneDirection), MathHelper.ToRadians(1) * (mouse.Y - previousMouseState.Y) * panSpeed));
Driver.ResetMouseState();
// Reset prevMouseState
previousMouseState = Mouse.GetState();
CreateLookAt();
}
private void CreateLookAt()
{
ViewMatrix = Matrix.CreateLookAt(Position, Position Direction, Up);
}
}
И код для рисования модели:
public void Draw(Camera Camera)
{
foreach (var mesh in Model.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
//effect.EnableDefaultLighting();
//effect.PreferPerPixelLighting = true;
effect.World = GetWorldMatrix();
effect.View = Camera.ViewMatrix;
effect.Projection = Camera.ProjectionMatrix;
}
mesh.Draw();
}
}
Я знаю, что фрагменты кода длинные и, вероятно, с трудом разбираются, но я очень ценю любую помощь. Спасибо!
Ответ №1:
Ну, я тупой. Решение простое — проблема в матрице проекции.
В моем коде, когда я устанавливаю ProjectionMatrix
, я устанавливаю его соотношение сторон равным ScreenWidth / ScreenHeight
. Однако, поскольку оба эти параметра являются целыми числами, результат также является целым. Таким образом, вместо правильного соотношения сторон 16: 9, которое является нецелым результатом ScreenWidth / ScreenHeight
, оно преобразуется в целое число, и мое соотношение сторон в итоге получается как ScreenWidth / ScreenHeight = 1920 / 1080 = (int)1.7778 = 1
.
Очевидно, что решение простое. Примените приведение с плавающей точкой к делителю, и в итоге вы получите правильный ответ. ScreenWidth / (float)ScreenHeight = 1920 / 1080.0f = 1.7778f
.
Я надеюсь, что мой день борьбы смог помочь кому-нибудь еще с этой проблемой. Кроме того, если у вас возникли какие-либо другие проблемы с созданием 3D-камеры в XNA, я бы посоветовал вам ознакомиться с этим примером кода. Он предназначен для телефона (по какой-то причине?) и поставляется в виде библиотеки DLL, что означает, что вы не сможете его запустить (вам придется добавлять исполняемый проект, ссылаться на библиотеку DLL и перестраивать исполняемый проект каждый раз, когда вы вносите правки в библиотеку DLL), но, тем не менее, он служит хорошей отправной точкой.