XNA/Monogame 3D 拉伸和纵横比
XNA/Monogame 3D Stretching and Aspect Ratios
我正在尝试使用可移动相机将一些 3D 模型正确地绘制到屏幕上,但我遇到了两个问题。
第一个问题立即可见:模型应该是立方体,但绘制成直角棱柱。就像他们squashed/compressed,但我找不到原因。
Sides of cubes
第二个问题出现在俯视 "cubes." 虽然它们应该只是旋转,但它们在旋转时似乎以一种奇怪的方式伸展。
Looking down
Still looking down, but rotated CW 90 degrees
这是我的相机代码:
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();
}
}
我知道代码很长,可能很难读完,但我非常感谢任何帮助。谢谢!
好吧,我很笨。解决方案很简单——问题出在投影矩阵上。
在我的代码中,当我设置 ProjectionMatrix
时,我将其纵横比设置为 ScreenWidth / ScreenHeight
。但是,由于这两个都是 整数 ,因此结果 也是一个整数 。因此,不是 16:9 的适当纵横比,它是 ScreenWidth / ScreenHeight
的非整数结果,它被转换为整数,我的纵横比最终为 ScreenWidth / ScreenHeight = 1920 / 1080 = (int)1.7778 = 1
.
显然,解决方案很简单。将浮点数附加到除数,您最终会得到正确的答案。 ScreenWidth / (float)ScreenHeight = 1920 / 1080.0f = 1.7778f
.
我希望我挣扎的一天能够帮助其他人解决这个问题。此外,如果您在 XNA 中创建 3D 相机时遇到任何其他问题,我建议您查看 this 代码示例。它是为 phone(出于某种原因?)而设计的,它以 DLL 的形式出现,这意味着您将无法 运行 它(您必须添加一个可执行项目,参考DLL,并在您对 DLL 进行编辑时重建可执行项目),但它仍然是一个很好的 starting/reference 点。
Glorious correctly-aspected cubes
我正在尝试使用可移动相机将一些 3D 模型正确地绘制到屏幕上,但我遇到了两个问题。 第一个问题立即可见:模型应该是立方体,但绘制成直角棱柱。就像他们squashed/compressed,但我找不到原因。
Sides of cubes
第二个问题出现在俯视 "cubes." 虽然它们应该只是旋转,但它们在旋转时似乎以一种奇怪的方式伸展。
Looking down
Still looking down, but rotated CW 90 degrees
这是我的相机代码:
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();
}
}
我知道代码很长,可能很难读完,但我非常感谢任何帮助。谢谢!
好吧,我很笨。解决方案很简单——问题出在投影矩阵上。
在我的代码中,当我设置 ProjectionMatrix
时,我将其纵横比设置为 ScreenWidth / ScreenHeight
。但是,由于这两个都是 整数 ,因此结果 也是一个整数 。因此,不是 16:9 的适当纵横比,它是 ScreenWidth / ScreenHeight
的非整数结果,它被转换为整数,我的纵横比最终为 ScreenWidth / ScreenHeight = 1920 / 1080 = (int)1.7778 = 1
.
显然,解决方案很简单。将浮点数附加到除数,您最终会得到正确的答案。 ScreenWidth / (float)ScreenHeight = 1920 / 1080.0f = 1.7778f
.
我希望我挣扎的一天能够帮助其他人解决这个问题。此外,如果您在 XNA 中创建 3D 相机时遇到任何其他问题,我建议您查看 this 代码示例。它是为 phone(出于某种原因?)而设计的,它以 DLL 的形式出现,这意味着您将无法 运行 它(您必须添加一个可执行项目,参考DLL,并在您对 DLL 进行编辑时重建可执行项目),但它仍然是一个很好的 starting/reference 点。
Glorious correctly-aspected cubes