转自【翻译】NeHe OpenGL 教程

前言

声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改。对NeHe的OpenGL管线教程的编写,以及yarn的翻译整理表示感谢。

NeHe OpenGL第四十八课:轨迹球

轨迹球实现的鼠标旋转

使用鼠标旋转物体,很简单也有很多实现方法,这里我们教会你模拟轨迹球来实现它.

 

轨迹球控制

By Terence J. Grant (tjgrant@tatewake.com)

如果只用鼠标来控制你的模型是不是很酷?轨迹球可以帮你做到这一点,我将告诉你我的实现,你可以把它应用在你的工程里。

我的实现是基于Bretton Wade’s,它是基于Ken Shoemake’s 实现的,最初的版本,你可以从游戏编程指南这本图上找到。但我还是修正了一些错误,并优化了它。

轨迹球实现的内容就是把二维的鼠标点映射到三维的轨迹球,并基于它完成旋转变化。

为了完成这个设想,首先我们把鼠标坐标映射到[-1,1]之间,它很简单:

MousePt.X  =  ((MousePt.X / ((Width  -1) / 2)) -1);MousePt.Y  = -((MousePt.Y / ((Height -1) / 2))-1);

这只是为了数学上的简化,下面我们计算这个长度,如果它大于轨迹球的边界,我们将简单的把z轴设为0,否则我们把z轴设置为这个二维点映射到球面上对应的z值。

一旦我们有了两个点,就可以计算它的法向量了和旋转角了。

下面我们从构造函数开始,完整的讲解这个类:

ArcBall_t::ArcBall_t(GLfloat NewWidth, GLfloat NewHeight)

当点击鼠标时,记录点击的位置  

  

void    ArcBall_t::click(const Point2fT* NewPt)

  

当拖动鼠标时,记录当前鼠标的位置,并计算出旋转的量。  

  

void ArcBall_t::drag(const Point2fT* NewPt, Quat4fT* NewRot)

如果窗口大小改变,设置鼠标移动的范围 

  

void    ArcBall_t::setBounds(GLfloat NewWidth, GLfloat NewHeight)

  

下面是完成计算所要用到的数据结果,都是一些矩阵和向量 

  

Matrix4fT Transform = {  1.0f,  0.0f,  0.0f,  0.0f,          0.0f,  1.0f,  0.0f,  0.0f,          0.0f,  0.0f,

1.0f,  0.0f,          0.0f,  0.0f,  0.0f,  1.0f };

Matrix3fT LastRot = { 1.0f, 0.0f, 0.0f,

0.0f, 1.0f, 0.0f,

0.0f, 0.0f, 1.0f };

Matrix3fT ThisRot = { 1.0f, 0.0f, 0.0f,

0.0f, 1.0f, 0.0f,

0.0f, 0.0f, 1.0f };

ArcBallT ArcBall(640.0f, 480.0f);

Point2fT MousePt;

bool isClicked = false; // 是否点击鼠标

bool isRClicked = false; // 是否右击鼠标

bool isDragging = false; // 是否拖动

在上面定义的变量中,transform是我们获得的最终的变换矩阵,lastRot是上一次鼠标拖动得到的旋转矩阵,thisRot为这次鼠标拖动得到的旋转矩阵。

当我们点击鼠标时,创建一个单位旋转矩阵,当我们拖动鼠标时,这个矩阵跟踪鼠标的变化。

为了更新鼠标的移动范围,我们在函数ReshapeGL中加入下面一行:

void ReshapeGL (int width, int height){ . . . ArcBall.setBounds((GLfloat)width, (GLfloat)height);    // 更新鼠标的移动范围}

// 处理鼠标的按键操作

LRESULT CALLBACK WindowProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

{

. . .

case WM_MOUSEMOVE:

MousePt.s.X = (GLfloat)LOWORD(lParam);

MousePt.s.Y = (GLfloat)HIWORD(lParam);

isClicked = (LOWORD(wParam) & MK_LBUTTON) ? true : false;

isRClicked = (LOWORD(wParam) & MK_RBUTTON) ? true : false;

break;

case WM_LBUTTONUP: isClicked = false; break;

case WM_RBUTTONUP: isRClicked = false; break;

case WM_LBUTTONDOWN: isClicked = true; break;

case WM_RBUTTONDOWN: isRClicked = true; break;

. . .

}

为了随着输入更新我们的的状态,在Update函数中需要处理更新参数 

  

if (isRClicked)          // 如果右键按下,这重置所有的变量{ Matrix3fSetIdentity(&LastRot);

  Matrix3fSetIdentity(&ThisRot);

  Matrix4fSetRotationFromMatrix3f(&Transform, &ThisRot);

  }

if (!isDragging) // 如果没有拖动

{

if (isClicked) // 第一次按下

{

isDragging = true; // 设置拖动为变量为true

LastRot = ThisRot;

ArcBall.click(&MousePt);

}

}

else

{

if (isClicked) //如果按住拖动

{

Quat4fT ThisQuat;

ArcBall.drag(&MousePt, &ThisQuat); // 更新轨迹球的变量

Matrix3fSetRotationFromQuat4f(&ThisRot, &ThisQuat); // 计算旋转量

Matrix3fMulMatrix3f(&ThisRot, &LastRot);

Matrix4fSetRotationFromMatrix3f(&Transform, &ThisRot);

}

else // 如果放开鼠标,设置拖动为false

isDragging = false;

}

好了,完成了上面的内容。我们到了绘制的阶段。

记住在绘制前,把我们得到的矩阵乘以当前的模型变换矩阵。 

  

glPushMatrix();         // 保存当前的矩阵 

glMultMatrixf(Transform.M); // 应用我们的变换矩阵

glBegin(GL_TRIANGLES); // 绘制模型

. . .

glEnd();

glPopMatrix(); // 弹出保存的矩阵

原文及其个版本源代码下载:

http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=48

 
 

NeHe OpenGL教程 第四十八课:轨迹球的更多相关文章

  1. NeHe OpenGL教程 第三十八课:资源文件

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  2. NeHe OpenGL教程 第四十五课:顶点缓存

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  3. NeHe OpenGL教程 第四十六课:全屏反走样

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  4. NeHe OpenGL教程 第四十二课:多重视口

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  5. NeHe OpenGL教程 第四十四课:3D光晕

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  6. NeHe OpenGL教程 第四十课:绳子的模拟

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  7. NeHe OpenGL教程 第三十六课:从渲染到纹理

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  8. NeHe OpenGL教程 第三十五课:播放AVI

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  9. NeHe OpenGL教程 第三十二课:拾取游戏

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

随机推荐

  1. python 学习笔记-----编码问题

    1.python 最早支持的是ASCII编码. 所以对于普通的字符串"ABC"为ASCII编码的形式.字母和数字之间的转换函数为ord('字母')和chr(‘数字’)函数. ord ...

  2. .NET WebForm 简介

    WebForm是微软开发的一款产品,它将用户的请求和响应都封装为控件.让开发者认为自己是在操作一个windows界面.极大地提高了开发效率. 在学习WebForm时,其知识量比WinForm要多,在实 ...

  3. MongoDB整库备份与还原以及单个collection备份、恢复方法

    mongodb数据库维护离不开必要的备份.恢复操作,而且一般不会出错,所以我们在使用的时候大部分时候使用备份和恢复操作就可以了   mongodump.exe备份的原理是通过一次查询获取当前服务器快照 ...

  4. windows下安装KeystoneJS

    安装参考: http://keystonejs.com/zh/getting-started/ http://jsnoder.com/kjs/quickstart 安装前提条件: 安装 Node.JS ...

  5. iOS开发UI篇—使用picker View控件完成一个简单的选餐应用

    iOS开发UI篇—使用picker View控件完成一个简单的选餐应用 一.实现效果 说明:点击随机按钮,能够自动选取,下方数据自动刷新. 二.实现思路 1.picker view的有默认高度为162 ...

  6. iOS开发拓展篇—音频处理(音乐播放器6)

    iOS开发拓展篇—音频处理(音乐播放器6) 一.图片处理 说明: Aspect表示按照原来的宽高比进行缩放. Aspectfit表示按照原来的宽高比缩放,要求看到全部图片,后果是不能完全覆盖窗口,会留 ...

  7. css布局之三列布局

    网站上使用三列布局的还是比较多的,不过三列和两列有些相似: 1.自适应三列 <!DOCTYPE html> <html lang="en"> <hea ...

  8. SQL 解决in的参数烦恼(经典,简洁,高效)

    原SQL是不能执行的:select * from 表A where 字段A in (select 逗号分隔的字段B from 表B where 条件) 解决方案:select b.* from (se ...

  9. 关于QGraphicsScene 和 QGraphicsView 和 QDialog 的杂乱笔记【或说指针复习。。】

    LtCalibrateDlg::~LtCalibrateDlg() { if (m_pIplImageGray) cvReleaseImage(&m_pIplImageGray); MYDEL ...

  10. mysql表分区(摘自 MySQL表的四种分区类型)

    一.什么是表分区通俗地讲表分区是将一大表,根据条件分割成若干个小表.mysql5.1开始支持数据表分区了. 如:某用户表的记录超过了600万条,那么就可以根据入库日期将表分区,也可以根据所在地将表分区 ...