【转载】屏幕坐标向3维坐标的转化-DXUT的CD3DArcBall类
原文:http://blog.csdn.net/bluekitty/article/details/6070828
3D应用程序中,我们可以通过鼠标进行空间中物体的旋转和视角的变换等,而鼠标的移动是2D的(只有x,y坐标的变化),鼠标的这个2D移动是如何反映到3D空间中的旋转呢?这就要进行2D坐标和3D坐标的转换。
DXUT的CD3DArcBall类有一个成员函数ScreenToVector负责这个转换,下面是这个函数的实现:
- D3DXVECTOR3 CD3DArcBall::ScreenToVector( float fScreenPtX, float fScreenPtY )
- {
- // Scale to screen
- FLOAT x = -( fScreenPtX - m_Offset.x - m_nWidth / ) / ( m_fRadius * m_nWidth / );
- FLOAT y = ( fScreenPtY - m_Offset.y - m_nHeight / ) / ( m_fRadius * m_nHeight / );
- FLOAT z = 0.0f;
- FLOAT mag = x * x + y * y;
- if( mag > 1.0f )
- {
- FLOAT scale = 1.0f / sqrtf( mag );
- x *= scale;
- y *= scale;
- }
- else
- z = sqrtf( 1.0f - mag );
- // Return vector
- return D3DXVECTOR3( x, y, z );
- }
变量说明:
m_Offset:POINT类型,D3D视口起始的偏移量,一般D3D视口都是独占并铺满一个窗口,所以一般为(0,0)。
m_fRadius:球的半径,这个半径一般为1。
m_nWidth,m_nHeight:视口的宽高,没有偏移的话,就是窗口的ClientWidth和ClientHeight。
这里简单的介绍一下ArcBall,想象有一个位于空间原点的球体,这个球的半径是1,它被x轴所在的纵向平面一切2半,一半位于z轴的正半轴,另一半在负半轴,在z正半轴的那个半球就是ArcBall(注意D3D是左手坐标系)。之所以定义半径为1,就是使所有位于这个球面上的向量的模都是1,是归一化的向量,便于后续计算,所以这个球也可以叫做归一化的球(实际可能没这个叫法,但就是这个意思)。
回到ScreenToVector这个函数,它实际是把屏幕平面坐标fScreenPtX 和fScreenPtY投影到这个球上以完成2D到3D的转换(逆投影),它的2个参数是float fScreenPtX, float fScreenPtY,很好理解,就是转化为float的屏幕坐标。前两行代码:
FLOAT x = -( fScreenPtX - m_Offset.x - m_nWidth / 2 ) / ( m_fRadius * m_nWidth / 2 );
FLOAT y = ( fScreenPtY - m_Offset.y - m_nHeight / 2 ) / ( m_fRadius * m_nHeight / 2 );
对于一般情况(没有偏移,球是归一化的球),这两行代码可以简化成这样,
FLOAT x = -( fScreenPtX- m_nWidth / 2 ) / ( m_nWidth / 2 );
FLOAT y = ( fScreenPtY- m_nHeight / 2 ) / ( m_nHeight / 2 );
这两行代码乍一看不是很好理解,可以看出x和y的范围是-1到1,这有什么意义?记得刚才说的那个球被x轴所在的纵向平面一切为2么,实际上就是把那个切面上的圆的一个内接矩形当做你的屏幕了,如下图(图画的比较烂,稍微有点走样)
红色代表切面的那个圆,蓝色表示你的屏幕,屏幕的中心(实际是窗口的中心,这里假定是全屏显示的)和空间原点是重合的,那两行代码所求出的x,y值就是蓝色区域(也可以说是红色圆区域内,为什么后面有说明)内的点的坐标。当屏幕坐标fScreenPtX和fScreenPtY增大的时候,投影到空间坐标中那个蓝色矩形的点的坐标是如何变化的呢?应该的情况是x随fScreenPtX增大而增大,GDI坐标的y轴方向和空间坐标的y轴方向是相反的,所以y随fScreenPtY的增大而减小,但求的值正好相反,x是减小的(第一行代码将结果加了负号),而y是增大的,最终的结果接就是,当屏幕上的点向右下移动的时候,转换的空间坐标的点实际是在向左上移动。这和观察变换一摸一样(观察变换是世界变换的逆向变换),所以这个球用来模拟摄像机的位置非常合适。
继续往下看,这段代码
- FLOAT z = 0.0f;
- FLOAT mag = x * x + y * y;
- if( mag > 1.0f )
- {
- FLOAT scale = 1.0f / sqrtf( mag );
- x *= scale;
- y *= scale;
- }
- else
- z = sqrtf( 1.0f - mag );
很明显是在求z值,但首先必须对x和y进行修正,必须进行修正的原因就是向量模的定义:向量的模代表了向量的长度,它的值是根号(x*x+y*y+z*z),即|v|^2=x^2+y^2+z^2,这个公式很容易用勾股定律推出来。因为我们求的向量终点都在那个球面上,所以v的模在这里就是m_fRadius也就是1,即x*x+y*y+z*z=1,很明显x*x+y*y不能大于1,代码中用变量mag存贮x*x+y*y,当mag>1的时候就必须修正xy的值使它们的平方和等于1,这时z=0,当mag<1的时候,z = sqrtf( 1.0f - mag )。最后函数返回一个对应平面坐标的向量。
现在回过头来整个再看一遍代码就可以用另外一种方式来解释这个变换,可以通过将2D的点逆投影到3D球体的表面来进行变换,窗口是2D矩形,它被放入空间后变形为一个圆形,窗口上的每一个点都被转化成这个圆范围内的点,这里是有变形的,例如,根据上面的计算,窗口左上角对应的空间坐标是(0.707,-0.707,0)(注意是空间坐标的反方向,值是根号2除以2),那我要问你窗口左边中点对应空间坐标的x坐标是什么?用GDI的方式考虑,因为x方向上没有变化,只是点垂直向下移动了,所以横坐标没有变化,但对应空间坐标的x是0.707么?试一试就知道了。
【转载】屏幕坐标向3维坐标的转化-DXUT的CD3DArcBall类的更多相关文章
- OpenGL屏幕二维坐标转化成三维模型坐标
我们把OpenGL里模型的三维坐标往二维坐标的转化称为投影,则屏幕上的二维坐标往三维坐标转化则可以称为反投影,下面我们来介绍一下反投影的方法. 主要是gluUnProject函数的使用,下面是代码: ...
- Win窗口坐标二维坐标与OpenGl的世界坐标系的之间的相互转换
Win窗口坐标二维坐标与OpenGl的世界坐标系的转换 几何处理管线擅长于使用视图和投影矩阵以及用于裁剪的视口把顶点的世界坐标变换为窗口坐标. 但是,在有些情况下,需要逆转这个过程.一种常见的情形是: ...
- 二维坐标的平移,旋转,缩放及matlab实现
本文结合matlab 软件解释二维坐标系下的平移,旋转,缩放 首先确定点在二维坐标系下的表达方法,使用一个1*3矩阵: Pt = [x,y,1] 其中x,y 分别为点的X,Y坐标,1为对二维坐标的三维 ...
- UVALive 5102 Fermat Point in Quadrangle 极角排序+找距离二维坐标4个点近期的点
题目链接:点击打开链接 题意: 给定二维坐标上的4个点 问: 找一个点使得这个点距离4个点的距离和最小 输出距离和. 思路: 若4个点不是凸4边形.则一定是端点最优. 否则就是2条对角线的交点最优,能 ...
- 白鹭引擎 - 本地坐标和舞台坐标的转化 ( globalToLocal, localToGlobal )
class Main extends egret.DisplayObjectContainer { /** * Main 类构造器, 初始化的时候自动执行, ( 子类的构造函数必须调用父类的构造函数 ...
- 【转载】ArcBall二维控制三维旋转
原文:http://oviliazhang.diandian.com/post/2012-05-19/40027878859 由于目前大多的显示器是二维的,要控制三维物体的旋转就显得不那么直接了.Ar ...
- OpenGL 获取当前屏幕坐标对应的三维坐标
转自原文 OpenGL 获取当前屏幕坐标对应的三维坐标,使用很简单glu库中的一个函数 #include <GL/glut.h> #include <stdlib.h> #in ...
- 用C#控制台编写 推箱子之类的 坐标移动----之二维坐标
//首先用枚举 列出方向 上,下,左,右(枚举的最后一位数后不用符号 否则会报错) public enum dro { up = 1, down = ...
- 屏幕坐标点转UGUI坐标【包含屏幕适配】
using UnityEngine; public class ScreenToUI : MonoBehaviour { public const float UI_Width = 1366f; pu ...
随机推荐
- 一次存储链路抖动因I/O timeout不同在AIX和HPUX上的不同表现(转)
去年一个故障案例经过时间的沉淀问题没在发生今天有时间简单的总结一下,当时正时午睡时分,突然告警4库8个实例同时不可用,这么大面积的故障多数是有共性的关连,当时查看数据库DB ALERT日志都是I/O错 ...
- Tomcat 安全设置 及 内存修改
1.删除%tomcatRoot%/webapps目录下的examples.docs文件夹 2.修改%tomcatRoot%/conf/tomcat-users.xml <?xml version ...
- [控件] AngleGradientView
AngleGradientView 效果 说明 1. 用源码产生带环形渐变色的view 2. 可以配合maskView一起使用 (上图中的右下角图片的效果) 源码 https://github.com ...
- [UI] Elastic Stack & scrollReveal.js
Elastic Stack & scrollReveal.js Elastic Stack http://freebiesbug.com/code-stuff/elastistack-js-d ...
- Man's Best Friend: The Science Behind the Dog and Human Relationship
http://info.thinkfun.com/stem-education/mans-best-friend-the-science-behind-the-dog-and-human-relati ...
- controller断点进入失败:包路径问题
controller 接受前端参数的方法(前端要有传值给controller的方法,后台要有接收值得方法) 1.@RequestParam 接收表单参数 2.@RequestBody 接收json字符 ...
- 在C中调用Matlab (转)
http://blog.163.com/rongting_chen/blog/static/164906844201252354518462/ http://www.ilovematlab.cn/th ...
- 2018 徐州赛区网赛 G. Trace
题目链接在这里 题意是:按时间先后有许多左下角固定为(0,0),右上角为(xi,yi)的矩形浪潮,每次浪潮会留下痕迹,但是后来的浪潮又会冲刷掉自己区域的老痕,留下新痕迹,问最后留下的痕迹长度为多少? ...
- 占位符 %s
a = input("Name:")b = input("Age:")c = input("Job:")d = input("ho ...
- jquery validation验证身份证号、护照、电话号码、email
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...