问题
I am trying to understand what the value range of projected vectors is. It seems at odds with what MSDN says.
The code below outputs a colored triangle. The triangle is fully visible. I would expect that after the verticies have been projected, the following would be true:
X and Y are in the range of -1 to 1. Z is in the range of 0 to 1.
I got this information from here: http://msdn.microsoft.com/en-us/library/bb195665.aspx
However, when I manually transform the verticies of the triangle using the product of view and projection matrix, they have the following values:
{X:1,8 Y:0 Z:3,991996} {X:0 Y:3 Z:3,991996} {X:-1,8 Y:0 Z:3,991996}
Why do the values fall out of the visible range, yet the trinangle is fully visible?
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using Microsoft.Xna.Framework.Input;
namespace WasMachtDieProjektion
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
Matrix _view;
Matrix _projection;
VertexPositionColor[] _verticies;
Vector3[] _vectors;
BasicEffect _effect;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void LoadContent()
{
_vectors = new Vector3[] {
new Vector3(-3,0,0),
new Vector3(0,3,0),
new Vector3(3,0,0)
};
_verticies = new VertexPositionColor[] {
new VertexPositionColor(_vectors[0], Color.AliceBlue),
new VertexPositionColor(_vectors[1], Color.Yellow),
new VertexPositionColor(_vectors[2], Color.Red)
};
_effect = new BasicEffect(graphics.GraphicsDevice);
_effect.VertexColorEnabled = true;
GraphicsDevice.RasterizerState = new RasterizerState() { CullMode = CullMode.None };
}
protected override void Update(GameTime gameTime)
{
_view = Matrix.CreateLookAt(new Vector3(0, 0, -4f), new Vector3(0, 0, 0), Vector3.Up);
_projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver2, GraphicsDevice.Viewport.AspectRatio, 0.01f, 20f);
Vector3[] transformed = new Vector3[3];
Matrix trans = _view * _projection;
Vector3.Transform(_vectors, ref trans, transformed);
foreach (var v in transformed)
{
Console.WriteLine(v);
}
Console.WriteLine("---");
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
_effect.View = _view;
_effect.Projection = _projection;
foreach (var pass in _effect.CurrentTechnique.Passes)
{
pass.Apply();
GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, _verticies, 0, 1);
}
base.Draw(gameTime);
}
}
}
回答1:
Do not forget the perspective division after projection. This means after multiplying by the projection matrix, devide the transformed vector's x
, y
and z
by their w
values. So you have to work on 4D homogenous vectors instead of 3D vectors. Your initial vectors are just (x,y,z,1)
but after the projection matrix (which is not an affine transformation) has been applied, their w
can (and should) be != 1
, so you have to devide by that, which realizes the actual perspective distortion:
(x', y', z', w') = trans * (x, y, z, 1);
(x'', y'', z'') = (x'/w', y'/w', z'/w');
Consult some introductory material on 3D transformations, projections and homogenous coordinates for some more insight.
EDIT: I just saw another error in your code. trans
has to be _projection * _view
, so that _view
gets applied first, when multiplying a vector by trans
. Except if XNA uses the mathematically incorrect convention of row_vector * matrix
. If that's the case, your order should do it.
回答2:
Not 100% sure but I think it might be because it is a projection view that no matter what the matrix is it will always represent it. If it were an orthogonal view it would then begin to not display the full triangle.
来源:https://stackoverflow.com/questions/6111721/understanding-the-projection-matrix