【转载】屏幕坐标转换为三维空间坐标

原文:http://www.cnblogs.com/graphics/archive/2009/11/28/1612832.html

如果您正在学习ArcBall技术或者您对于屏幕坐标到三维坐标的转换有些模糊,那么一定不要错过本篇。ScreenToVector函数是微软DXUT框架中AcrBall类中的一个函数,它的作用是完成二维屏幕坐标到三维球坐标的转换,先看一下函数定义

代码

1 D3DXVECTOR3 CD3DArcBall::ScreenToVector( float fScreenPtX, float fScreenPtY )
 2 {
 3     // Scale to screen
 4      FLOAT x = -( fScreenPtX - m_Offset.x - m_nWidth / 2 ) / ( m_fRadius * m_nWidth / 2 );
 5     FLOAT y = ( fScreenPtY - m_Offset.y - m_nHeight / 2 ) / ( m_fRadius * m_nHeight / 2 );
 6 
 7     FLOAT z = 0.0f;
 8     FLOAT mag = x * x + y * y;
 9 
10     if( mag > 1.0f )
11     {
12         FLOAT scale = 1.0f / sqrtf( mag );
13         x *= scale;
14         y *= scale;
15     }
16     else
17         z = sqrtf( 1.0f - mag );
18 
19     // Return vector
20     return D3DXVECTOR3( x, y, z );
21 }

函数的输入是屏幕坐标,输出是一个三维向量-这个向量位于ArcBall的球面上。为什么要分析这个函数?首先是它很重要,其次它比较难以理解,它的第一行代码让很多刚刚接触它的人都搞不明白,为什么要将x的值取反呢?我苦苦思索了很久,又苦苦搜索了很久,但是始终找不到满意的答案,于是就自己硬着头皮分析。经过好几周的苦战,终于有点眉目了,下面我就把自己的一点看法分享给大家。我不敢保证我的分析是正确的,只希望能给您一点启发,如果您发现了什么错误,或者您有更简单的方法,请一定告诉我,先谢过!

为了更好地便于大家理解,首先要强调几个知识点

1. Windows屏幕/窗口坐标特点:以窗口左上角为原点,X值向右递增,Y值向下递增

2. DirectX使用的是左手系:X轴指向右,Y轴指向上,Z轴垂直屏幕指向内侧

3. Quaternion表示的旋转满足右手系-绕旋转轴逆时针旋转(对着旋转轴向原点看)

好了,上一张图,对比一下屏幕坐标与DirectX坐标的差异

下面开始分析代码,先看头两行代码,他们的作用是将屏幕坐标转换为[0-1]范围内的值,通常窗口的偏移量设置为0,而ArcBall的半径设置为1,所以这两行代码可以转化为下面的形式

代码

1 FLOAT x = -( fScreenPtX - m_nWidth / 2 ) / ( m_nWidth / 2 );
2 FLOAT y = ( fScreenPtY - m_nHeight / 2 ) / ( m_nHeight / 2 );
3

接下来的代码求z的值,如果x,y构成的二维向量的模>1,那么直接令z=0,否则的话计算z值,并使三维向量的模保持为1,这是为了方便后面求旋转角度和旋转轴的计算

为什么X的值取反?

首先:由z的计算过程知,z的值永远>=0,又因为DirectX使用左手系,所以,实际上这里是用的是屏幕内侧的半球来进行旋转的这就要求ArcBall的旋转方向与鼠的标滑动方向相反,进而要求屏幕坐标的x,y值与ArcBall的x,y值相反,因为屏幕坐标无z值一说,所以z值不用考虑。

因为屏幕坐标的x轴和DirectX坐标系的x轴方向一致,所以将x值取反,而对于y轴来说,本来就是相反的,所以不用处理了。这里实际上包含了下面这个过程

注:这个图中的球是屏幕内部的半球,由于画的不好,效果不是很明显。

由上面的图可知,将x值取反后,实际上是将屏幕的左上区域映射到ArcBall的右下半球,右上区域映射到左下半球,左下区域映射到右上半球,右下区域映射到左上半球,哈哈,绕迷糊了吧,不过对着图还是比较好理解的,映射的方向由同种颜色的矩形到圆形表示。

那么我们看看旋转是如何实现的,再看图

这个图表示,当用户在窗口左上区域向右拖动鼠标,起点和终点分别是P1和P2,那么将在ArcBall的右下区域产生两个向量,分别是V1和V2,而ArcBall旋转的方向则是从V1到V2,旋转轴是由V1和V2的叉积确定的。

有些时候为了追求简单,往往会将问题搞得更复杂,就像这个函数一样,可能是写这个函数的哥们为了简便将x的值取反,使我们理解起来如此困难,如果他能多写一个负号的话,那么问题就简单多了,为什么这么说呢?

因为使用屏幕内测的半球导致鼠标拖拽方向和ArcBall的旋转方向相反,如果我们使用屏幕外侧的半球不就简单了嘛!Yeah!you got it!

将x值保持不变,y,z的值都取反,代码变成下面这样

代码


 2 FLOAT x = ( fScreenPtX - m_Offset.x - m_nWidth / 2 ) / ( m_fRadius * m_nWidth / 2 );
 3 FLOAT y = -( fScreenPtY - m_Offset.y - m_nHeight / 2 ) / ( m_fRadius * m_nHeight / 2 );
 4 
 5 FLOAT z = 0.0f;
 6 FLOAT mag = x * x + y * y;
 7 
 8 if( mag > 1.0f )
 9 {
10     FLOAT scale = 1.0f / sqrtf( mag );
11     x *= scale;
12     y *= scale;
13 }
14 else
15     z = -sqrtf( 1.0f - mag );
16

这样窗口坐标的x,y轴就与DirectX坐标系的x,y轴重合了。而z值永远<=0保证了使用的是外侧的半球,这样鼠标的拖拽方向就和ArcBall的旋转方向一致了!上面的分析都是针对DirectX进行的,如果您使用的是OpenGL, 则情况恰好相反,OpenGL使用的是右手系,所以直接将y值取反就可以了。你明白了么?

Happy Coding!!!

== THE END ==

时间: 2024-10-12 12:07:43

【转载】屏幕坐标转换为三维空间坐标的相关文章

SuperMap for JavaScript 中的最佳路径分析功能转换为三维中的最佳路径分析功能

1.注意,要处理有交通网络分析服务 2.代码 var nodeArray = [],pathListIndex = 0,routeCompsIndex = 0,pathTime,style = { strokeColor: "#304DBE", strokeWidth: 3, pointerEvents: "visiblePainted", fill: false            }, styleGuidePoint = { pointRadius: 10,

列表转换为三维矩阵

先记录一下刚开始最慢最蠢的方法:(第一个函数是用单词训练的word2vec,第二个是字符训练的) '''#相同维度单词级别向量输入(注意列表向数组的转换及向量向数组的转换.向量与列表格式的区别.二维到三维的转换(https://blog.csdn.net/u013044310/article/details/80407220)def get_wordvec2(url,url_label): model = gensim.models.Word2Vec.load('./url_w2corpus_w

Cesium基础使用介绍

前言 最近折腾了一下三维地球,本文简单为大家介绍一款开源的三维地球软件--Cesium,以及如何快速上手Cesium.当然三维地球重要的肯定不是数据显示,这只是数据可视化的一小部分,重要的应该是背后的数据生成及处理等.本文先为大家介绍这简单的部分. 一. Cesium简介 Github地址:https://github.com/AnalyticalGraphicsInc/cesium.官方介绍如下: An open-source JavaScript library for world-clas

OpenGl 坐标转换 (转载)

OpenGl 坐标转换 (转载) 1. OpenGL 渲染管线 OpenGL渲染管线分为两大部分,模型观测变换(ModelView Transformation)和投影变换(Projection Transformation).做个比喻,计算机图形开发就像我们照相一样,目的就是把真实的场景在一张照相纸上表现出来.那么观测变换的过程就像是我们摆设相机的位置,选择好要照的物体,摆好物体的造型.而投影变换就像相机把真实的三维场景显示在相纸上一样.下面就分别详细的讲一下这两个过程. 1.1模型观测变换

(十)WebGIS中地理坐标与屏幕坐标间的转换原理

1.前言 地图本身是拥有坐标的,一般可以大致分为平面坐标和经纬度坐标,在这里我们统称为地理坐标,比如北京,(115.9°E ,39.6°N)和(506340,304400)均是其地理坐标,只是表示形式不同而已. 我们在上一章讲解了矢量图层中数据的来源,最后提出了一个还未解决的问题,即当我们获得了矢量数据后,如何在屏幕中将这些数据里的地理(Geometry)坐标转换为屏幕坐标,从而在屏幕端Canvas里的各个UIComponent(要素)中绘制出来? 这一章我们将对此转换做出讲解. 2.转换前提

ScreenToViewportPoint,WorldToViewportPoint,ViewportToWorldPoint的运用,实现一个简单的对三维中物体的拖拽移动效果

众所周知,我们手机或者手机屏幕上的坐标是一个二维平面的的坐标值,而且这个坐标是一像素为单位的,也就是说这个是会根据你用的设备的不同,你手机或者电脑上的坐标的长宽最大值也就不同. 之前不太了解标题上三个方法的用法,走了不少弯路,然后下来仔细研究了一下,感觉还是挺有收获的. 简单说一下,我们在Unity中屏幕坐标是以坐下为原点,向两边延伸,右上为终点,假定现在我们的分辨率是1920x1080的话,那么简单,四个角对应的值大家可能都能猜到,(假定为横屏)坐下(0,0),右下(1920,0),左上(10

游戏中的三维数学

一.点和矢量 (一)坐标系分类 笛卡尔坐标系: 圆柱坐标系: 球坐标系: 通常来说笛卡尔坐标系是我们最常用的坐标系,但我们同样要根据不同的情况来选择合适的坐标系,例如我们在做一些环绕动画的时候,采用圆柱坐标系可以更简单. 在三维笛卡尔坐标系中又分为左右手坐标系,用左右手来方便做记忆,大拇指指向X轴,食指指向Y轴,中指指向Z轴,3指垂直即可建立模型.左右坐标系的转换只需要把一个轴转换,保留另外两个轴的方向不变即可.对于三维图形程序员来说,一般以左手坐标系工作,并且Y轴向上,X轴向右,Z轴远离观察者

Cocos2d-x教程(35)-三维拾取Ray-AABB碰撞检测算法

欢迎加入Cocos2d-x 交流群:193411763 转载时请注明原文出处 :http://blog.csdn.net/u012945598/article/details/39927911 ---------------------------------------------------------------------------------------------------------------------------------------------------------

屏幕坐标和世界坐标的转换+对象池技术(3D打地鼠小游戏)

游戏中可能经常会遇到需要某个物体跟着鼠标移动,然后又需要把物体放在某个鼠标指定的位置 实现方式 Camera.main.WorldToScreenPoint Camera.main.ScreenToWorldPoint 3D打地鼠实例 我这里用到的素材都比较简陋,几乎全是用Unity做的 首先是锤子 就是两个Cylinder,在把手的位置放一个空物体用于模拟锤子的动作,命名为Hammer,把锤子作为Hammer的子物体,给Hammer添加Animation动画: 在三个关键帧位置设置Hammer