透视投影矩阵
投影就是将一个三维空间中某点(或线、面等)根据一定的规则转换到二维空间中,投影根据其投影方式又分为透视投影和正射投影,如下图即为透视投影的示意图:
透视投影有四个重要的参数,即视角(φ)、宽高比(Width/Height)、近平面(ZNearPlane)和远平面(ZFarPlane),所以在摄像机中能看到的区域就是视角范围内、近平面到远平面之间的范围。对于世界空间中任意一点或向量P,要将其投影到屏幕上显示,首先要将向量(O表示世界空间的坐标原点)转换到摄像机空间,即向量(C表示摄像机空间的坐标原点),其转换公式如下:
在此,摄像机矩阵CameraMatrix即摄像机视图矩阵。在得到摄像机空间中P点的向量后,然后根据摄像机投影相关参数将该点P投影到屏幕上,如下图所示:
由上图可知:
摄像机坐标原点至屏幕的计算距离为:
则P点在单位平面上的投影位置为:
最后将单位平面上的投影位置转换到屏幕平面上,如下图所示:
所以在屏幕平面上的投影坐标为:
如下代码绘制三个空间向量到屏幕上的投影:
Vector4 calV1 = new Vector4(0f,0f,0f,1f);
calV1 =Vector4.Transform(calV1, viewMatrixComp);
int x1 = (int)(((float)calV1.X*this.Height/(float)(2 * calV1.Z * Math.Tan((float)Math.PI / 4))) + this.Width / 2);
int y1 = (int)(-calV1.Y * (float)this.Height / (2 * calV1.Z * Math.Tan((float)Math.PI / 4)) + this.Height / 2);
myFont.DrawText(sprite, "1", x1, y1, Color.Yellow);
Vector4 calV2 = new Vector4(5f, 10f, 0, 1);
calV2 = Vector4.Transform(calV2, viewMatrixComp);
int x2 = (int)(((float)calV2.X * this.Height / (float)(2 * calV2.Z * Math.Tan((float)Math.PI / 4))) + this.Width / 2);
int y2 = -(int)(calV2.Y * (float)this.Height / (2 * calV2.Z * Math.Tan((float)Math.PI / 4))) + this.Height / 2;
myFont.DrawText(sprite, "2", x2, y2, Color.Yellow);
Vector4 calV3 = new Vector4(10f, 0f, 0, 1);
calV3 = Vector4.Transform(calV3, viewMatrixComp);
int x3 = (int)(((float)calV3.X * this.Height / (float)(2 * calV3.Z * Math.Tan((float)Math.PI / 4))) + this.Width / 2);
int y3 = (int)(-calV3.Y * (float)this.Height / (2 * calV3.Z * Math.Tan((float)Math.PI / 4)) + this.Height / 2);
myFont.DrawText(sprite, "3", x3, y3, Color.Yellow);
其结果如下图所示:
在DirectX中提供了创建投影矩阵的方法,如Matrix.PerspectiveFovLH()、Matrix.PerspectiveFovRH()、Matrix.PerspectiveLH()、Matrix.PerspectiveRH()、Matrix.PerspectiveOffCenterLH()和Matrix.PerspectiveOffCenterRH()。下面主要介绍Matrix.PerspectiveFovLH()方法,其定义如下:
public staticMatrix PerspectiveFovLH(float fieldOfViewY,float aspectRatio,float znearPlane,float zfarPlane);
其中参数fieldOfViewY表示垂直Y轴平面(即XZ平面)上的视角,参数aspectRatio表示屏幕宽高比,参数znearPlane表示摄像机可见的最小距离,参数zfarPlane表示摄像机可见的最远距离。如果用h表示视图屏幕高度,则h=cot(fieldofViewY/2)(此处计算的为相对于真实屏幕尺寸的比例);视图屏幕宽度用w表示,则w=h×aspectRatio,则生成的投影矩阵各参数为:
如下代码生成的一个投影矩阵:
Matrix projMatrix = Matrix.PerspectiveFovLH((float)Math.PI / 2, this.Width / this.Height, 1.0f, 1000.0f);
device.Transform.Projection = projMatrix;
所以对于一个世界坐标系中的向量VectorOP,需要先经过摄像机空间坐标转换,然后经过投影矩阵变换,最后乘以屏幕矩阵才得到其在屏幕上的坐标位置,如下公式(在此向量都为四维向量、矩阵为四阶矩阵):
VectorOPP=VectorOP*viewMatrix*projectionMatrix*ScaleMatrix*ScreenMatrix
其中ScreenMatrix矩阵为屏幕变换矩阵,其各元素值为:
ScaleMatrix矩阵为缩放矩阵,该矩阵中缩放因子都一样,其值为向量VectorOP’=VectorOP*viewMatrix*projectionMatrix中最后一个值,如:
如下通过上面公式计算投影至平面位置的代码:
Vector4 VectorOP = new Vector4(5f, 10f, 0f, 1f);
VectorOP = Vector4.Transform(VectorOP, viewMatrixComp);
VectorOP = Vector4.Transform(VectorOP, projMatrix);
Matrix ScreenMatrix = Matrix.Zero;
ScreenMatrix.M11 = ScreenMatrix.M41= (float)this.Width / 2; ScreenMatrix.M22 = -(float)this.Height / 2;
ScreenMatrix.M42 = (float)this.Height / 2;
VectorOP = Vector4.Multiply(VectorOP, 1.0f / VectorOP.W);
VectorOP = Vector4.Transform(VectorOP, ScreenMatrix);
myFont.DrawText(sprite, "+", (int)VectorOP.X, (int)VectorOP.Y, Color.Yellow);
运行程序,由于设置显示向量与前面定义第二个向量的值一样,所以在此显示的位置与前面显示位置相同,如下图所示:
现在根据前面三个向量位置来绘制一个三角形来和前面自定义的算法来进行比较,如下代码(该段代码也位于Render()函数中):
Vector3 cameraPosition = new Vector3(0, 0, -30);
Vector3 cameraTarget = new Vector3(0f, 0.0f, 0.0f);
Vector3 cameraUpVector = new Vector3(0, 1, 0);
Matrix viewMatrixComp = Matrix.LookAtLH(cameraPosition, cameraTarget, cameraUpVector);
device.Transform.View = viewMatrixComp;
Matrix projMatrix = Matrix.PerspectiveFovLH((float)Math.PI / 2, (float)this.Width / this.Height, 1.0f, 1000.0f);
device.Transform.Projection = projMatrix;
device.RenderState.CullMode = Cull.None;
device.RenderState.Lighting = false;
device.RenderState.FillMode = FillMode.WireFrame;
CustomVertex.PositionColored[] vertices = new CustomVertex.PositionColored[3];//定义顶点
vertices[0].Position = new Vector3(0f, 0f, 0f);
vertices[0].Color = Color.Red.ToArgb();
vertices[1].Position = new Vector3(5f, 10f, 0f);
vertices[1].Color = Color.Green.ToArgb();
vertices[2].Position = new Vector3(10f, 0f, 0f);
vertices[2].Color = Color.Yellow.ToArgb();
device.VertexFormat = CustomVertex.PositionColored.Format;
device.DrawUserPrimitives(PrimitiveType.TriangleList, 1, vertices);
运行程序,其结果如下图所示:
右上图可见通过自定义算法与DirectX中提供的投影算法得到的结果有差距,具体就是Y方向位置有差距,这主要是由Windows窗体的标题栏及边框宽度造成的,可以将前面所有的代码this.Width更改为this.ClientSize.Width、将this.Height更改为this.ClientSize.Height即可解决。当然也可以在Main()函数中添加如下代码使得窗体没有框架:
MatrixExample.FormBorderStyle = FormBorderStyle.None;//设置窗体无框架
运行程序,其结果如下图所示:
这样得到的结果就会没有差距,当然也可以在程序中计算投影到屏幕上Y方向时减去标题栏的宽度来实现。
分享到:
相关推荐
基于Visual C#的DirectX开发实例,教程,程序等大全
料等,主要介绍如何采用Visual C# 语言对DirectX进行三维图形开发。本文 共分十六章,以“由易到难、由简单到复杂”为主线,以介绍DirectX 开发方 法为主,并穿插介绍相关的计算机三维图形知识等。
本文结合计算机三维图形学和DirectX的开发帮助,并参考国内外关于DirectX开发的书籍和网站资料等,主要介绍如何采用Visual C# 语言对DirectX进行三维图形开发。本文共分十六章,以“由易到难、由简单到复杂”为主线...
Visual C#项目开发实例 自学手册,把书的源代码上传赚积分
Visual C# .NET 编程经典——从VB6到Visual C# .NET 快速进阶
E:\Visual C#项目开发实例\mr\文档管理系统.rar
《Visual C++项目开发指南——定制自己的Photoshop》 PDG格式电子书完整版 本书以实现“我的Photoshop”项目的开发过程贯穿始终,通过大量实例,深入浅出地介绍了许多Visual C++ 6.0的编程技术及项目管理方法。所讲...
Visual C++开发GIS系统——开发实例剖析
E:\Visual C#项目开发实例\mr\人事工资管理系统.rar
完全手册Visual C# 2008开发技术详解
Visual C#项目开发实例 企业客户管理系统.rar
Visual C#项目开发实例\mr\网上购物商城.rar
本文结合计算机三维图 形学和 DirectX 的开发帮助,并参考国内外关于 DirectX 开发的书籍和网站资 料等,主要介绍如何采用 Visual C# 语言对 DirectX 进行三维图形开发。本文 共分十六章,以“由易到难、由简单到...
Visual C#项目开发实例 企业门户网站.rar
E:\Visual C#项目开发实例\mr\资产评估管理系统.rar
挺好的Visual_C#_2008——设计QQ用户登录界面Visual_C#_2008——设计QQ用户登录界面
第一部分源码——企业客户管理系统,整个项目的源代码都在这里。
Visual C#项目开发实例 自学手册 上传赚积分
《Visual C++开发GIS系统——开发实例剖析》附书源代码.zip