zoukankan      html  css  js  c++  java
  • ScreenToVector详解

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

    代码

    函数的输入是屏幕坐标,输出是一个三维向量-这个向量位于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的值都取反,代码变成下面这样

    代码
     1 
     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 ==

    作者:zdd
    出处:http://www.cnblogs.com/graphics/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
  • 相关阅读:
    【JAVA SE基础篇】49.文件字符流和字节数组流
    【JAVA SE基础篇】48.IO流四大抽象类介绍和字节流
    【JAVA SE基础篇】47.file类的方法
    【JAVA SE基础篇】46.IO流的介绍
    【HTML篇】4.HTML的内嵌标签、框架标签、表单
    【HTML篇】3.HTML的图片标签、超链接标签、表格标签
    【HTML篇】2.HTML的head标签和body标签
    【HTML篇】1.HTML的介绍、三大基石、标准文档结构
    【JAVA SE基础篇】45.迭代器、Collections工具类以及使用容器存储表格
    【JAVA SE基础篇】44.手工实现简易HashMap和HashSet
  • 原文地址:https://www.cnblogs.com/graphics/p/1612832.html
Copyright © 2011-2022 走看看