【转载】3D/2D中的D3DXMatrixPerspectiveFovLH和D3DXMatrixOrthoLH投影函数详解

原文:3D/2D中的D3DXMatrixPerspectiveFovLH和D3DXMatrixOrthoLH投影函数详解

3D中z值会影响屏幕坐标系到世界坐标系之间的转换,2D中Z值不会产生影响(而只是屏幕宽高比会产生影响,z值只对深度剔除产生影响)。所以U3D中如果用2D摄像机那么屏幕坐标和世界坐标之间的转换需要用指定的2D摄像机才行,如果用主3D摄像机那么UI转换会产生计算结果异常。

一、D3DXMatrixPerspectiveFovLH函数

作用:Builds a left-handed perspective projection matrix based on a field of view.获得指定参数的透视投影矩阵,用于3D投影变换。

使用:

D3DXMATRIX proj;
D3DXMatrixPerspectiveFovLH(
            &proj,
            D3DX_PI * 0.5f, // 90 - degree
            (float)Width / (float)Height,
            1.0f,
            1000.0f);
Device->SetTransform(D3DTS_PROJECTION, &proj); // 设置变换矩阵和状态,没有真正开始变换,提交后交给图形硬件

1.镜头平移:改变cameraPos用来计算观察位置

2.镜头拉近拉远缩放物体对象:

1).设置观察点的位置,cameraPos用来计算观察位置(没有忽略z值故可以)。

2) fovy z轴和zy上斜线之间夹角变小,那么物体也会变大(aspect不能随便变换否则就不等比例了)。

3.镜头拉高拉低:

1)cameraPos来拉高,y变就可以了。

原型:

D3DXMATRIX* D3DXMatrixPerspectiveFovLH(

_Inout_  D3DXMATRIX *pOut,

_In_     FLOAT  fovy,

_In_     FLOAT  aspect,

_In_     FLOAT  zn,

_In_     FLOAT  zf

);

pOut:透视变换矩阵,将视锥内的物体变换到(-1,-1,-1)->(1,1,1)的正方体内。

fovy:视锥体的上下夹角(6面体),z轴平分该夹角。

当fovy变小时候,因为也要在屏幕90度中用,所以高度方向被放大了,反之fovy变大,高度方向变小。

aspect:aspect = w / h,fovy = 90度,由投影矩阵的计算过程,投影yScale = cot(fovy/2); xScale = yScale /aspect.

当屏幕w:h = 100:20时,当aspect = 5:1,那么视锥内的[5,1]正方形截面世界将被变换映射到(1,1)的平面内,

xScale压缩了1/5,当透视投影映射到屏幕坐标时候[5,1],xScale方向需要根据屏幕放大5倍,这样视锥体里面的世界等比缩放到屏幕。

当aspect = 1变小;那么yScale = 1, xScale = 1,视锥体内的xy正方形截面[1,1]世界被放置到(1,1); 映射到屏幕为[5,1]xScale映射到屏幕被放大了5倍,yScale不变。

当aspect = 10变大,那么yScale = 1,xScale = 1/10,视锥体内的xy正方形截面[10,1]世界->(1,1)->[5,1],xScale被缩小了2倍,yScale不变。

zn,zf:z近裁剪面,z远裁剪面,z视锥体depth深度的改变,映射到屏幕上,也是zn深度变大了,那么屏幕上物体变小了,zn深度变小了,那么屏幕上物体将变大。

变换矩阵:

xScale     0          0               0
0        yScale       0               0
0          0       zf/(zf-zn)         1
0          0       -zn*zf/(zf-zn)     0
where:
yScale = cot(fovY/2)

xScale = yScale / aspect ratio

yScale是根据视锥夹角求得的,xScale由屏幕大小来设置等于xScale = yScale / aspect = yScale * h / w,也就是希望: xScale /yScale = h / w.即当yScale = 1时,屏幕w/h = 4/3。那么xScale的缩放比例为 3 / 4,就是是cot(fovX / 2)更小,fovX越大投影的点越多;当xScale更小,除以w = z后,那么[-1,1]的设备坐标系x范围内可以放入更多的点,当转换到屏幕坐标系时,x方向的点放大为y方向放大的4/3, 所以x方向和y方向的缩放比例为1:1,也就是等比的缩放不会导致问题,主要的缩放来自于fovY视锥夹角,和摄像机位置。

也就是说xScale = yScale / aspect ratio是英明的公式,保证了等比缩放,而和真正的缩放分离了。

z轴方向的zf/(zf-zn)是对1/z插值的常数部分, -zn*zf/(zf-zn)是对1/z插值的系数部分。

m31 = 1是最隐秘的雕虫小技。

二、D3DXMatrixOrthoLH函数

作用:生成一个左手坐标系正交投影矩阵,用于视图坐标到投影坐标系的2D转换。
使用:
D3DXMatrix mOrtho
D3DXMatrixOrthoLH(&mOrtho, WINDOW_WIDTH, WINDOW_HEIGHT, 0.1f, 1000.0f);

g_pd3dDevice->SetTransform(D3DTS_PROJECTION,&mOrtho); // 设置变换矩阵和状态,没有真正开始变换,提交后交给图形硬件实现变换

1.镜头平移:改变cameraPos用来计算观察位置

2.镜头拉近放大物体对象:

1).Z深度值被丢弃了,可以通过放大屏幕投影来变大,那么需要放大正交投影的值,因为正交投影后2/w、2/h,所以可以通过缩小正交投影传入的w、h来实现放大;放大传入的w、h可以实现模拟的远离。

3.镜头拉高拉低:

1)cameraPos,y值变大来拉高;通过放大传入的w、h可以实现模拟的远离缩小。

原型:

D3DXMATRIX* D3DXMatrixOrthoLH(
  _Inout_  D3DXMATRIX *pOut, // 输出的变幻矩阵
  _In_     FLOAT w, // 屏幕的宽度
  _In_     FLOAT h, // 屏幕的高度
  _In_     FLOAT zn, // z深度缓存最小值
  _In_     FLOAT zf // z深度缓存最大值
);

得到的变换矩阵为:
2/w  0    0           0
0    2/h  0           0
0    0    1/(zf-zn)   0
0    0    zn/(zn-zf)  1

2D正交变换将摄像机坐标空间里面的点变换到设备规范化坐标空间中

2/w,2/h对摄像机空间内的x,y进行正交投影,进行了一定的缩放,当w,h越大那么缩小越大,设备规范化坐标系内[-1,1]容纳的像素就越多,变换到屏幕坐标系中就感觉远离缩小了。z轴空间上是对z进行了线性插值,使得z值在[0,1]空间内,zn时候为0,zf时候为1(深度缓存用于遮挡剔除深度测试,和stencil测试)。

参考:

http://msdn.microsoft.com/en-us/library/windows/desktop/bb205350%28v=vs.85%29.aspx

http://msdn.microsoft.com/en-us/library/windows/desktop/bb204940%28v=vs.85%29.aspx

时间: 2025-01-02 14:54:32

【转载】3D/2D中的D3DXMatrixPerspectiveFovLH和D3DXMatrixOrthoLH投影函数详解的相关文章

【转载】C++中替代sprintf的std::ostringstream输出流详解

一.简单介绍 ostringstream是C++的一个字符集操作模板类,定义在sstream.h头文件中.ostringstream类通常用于执行C风格的串流的输出操作,格式化字符串,避免申请大量的缓冲区,替代sprintf. 派生关系图: 二.ostringstream的基本使用 ostringstream的构造函数形式:explicit ostringstream ( openmode which = ios_base::out ); explicit ostringstream ( con

LWIP中的接收数据相关的函数详解(1)

一.在main()函数中 1 /* check if any packet received */ 2 if (ETH_CheckFrameReceived()) 3 { 4 /* process received ethernet packet */ 5 LwIP_Pkt_Handle(); 6 } 二. 1 /** 2 * @brief Called when a frame is received 3 * @param None 4 * @retval None 5 */ 6 void L

【python】Numpy中stack(),hstack(),vstack()函数详解

转自 https://blog.csdn.net/csdn15698845876/article/details/73380803 这三个函数有些相似性,都是堆叠数组,里面最难理解的应该就是stack()函数了,我查阅了numpy的官方文档,在网上又看了几个大牛的博客,发现他们也只是把numpy文档的内容照搬,看完后还是不能理解,最后经过本人代码分析,算是理解了stack()函数增加维度的含义.以下内容我会用通俗易懂的语言解释,内容可能有点多,耐心看,如果哪里说的不对,欢迎纠正! 1. stac

delphi中的Format函数详解

首先看它的声明:[[email protected]][@21ki!] function Format(const Format: string; const Args: array of const): string; overload;[[email protected]][@21ki!] 事实上Format方法有两种形式,另外一种是三个参数的,主要区别在于它是线程安全的,[[email protected]][@21ki!]但并不多用,所以这里只对第一个介绍:[[email protect

linux中fork()函数详解[zz]

转载自:http://www.cnblogs.com/york-hust/archive/2012/11/23/2784534.html 一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事. 一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间.然后把原来的进程的所有值都复制到新的新进程中,只有

Android中内容观察者的使用---- ContentObserver类详解

  转载请注明出处:http://blog.csdn.net/qinjuning 前言: 工作中,需要开启一个线程大量的查询某个数据库值发送了变化,导致的开销很大,后来在老大的指点下,利用了 ContentObserver完美的解决了该问题,感到很兴奋,做完之后自己也对ContentObserver做下总结. ContentObserver——内容观察者,目的是观察(捕捉)特定Uri引起的数据库的变化,继而做一些相应的处理,它类似于 数据库技术中的触发器(Trigger),当ContentObs

[转载,感觉写的非常详细]DUBBO配置方式详解

[转载,感觉写的非常详细]DUBBO配置方式详解 原文链接:http://www.cnblogs.com/chanshuyi/p/5144288.html DUBBO 是一个分布式服务框架,致力于提供高性能和透明化的 RPC 远程服务调用方案,是阿里巴巴 SOA 服务化治理方案的核心框架,每天为 2,000+ 个服务提供 3,000,000,000+ 次访问量支持,并被广泛应用于阿里巴巴集团的各成员站点. Dubbo采用全spring配置方式,透明化接入应用,对应用没有任何API侵入,只需用Sp

C#函数式编程中的标准高阶函数详解

何为高阶函数 大家可能对这个名词并不熟悉,但是这个名词所表达的事物却是我们经常使用到的.只要我们的函数的参数能够接收函数,或者函数能够返回函数,当然动态生成的也包括在内.那么我们就将这类函数叫做高阶函数.但是今天我们的标题并不是高阶函数,而是标准高阶函数,既然加上了这个标准,就意味着在函数式编程中有一套标准的函数,便于我们每次调用.而今天我们将会介绍三个标准函数,分别为Map.Filter.Fold. Map 这个函数的作用就是将列表中的每项从A类型转换到B类型,并形成一个新的类型.下面我们可以

day01_linux中与Oracle有关的内核参数详解

linux中与Oracle有关的内核参数详解 在安装Oracle的时候需要调整linux的内核参数,但是各参数代表什么含义呢,下面做详细解析. Linux安装文档中给出的最小值: fs.aio-max-nr = 1048576 fs.file-max = 6815744 kernel.shmall = 2097152 kernel.shmmax = 4294967295 kernel.shmmni = 4096 kernel.sem = 250 32000 100 128 net.ipv4.ip