ArcBall二维控制三维旋转

由于目前大多的显示器是二维的,要控制三维物体的旋转就显得不那么直接了。ArcBall是一种将二维鼠标位置的变化映射到三维物体旋转的方法,让用户通过很直观的方法控制物体旋转。

网上相关方法还是不少的,包括:

http://rainwarrior.thenoos.net/dragon/arcball.html

http://nehe.gamedev.net/tutorial/arcball_rotation/19003/

当然,Nehe的例子还是一如既往地很难看懂,总觉得搞竞赛啊算法很好的人代码可读性太差了,可能是追求敲代码的效率吧,苦了读者了。

我觉得说得最清楚的是这个http://en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Tutorial_Arcball

下面从头说一下ArcBall的思想。

一言以蔽之,就是把屏幕看成一个球,拖动鼠标就是在转动这个球。

对照http://en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Tutorial_Arcball的四个步骤:

1. 首先把按下鼠标和拖动鼠标的坐标记为Q1,Q2,x和y分别按屏幕大小缩放到[-1, 1]。如:Q1(100, 500), Q2(800, 600),屏幕大小1000x800。则缩放后得到的P1(-0.8, -0.25), P2(0.6, -0.5)。之所以做这个映射完全是为了方便以后的计算,就是Nehe说的Happy Coinsidence~

C++语言: 高亮代码由发芽网提供 // map (x, y) to [-1.0, 1.0]
vec . x = 2.0 * x / width - 1.0;
// y is set to be opposite since the coordinates of screen and
// opengl are different
vec . y = 1.0 - 2.0 * y / height;

2. 把二维坐标转成三维的,这部是最关键的。现在我们可以把屏幕看成一个xyz都是[-1, 1]的球体了,球心在(0, 0, 0)处。

比如A和B是两个鼠标映射后的点,从前视图看,A在球“上”(这里正确的理解是球壳上,而不是球体内部);B在球体外部。之所以说A在球壳上,是我们人为假设的,就是为了要对应到球体的转动。既然A在球壳上,我们就根据x,y值求的对应的z值(x、y、z的平方和是1,因为在球壳上);对B而言,我们把它“就近迁移”到球壳上,那么球壳上离B最近的点是什么呢?从正视图看应该是这样的:

所以我们认为C点的z坐标是0。

所以三维坐标的计算方法:

C++语言: 高亮代码由发芽网提供 double square = vec . x * vec . x + vec . y * vec . y;
if ( square <= 1.0) {
    // if (x, y) is within the circle of radius 1
    // calculate z so that the modulus of vector is 1
    vec . z = qSqrt( 1.0 - square);
} else {
    // if is out of the circle, do nomarlization
    // this vector is the nearest position on the circle
    // so that z is 0
    double length = qSqrt( square);
    vec . x /= length;
    vec . y /= length;
    vec . z = 0.0;
}

3. 接下来求旋转角。我们知道向量A点乘向量B=|A||B|cos(alpha)其中alpha是向量夹角。根据前两步,我们能得到鼠标按下的位置A和拖动时当前位置在球上的坐标B,现在我们想求出向量OA和OB的夹角。那么Happy Coinsidence就来了,因为球的半径是1,所以|OA|=|OB|=1。那么alpha=arccos(A和B的点积)。

C++语言: 高亮代码由发芽网提供 double ArcBall :: getRotateAngle( Vector3d vec1 , Vector3d vec2)
{
    return qAcos( vec1 . dotProduct( vec2));
}

4. 我们知道glRotatex需要三个参数:一个旋转角和一个旋转轴对应的三个坐标。所以接下去我们就要求旋转轴。既然刚刚点积发挥过作用了,这次我们就要让叉乘出出风头了。向量A和B叉乘的结果是它们所在平面的法向量,也就意味着就是我们要求的旋转轴了。

有了旋转角和旋转轴,是不是glRotatex一下就解决了?

但是由于我们只计算了鼠标按下的位置和当前鼠标位置的旋转效果,所以上一次旋转的效果在第二次按下鼠标时就消失了。记录下每次的旋转角和旋转轴显然不是一个好办法,因为旋转次数多了以后每帧都要调用非常多的glRotatex显然不合适。所以我们记录下每次旋转的旋转矩阵,然后利用矩阵乘法达到累积旋转的效果。

已知旋转角和旋转轴求旋转矩阵的方法是:http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle

网上也有很多别的地方有这个公式,但是实际的效果却是翻转的,我百思不得其解,今天试了一下把这个矩阵转置一下,竟然对了,但是不知道是什么原因,是不是右手系的关系。

到这里,我们就解决用ArcBall二维控制三维旋转了。

下面来说一说几个记录旋转量不同的方法:

1.旋转角和旋转轴:绕某个轴旋转某个角度

旋转矩阵:

2.欧拉角:分别绕三轴旋转的角度,注意是绕轴三次旋转,而不是一次。就好像在说,先绕y轴转30度,再绕x轴旋转20度,再绕z轴旋转50度。用glRotatex的话,需要用三次。旋转的顺序也是有关的,而且万一选择不好,会造成万向锁现象。

旋转矩阵:

3. 至于四元数,我自己也没搞清楚,就不瞎掰了……

ArcBall二维控制三维旋转相关推荐

  1. AutoCAD 利用二维线段通过旋转得到三维图

    步骤: 1.画轮廓线.Line直线/Circle圆/@相对坐标/Ucs确定原点 2.45°直线画法:界面左下角图标--开启极轴追踪--右键--增量角选45°.再画直线时,当直线移动到45度时会显示射线 ...

  2. 在Matlab下编程实现二维与三维的航迹跟踪控制、路径跟踪控制和轨迹跟踪控制,实现编队集群控制与避障控制

    在Matlab下编程实现二维与三维的航迹跟踪控制.路径跟踪控制和轨迹跟踪控制,实现编队集群控制与避障控制. 研究对象有空中无人机.地面机器人.水面无人艇.水下机器人以及多智能体等. ID:321006 ...

  3. 【基础建模】二维转三维学习笔记

    不能再跟前两篇一样,每看一个教程,写一篇笔记,还做三四遍,过于耗费时间,本末倒置.时间有限,应该把更多的时间用于实践.从现在起,只做重要的解说,细枝末节省略,可直接参考教程. 教程地址:我要自学网3d ...

  4. Android 高级UI解密 (三) :Canvas裁剪 与 二维、三维Camera几何变换(图层Layer原理)

    Android的绘图机制是核心内容之一,无论是什么样的功能最终都是以图像的形式呈现给用户.因此掌握Android的绘图技巧,有助于Android理解层次的提高,在面对产品经理提出的idea时也更有底气 ...

  5. python将二维数组逆时针旋转45°

    #二维数组逆时针旋转45° def rotate(array):if array==None:return 0length=len(array)#右上角输出i=length-1while i>0 ...

  6. 11.图形变换——二维变换+三维变换

    ♥,.*,.♥,.*,.♥,.*,.♥,.*♥,.*,.♥,.*,.♥,.*,.♥,.*,.♥,.*,.♥,.*,.♥,.*,.♥♥,.*,.♥,.*,.♥,.*,.♥,.*♥,.*,.♥,.*,.♥ ...

  7. Qt中打开二维、三维的工程图

    用了整整一个周的时间才搞完如何在Qt中打开二维.三维的工程图,并实现基本的移动.旋转.放大缩小.动画等功能.在这个过程中网上找了很多资料,主要用到Qt 中的QAxWidget类(ActiveX),下面 ...

  8. 计算机图形学二维图形基本变换实验原理,【实验课件】二维及三维图形基本变换的实现...

    实验二 二维及三维图形基本变换的实现 一.实验学时 4学时 二.实验类型 设计型实验 三.实验目的和要求 1. 掌握二维图形变换的原理,对一条直线实现二维基本变换(平移.错切.比例.旋转). 2. 掌 ...

  9. 手眼标定详述(坐标系介绍,二维、三维的手眼标定方法@九点法、AX=XB)

    手眼标定 1. 写在前面 2. 手眼标定基本分类 2.1 手眼标定坐标系 2.2 眼在手外(EYE TO HEAD) 2.3 眼在手上(EYE IN HEAD) 3. 九点法(二维)- 算法实现流程 ...

最新文章

  1. 美国字节程序员吐槽:国内同事太卷了!工资买不起房,卷的意义是什么?
  2. android 自定义键盘_Android自定义输入车牌号键盘、车牌简称,数字 ,字母键盘...
  3. 亚马逊与微软赢得一亿美元云计算合约,IBM黯然神伤
  4. JavaScript进阶2-学习笔记
  5. [js高手之路]从原型链开始图解继承到组合继承的产生
  6. linux系统引导分区,揭秘Linux(二)——操作系统引导与硬盘分区
  7. AC日记——字符串P型编码 openjudge 1.7 31
  8. 为什么公司要努力发展数字化战略
  9. Python3网络爬虫开发实战分析Ajax爬取今日头条街拍美图
  10. 第二次课动手动脑的问题以及课后实验性的问题
  11. 【C语言】猜拳小游戏代码实现
  12. 微信浏览器ISO系统底部导航栏
  13. 创新创业技术路线怎么写_2016如何撰写创新创业项目申请书.ppt
  14. Docker 搭建 LNMP镜像 + Wordpress
  15. 报表工具的 SQL 植入sql注入风险及规避方法
  16. python判断手机号运营商_基于python的-使用正则表达式验证手机号并匹配运营商和所述地域...
  17. 管培生走下神坛,“高管捷径”破灭
  18. HBase-14.1-JMX监控实战-hadoop
  19. Gitee上传代码保姆级教程(亲测有效)
  20. 让代码看起来更舒服,选择适合的字体。 (转)--我推荐的是:Bitstream Vera Sans Mono

热门文章

  1. atm系统的用例模型_UML建模语言7种图(以银行ATM系统为例)
  2. 设计模式 笔记4 | 简单工厂模式 在源码中的应用 | Calendar 日历 | 源码浅析 | 使用总结 | 建造者模式
  3. java如何实现e的次方_Java中怎样怎样算出求函数的幂次方?
  4. 张三同学没答好「进程间通信」,被面试官挂了....
  5. iOS OC使用FDK-AAC编译
  6. 可以悬浮在屏幕的搜题软件_悬浮窗搜题app下载_悬浮窗搜题软件官方版下载 v1.0-天空游戏网...
  7. 算了,批量下载吧,我的ECMWF数据
  8. ECMWF时间序列处理
  9. 电商 静态页面(详细讲解)
  10. 【宽搜】【并查集】Vijos P1015 十字绣