OpenGL鼠标轨迹球(Trackball)原理

什么是鼠标轨迹球

类似AutoCAD里的“动态观察”,三维模型都是要投影到二维的屏幕上才能显示给用户,而用户如果想观察一下三维模型的立体形状使用“动态观察”是再好不过了。我们一般的操作是这样的:鼠标(按中健或者其他健)在二维屏幕上拖动,之后三维模型就会以屏幕中心点为中心进行相应的旋转,鼠标拖动得越长,三维模型旋转的角度就越大。AutoCAD这种重量级的商业软件在这方面的用户体验自然是非常完美的了,可你知道它的原理么,如果自己用OpenGL如果实现呢?

OpenGL里的轨迹球

计算机的三维显示类似生活中的摄影,屏幕就是一个相机,三维模型就是被摄物体。我们可以调整相机与被摄物之间的距离来在屏幕显示不同大小影像。轨迹球就是在屏幕之外虚构一个球形曲面,使鼠标在二维屏幕上的移动投影到球形曲面上,这样就能得到更佳的用户体验(不使用轨迹球也能实现动态观察,只是效果很生硬)。

以屏幕为中心为球心,x轴向右,Y轴向上,z轴向屏幕之外,很容易建立一个球体的几何方程如下:

x2+y2+z2=r2x2+y2+z2=r2

这里,r代表球体的半径。

当鼠标在球面的范围内移动时,我们可以由鼠标在二维屏幕上的二维点坐标P(x,y)通过数学关系求得其在球面上的投影点P',鼠标从P1点移动到P2点,对应的在球面上就是从P1'移动到P2'。P1'和P2'与球心之间可以形成两个向量,鼠标移动转化成了向量从V1(OP1'向量)转到V2(OP2'向量),V1和V2的向量叉乘得到向量N即是三维物体的旋转轴,V1到V2的转角量就是三维物体的旋转角度。

使轨迹球更连续

实际的屏幕是个矩形,而球体在平面的投影只会是个圆,因此只要轨迹球的半径不是无限大,就总会有一些区域的点投影后会落在球面之外。此时怎么办呢?

一个好的办法就是在球体投影不能覆盖的区域使用另外一个曲面与之拼接。一个现成的二次曲面能够胜任,它的表达式如下:

z(x,y)=r2/2x2+y2−−−−−−√     

这个曲面与球面的交线正好一个圆(下图中所示红线),其半径为r/2–√r/2。

经过这样处理的轨迹球就比较平滑,于是整个坐标计算过程如下:

由于一般安装opengl时,并没有包含glew32.dll和头文件及.lib文件,所以要先将相关文件包含到相关目录中,这里给出glew的下载链接:点击打开链接点击打开链接

这里给出实现轨迹球的代码

ArcBall.h:

#include "StdAfx.h"#ifndef _ArcBall_h
#define _ArcBall_h#include <stdlib.h>// 仅在Debug模式下,启用断言
#ifdef _DEBUG
# include "assert.h"
#else
# define assert(x) { }
#endif//2维点
typedef union Tuple2f_t
{struct{GLfloat X, Y;} s;GLfloat T[2];
} Tuple2fT;     //3维点
typedef union Tuple3f_t
{struct{GLfloat X, Y, Z;} s;GLfloat T[3];
} Tuple3fT;      //4维点
typedef union Tuple4f_t
{struct{GLfloat X, Y, Z, W;} s;GLfloat T[4];
} Tuple4fT;      //3x3矩阵
typedef union Matrix3f_t
{struct{//column majorunion { GLfloat M00; GLfloat XX; GLfloat SX; };  union { GLfloat M10; GLfloat XY;             };  union { GLfloat M20; GLfloat XZ;             };  union { GLfloat M01; GLfloat YX;             };  union { GLfloat M11; GLfloat YY; GLfloat SY; };  union { GLfloat M21; GLfloat YZ;             };  union { GLfloat M02; GLfloat ZX;             };  union { GLfloat M12; GLfloat ZY;             };  union { GLfloat M22; GLfloat ZZ; GLfloat SZ; };  } s;GLfloat M[9];
} Matrix3fT;    //4x4矩阵
typedef union Matrix4f_t
{struct{//column majorunion { GLfloat M00; GLfloat XX; GLfloat SX; };  union { GLfloat M10; GLfloat XY;             };  union { GLfloat M20; GLfloat XZ;             };  union { GLfloat M30; GLfloat XW;             };  union { GLfloat M01; GLfloat YX;             };  union { GLfloat M11; GLfloat YY; GLfloat SY; };  union { GLfloat M21; GLfloat YZ;             };  union { GLfloat M31; GLfloat YW;             };  union { GLfloat M02; GLfloat ZX;             };  union { GLfloat M12; GLfloat ZY;             };  union { GLfloat M22; GLfloat ZZ; GLfloat SZ; };  union { GLfloat M32; GLfloat ZW;             };  union { GLfloat M03; GLfloat TX;             };  union { GLfloat M13; GLfloat TY;             };  union { GLfloat M23; GLfloat TZ;             };  union { GLfloat M33; GLfloat TW; GLfloat SW; };  } s;GLfloat M[16];
} Matrix4fT;    //定义类型的别名
#define Point2fT    Tuple2fT
#define Quat4fT     Tuple4fT
#define Vector2fT   Tuple2fT
#define Vector3fT   Tuple3fT
#define FuncSqrt    sqrtf
# define Epsilon 1.0e-5//2维点相加
inlinestatic void Point2fAdd(Point2fT* NewObj, const Tuple2fT* t1)
{assert(NewObj && t1);NewObj->s.X += t1->s.X;NewObj->s.Y += t1->s.Y;
}//2维点相减
inlinestatic void Point2fSub(Point2fT* NewObj, const Tuple2fT* t1)
{assert(NewObj && t1);NewObj->s.X -= t1->s.X;NewObj->s.Y -= t1->s.Y;
}//3维点矢积
inlinestatic void Vector3fCross(Vector3fT* NewObj, const Vector3fT* v1, const Vector3fT* v2)
{Vector3fT Result; assert(NewObj && v1 && v2);Result.s.X = (v1->s.Y * v2->s.Z) - (v1->s.Z * v2->s.Y);Result.s.Y = (v1->s.Z * v2->s.X) - (v1->s.X * v2->s.Z);Result.s.Z = (v1->s.X * v2->s.Y) - (v1->s.Y * v2->s.X);*NewObj = Result;
}//3维点点积
inlinestatic GLfloat Vector3fDot(const Vector3fT* NewObj, const Vector3fT* v1)
{assert(NewObj && v1);return  (NewObj->s.X * v1->s.X) +(NewObj->s.Y * v1->s.Y) +(NewObj->s.Z * v1->s.Z);
}//3维点的长度的平方
inlinestatic GLfloat Vector3fLengthSquared(const Vector3fT* NewObj)
{assert(NewObj);return  (NewObj->s.X * NewObj->s.X) +(NewObj->s.Y * NewObj->s.Y) +(NewObj->s.Z * NewObj->s.Z);
}//3维点的长度
inlinestatic GLfloat Vector3fLength(const Vector3fT* NewObj)
{assert(NewObj);return FuncSqrt(Vector3fLengthSquared(NewObj));
}//设置3x3矩阵为0矩阵
inlinestatic void Matrix3fSetZero(Matrix3fT* NewObj)
{NewObj->s.M00 = NewObj->s.M01 = NewObj->s.M02 = NewObj->s.M10 = NewObj->s.M11 = NewObj->s.M12 = NewObj->s.M20 = NewObj->s.M21 = NewObj->s.M22 = 0.0f;
}//设置4x4矩阵为0矩阵
inlinestatic void Matrix4fSetZero(Matrix4fT* NewObj)
{NewObj->s.M00 = NewObj->s.M01 = NewObj->s.M02 = NewObj->s.M10 = NewObj->s.M11 = NewObj->s.M12 = NewObj->s.M20 = NewObj->s.M21 = NewObj->s.M22 = NewObj->s.M30 = NewObj->s.M31 = NewObj->s.M32 = 0.0f;
}//设置3x3矩阵为单位矩阵
inlinestatic void Matrix3fSetIdentity(Matrix3fT* NewObj)
{Matrix3fSetZero(NewObj);NewObj->s.M00 = NewObj->s.M11 = NewObj->s.M22 = 1.0f;
}//设置4x4矩阵为单位矩阵
inlinestatic void Matrix4fSetIdentity(Matrix4fT* NewObj)
{Matrix4fSetZero(NewObj);NewObj->s.M00 = 1.0f;NewObj->s.M11 = 1.0f;NewObj->s.M22 = 1.0f;NewObj->s.M33=1.0f;
}//从四元数设置旋转矩阵
inlinestatic void Matrix3fSetRotationFromQuat4f(Matrix3fT* NewObj, const Quat4fT* q1)
{GLfloat n, s;GLfloat xs, ys, zs;GLfloat wx, wy, wz;GLfloat xx, xy, xz;GLfloat yy, yz, zz;assert(NewObj && q1);n = (q1->s.X * q1->s.X) + (q1->s.Y * q1->s.Y) + (q1->s.Z * q1->s.Z) + (q1->s.W * q1->s.W);s = (n > 0.0f) ? (2.0f / n) : 0.0f;xs = q1->s.X * s;  ys = q1->s.Y * s;  zs = q1->s.Z * s;wx = q1->s.W * xs; wy = q1->s.W * ys; wz = q1->s.W * zs;xx = q1->s.X * xs; xy = q1->s.X * ys; xz = q1->s.X * zs;yy = q1->s.Y * ys; yz = q1->s.Y * zs; zz = q1->s.Z * zs;NewObj->s.XX = 1.0f - (yy + zz); NewObj->s.YX =         xy - wz;  NewObj->s.ZX =         xz + wy;NewObj->s.XY =         xy + wz;  NewObj->s.YY = 1.0f - (xx + zz); NewObj->s.ZY =         yz - wx;NewObj->s.XZ =         xz - wy;  NewObj->s.YZ =         yz + wx;  NewObj->s.ZZ = 1.0f - (xx + yy);
}//3x3矩阵相乘
inlinestatic void Matrix3fMulMatrix3f(Matrix3fT* NewObj, const Matrix3fT* m1)
{Matrix3fT Result; assert(NewObj && m1);Result.s.M00 = (NewObj->s.M00 * m1->s.M00) + (NewObj->s.M01 * m1->s.M10) + (NewObj->s.M02 * m1->s.M20);Result.s.M01 = (NewObj->s.M00 * m1->s.M01) + (NewObj->s.M01 * m1->s.M11) + (NewObj->s.M02 * m1->s.M21);Result.s.M02 = (NewObj->s.M00 * m1->s.M02) + (NewObj->s.M01 * m1->s.M12) + (NewObj->s.M02 * m1->s.M22);Result.s.M10 = (NewObj->s.M10 * m1->s.M00) + (NewObj->s.M11 * m1->s.M10) + (NewObj->s.M12 * m1->s.M20);Result.s.M11 = (NewObj->s.M10 * m1->s.M01) + (NewObj->s.M11 * m1->s.M11) + (NewObj->s.M12 * m1->s.M21);Result.s.M12 = (NewObj->s.M10 * m1->s.M02) + (NewObj->s.M11 * m1->s.M12) + (NewObj->s.M12 * m1->s.M22);Result.s.M20 = (NewObj->s.M20 * m1->s.M00) + (NewObj->s.M21 * m1->s.M10) + (NewObj->s.M22 * m1->s.M20);Result.s.M21 = (NewObj->s.M20 * m1->s.M01) + (NewObj->s.M21 * m1->s.M11) + (NewObj->s.M22 * m1->s.M21);Result.s.M22 = (NewObj->s.M20 * m1->s.M02) + (NewObj->s.M21 * m1->s.M12) + (NewObj->s.M22 * m1->s.M22);*NewObj = Result;
}//4x4矩阵相乘
inlinestatic void Matrix4fSetRotationScaleFromMatrix4f(Matrix4fT* NewObj, const Matrix4fT* m1)
{assert(NewObj && m1);NewObj->s.XX = m1->s.XX; NewObj->s.YX = m1->s.YX; NewObj->s.ZX = m1->s.ZX;NewObj->s.XY = m1->s.XY; NewObj->s.YY = m1->s.YY; NewObj->s.ZY = m1->s.ZY;NewObj->s.XZ = m1->s.XZ; NewObj->s.YZ = m1->s.YZ; NewObj->s.ZZ = m1->s.ZZ;
}//进行矩阵的奇异值分解,旋转矩阵被保存到rot3和rot4中,返回矩阵的缩放因子
inlinestatic GLfloat Matrix4fSVD(const Matrix4fT* NewObj, Matrix3fT* rot3, Matrix4fT* rot4)
{GLfloat s, n;assert(NewObj);s = FuncSqrt(( (NewObj->s.XX * NewObj->s.XX) + (NewObj->s.XY * NewObj->s.XY) + (NewObj->s.XZ * NewObj->s.XZ) + (NewObj->s.YX * NewObj->s.YX) + (NewObj->s.YY * NewObj->s.YY) + (NewObj->s.YZ * NewObj->s.YZ) +(NewObj->s.ZX * NewObj->s.ZX) + (NewObj->s.ZY * NewObj->s.ZY) + (NewObj->s.ZZ * NewObj->s.ZZ) ) / 3.0f );if (rot3)   {rot3->s.XX = NewObj->s.XX; rot3->s.XY = NewObj->s.XY; rot3->s.XZ = NewObj->s.XZ;rot3->s.YX = NewObj->s.YX; rot3->s.YY = NewObj->s.YY; rot3->s.YZ = NewObj->s.YZ;rot3->s.ZX = NewObj->s.ZX; rot3->s.ZY = NewObj->s.ZY; rot3->s.ZZ = NewObj->s.ZZ;n = 1.0f / FuncSqrt( (NewObj->s.XX * NewObj->s.XX) +(NewObj->s.XY * NewObj->s.XY) +(NewObj->s.XZ * NewObj->s.XZ) );rot3->s.XX *= n;rot3->s.XY *= n;rot3->s.XZ *= n;n = 1.0f / FuncSqrt( (NewObj->s.YX * NewObj->s.YX) +(NewObj->s.YY * NewObj->s.YY) +(NewObj->s.YZ * NewObj->s.YZ) );rot3->s.YX *= n;rot3->s.YY *= n;rot3->s.YZ *= n;n = 1.0f / FuncSqrt( (NewObj->s.ZX * NewObj->s.ZX) +(NewObj->s.ZY * NewObj->s.ZY) +(NewObj->s.ZZ * NewObj->s.ZZ) );rot3->s.ZX *= n;rot3->s.ZY *= n;rot3->s.ZZ *= n;}if (rot4)  {if (rot4 != NewObj){Matrix4fSetRotationScaleFromMatrix4f(rot4, NewObj); }n = 1.0f / FuncSqrt( (NewObj->s.XX * NewObj->s.XX) +(NewObj->s.XY * NewObj->s.XY) +(NewObj->s.XZ * NewObj->s.XZ) );rot4->s.XX *= n;rot4->s.XY *= n;rot4->s.XZ *= n;n = 1.0f / FuncSqrt( (NewObj->s.YX * NewObj->s.YX) +(NewObj->s.YY * NewObj->s.YY) +(NewObj->s.YZ * NewObj->s.YZ) );rot4->s.YX *= n;rot4->s.YY *= n;rot4->s.YZ *= n;n = 1.0f / FuncSqrt( (NewObj->s.ZX * NewObj->s.ZX) +(NewObj->s.ZY * NewObj->s.ZY) +(NewObj->s.ZZ * NewObj->s.ZZ) );rot4->s.ZX *= n;rot4->s.ZY *= n;rot4->s.ZZ *= n;}return s;
}//从3x3矩阵变为4x4的旋转矩阵
inlinestatic void Matrix4fSetRotationScaleFromMatrix3f(Matrix4fT* NewObj, const Matrix3fT* m1)
{assert(NewObj && m1);NewObj->s.XX = m1->s.XX; NewObj->s.YX = m1->s.YX; NewObj->s.ZX = m1->s.ZX;NewObj->s.XY = m1->s.XY; NewObj->s.YY = m1->s.YY; NewObj->s.ZY = m1->s.ZY;NewObj->s.XZ = m1->s.XZ; NewObj->s.YZ = m1->s.YZ; NewObj->s.ZZ = m1->s.ZZ;
}//4x4矩阵的与标量的乘积
inlinestatic void Matrix4fMulRotationScale(Matrix4fT* NewObj, GLfloat scale)
{assert(NewObj);NewObj->s.XX *= scale; NewObj->s.YX *= scale; NewObj->s.ZX *= scale;NewObj->s.XY *= scale; NewObj->s.YY *= scale; NewObj->s.ZY *= scale;NewObj->s.XZ *= scale; NewObj->s.YZ *= scale; NewObj->s.ZZ *= scale;
}//设置旋转矩阵
inlinestatic void Matrix4fSetRotationFromMatrix3f(Matrix4fT* NewObj, const Matrix3fT* m1)
{GLfloat scale;assert(NewObj && m1);scale = Matrix4fSVD(NewObj, NULL, NULL);Matrix4fSetRotationScaleFromMatrix3f(NewObj, m1);Matrix4fMulRotationScale(NewObj, scale);
}typedef class ArcBall_t
{
protected://把二维点映射到三维点inlinevoid _mapToSphere(const Point2fT* NewPt, Vector3fT* NewVec) const;public://构造/析构函数ArcBall_t(GLfloat NewWidth, GLfloat NewHeight);~ArcBall_t() { };//设置边界inlinevoid    setBounds(GLfloat NewWidth, GLfloat NewHeight){assert((NewWidth > 1.0f) && (NewHeight > 1.0f));//设置长宽的调整因子this->AdjustWidth  = 1.0f / ((NewWidth  - 1.0f) * 0.5f);this->AdjustHeight = 1.0f / ((NewHeight - 1.0f) * 0.5f);}//鼠标点击void    click(const Point2fT* NewPt);//鼠标拖动计算旋转void    drag(const Point2fT* NewPt, Quat4fT* NewRot);//更新鼠标状态void    upstate();//void    mousemove(WPARAM wParam,LPARAM lParam);protected:Vector3fT   StVec;          //保存鼠标点击的坐标Vector3fT   EnVec;          //保存鼠标拖动的坐标GLfloat     AdjustWidth;    //宽度的调整因子GLfloat     AdjustHeight;   //长度的调整因子
public:Matrix4fT   Transform;      //计算变换            Matrix3fT   LastRot;        //上一次的旋转 Matrix3fT   ThisRot;        //这次的旋转float zoomRate;float lastZoomRate;bool        isDragging;     // 是否拖动bool        isRClicked;     // 是否右击鼠标bool        isClicked;      // 是否点击鼠标bool        isZooming;    //是否正在缩放Point2fT    LastPt;        Matrix4fT origTransform;Point2fT    MousePt;        // 当前的鼠标位置} ArcBallT;#endif

ArcBall.cpp:文件

/** KempoApi: The Turloc Toolkit *****************************/
/** *    *                                                  **/
/** **  **  Filename: ArcBall.cpp                           **/
/**   **    Version:  Common                                **/
/**   **                                                    **/
/**                                                         **/
/**  Arcball class for mouse manipulation.                  **/
/**                                                         **/
/**                                                         **/
/**                                                         **/
/**                                                         **/
/**                              (C) 1999-2003 Tatewake.com **/
/**   History:                                              **/
/**   08/17/2003 - (TJG) - Creation                         **/
/**   09/23/2003 - (TJG) - Bug fix and optimization         **/
/**   09/25/2003 - (TJG) - Version for NeHe Basecode users  **/
/**                                                         **/
/*************************************************************/
//
#include "StdAfx.h"
#include <windows.h>
#include <GL/glew.h>        #include <math.h>
#include "ArcBall.h"                                 //轨迹球参数:
//直径                    2.0f
//半径                    1.0f
//半径平方                1.0fvoid ArcBall_t::_mapToSphere(const Point2fT* NewPt, Vector3fT* NewVec) const
{Point2fT TempPt;GLfloat length;//复制到临时变量TempPt = *NewPt;//把长宽调整到[-1 ... 1]区间TempPt.s.X  =        (TempPt.s.X * this->AdjustWidth)  - 1.0f;TempPt.s.Y  = 1.0f - (TempPt.s.Y * this->AdjustHeight);//计算长度的平方length      = (TempPt.s.X * TempPt.s.X) + (TempPt.s.Y * TempPt.s.Y);//如果点映射到球的外面if (length > 1.0f){GLfloat norm;//缩放到球上norm    = 1.0f / FuncSqrt(length);//设置z坐标为0NewVec->s.X = TempPt.s.X * norm;NewVec->s.Y = TempPt.s.Y * norm;NewVec->s.Z = 0.0f;}//如果在球内else   {//利用半径的平方为1,求出z坐标NewVec->s.X = TempPt.s.X;NewVec->s.Y = TempPt.s.Y;NewVec->s.Z = FuncSqrt(1.0f - length);}
}ArcBall_t::ArcBall_t(GLfloat NewWidth, GLfloat NewHeight)
{this->StVec.s.X     = 0.0f;this->StVec.s.Y     = 0.0f;this->StVec.s.Z     = 0.0f;this->EnVec.s.X     = 0.0f;this->EnVec.s.Y     = 0.0f;this->EnVec.s.Z     = 0.0f;Matrix4fSetIdentity(&Transform);Matrix3fSetIdentity(&LastRot);Matrix3fSetIdentity(&ThisRot);this->isDragging=false;this->isClicked= false;this->isRClicked = false;this->isZooming = false;this->zoomRate = 1;this->setBounds(NewWidth, NewHeight);
}void ArcBall_t::upstate()
{if(!this->isZooming && this->isRClicked){                    // 开始拖动this->isZooming = true;                                        // 设置拖动为变量为true        this->LastPt = this->MousePt;this->lastZoomRate = this->zoomRate;}else if(this->isZooming){//正在拖动if(this->isRClicked){  //拖动        Point2fSub(&this->MousePt, &this->LastPt);this->zoomRate = this->lastZoomRate + this->MousePt.s.X * this->AdjustWidth * 2;}else{                                            //停止拖动this->isZooming = false;}}else if (!this->isDragging && this->isClicked){                                                // 如果没有拖动this->isDragging = true;                                        // 设置拖动为变量为truethis->LastRot = this->ThisRot;                                        this->click(&this->MousePt);                                }else if(this->isDragging){if (this->isClicked){                                            //如果按住拖动Quat4fT     ThisQuat;this->drag(&this->MousePt, &ThisQuat);                        // 更新轨迹球的变量Matrix3fSetRotationFromQuat4f(&this->ThisRot, &ThisQuat);        // 计算旋转量Matrix3fMulMatrix3f(&this->ThisRot, &this->LastRot);                Matrix4fSetRotationFromMatrix3f(&this->Transform, &this->ThisRot);    }else                                                        // 如果放开鼠标,设置拖动为falsethis->isDragging = false;}
}//按下鼠标,记录当前对应的轨迹球的位置
void    ArcBall_t::click(const Point2fT* NewPt)
{this->_mapToSphere(NewPt, &this->StVec);
}//鼠标拖动,计算旋转四元数
void    ArcBall_t::drag(const Point2fT* NewPt, Quat4fT* NewRot)
{//新的位置this->_mapToSphere(NewPt, &this->EnVec);//计算旋转if (NewRot){Vector3fT  Perp;//计算旋转轴Vector3fCross(&Perp, &this->StVec, &this->EnVec);//如果不为0if (Vector3fLength(&Perp) > Epsilon)    {//记录旋转轴NewRot->s.X = Perp.s.X;NewRot->s.Y = Perp.s.Y;NewRot->s.Z = Perp.s.Z;//在四元数中,w=cos(a/2),a为旋转的角度NewRot->s.W= Vector3fDot(&this->StVec, &this->EnVec);}//是0,说明没有旋转else                                 {NewRot->s.X = NewRot->s.Y = NewRot->s.Z = NewRot->s.W = 0.0f;}}
}

sample.cpp:

#include "StdAfx.h"
#pragma comment( lib, "opengl32.lib" )
#pragma comment( lib, "glut32.lib")
#pragma comment( lib, "glew32.lib")
#include <GL/glew.h>
#include <GL/glut.h>
#include <GL/glu.h>
#include <math.h>
#include "ArcBall.h"                                                                                                                            //初始化,必须用全局变量的方式,不能用new
ArcBallT arcBall(600.0f,400.0f);
ArcBallT*    ArcBall =&arcBall;// new ArcBallT(600.0f,400.0f);//&arcBall;void reshape(int w, int h){glViewport(0,0, w, h);glMatrixMode(GL_PROJECTION);glLoadIdentity();glFrustum(-1, 1, -1, 1, 1.5, 20);glMatrixMode(GL_MODELVIEW);ballArcBall->setBounds((GLfloat)w, (GLfloat)h);//1. 设置窗口边界
}
void init(){glClearColor(0,0,0,0);glShadeModel(GL_FLAT);
}void display (void)
{glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);                glColor3f(1.0,1.0,1.0);glLoadIdentity();gluLookAt(0.0, 0.0, 5.0,0.0, 0.0, 0.0,0.0, 1.0, 0.0);glScalef(1.0, 2.0, 1.0);//glPushMatrix();                glTranslatef(0,0,-3);            glScalef(ArcBall->zoomRate, ArcBall->zoomRate, ArcBall->zoomRate);//2. 缩放glMultMatrixf(ArcBall->Transform.M);                        //3. 旋转glutWireCube(1.0);//glPopMatrix();                                                    glFlush ();
}
//移动
void move(int x, int y)
{ArcBall->MousePt.s.X = x;ArcBall->MousePt.s.Y = y;ArcBall->upstate();glutPostRedisplay();
}
//点击
void mouse(int button, int state, int x, int y)
{if(button == GLUT_LEFT_BUTTON && state==GLUT_DOWN){ArcBall->isClicked = true;move(x,y);}else if(button == GLUT_LEFT_BUTTON && state==GLUT_UP)ArcBall->isClicked = false;else if(button == GLUT_RIGHT_BUTTON && state==GLUT_DOWN){ArcBall->isRClicked = true;move(x,y);}else if(button == GLUT_RIGHT_BUTTON && state == GLUT_UP)ArcBall->isRClicked = false;ArcBall->upstate();glutPostRedisplay();
}
int main(int argc, char** argv)
{glutInit(&argc, argv);glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);glutInitWindowSize(640,480);glutCreateWindow("HI");init();glutDisplayFunc(display);glutReshapeFunc(reshape);glutMouseFunc(mouse);        //registered the mouse event.glutMotionFunc(move);        //registered the move eventglutMainLoop();return 0;
}

另推荐一个博客,也是讲解轨迹球:点击打开链接

OpenGL中的轨迹球问题相关推荐

  1. 【OpenGL(SharpGL)】支持任意相机可平移缩放的轨迹球实现

    [OpenGL(SharpGL)]支持任意相机可平移缩放的轨迹球实现 阅读目录(Content) 2016-07-08 2016-02-10 1. 轨迹球原理 2. 轨迹球实现1) 计算投影点2) 计 ...

  2. OpenGL鼠标轨迹球

    转自:http://www.lubanren.net/weblog/post/283.html 什么是鼠标轨迹球 类似AutoCAD里的"动态观察",三维模型都是要投影到二维的屏幕 ...

  3. 在MFC中,运用轨迹球算法实现鼠标旋转物体

    参考资料:nehe教程第48课<轨迹球实现的鼠标旋转> 1.在头文件中为Arcball添加变量 //为Arcball添加变量用来获取当前鼠标点Point2fT MousePt;// NEW ...

  4. OpenGL MFC单文档 实现轨迹球

    OpenGL MFC单文档 实现轨迹球 前言:我是一个怀揣着伟大梦想的普通男生!结果就是,一有时间就幻想着自己的伟大梦想,忘了自己下一步该迈出左脚还是右脚.这不,只顾做梦了,时光可不管我,独自一个人走 ...

  5. OpenGL(sharpGL)支持任意相机可平移缩放的轨迹球原理描述及源码

    本文提供一个本人编写的轨迹球类(ArcBall.cs),它可以直接应用到任何 camera 下,还可以同时实现缩放和平移. 工程源代码在文末. 1.轨迹球原理: 上面是我黑来的两张图,拿来说明轨迹球的 ...

  6. opengl学习笔记Ⅴ——磨人的轨迹球真可爱

    这回我们继续优化交互.使用轨迹球来使得模型转得更加人性化. 轨迹球可以理解为模型外部套一个球体,在我们鼠标拖动旋转模型时,能够准确地模拟出球体被拖动的感觉. 首先,我在这里放弃了透视投影,考虑透视投影 ...

  7. OpenGL轨迹球代码

    转自:http://blog.sina.com.cn/s/blog_8d8425f30100ywap.html 从网上下载的经典OpenGL轨迹球代码,自己增加了缩放操作,共三个文件: sam从网上下 ...

  8. NeHe OpenGL教程 第四十八课:轨迹球

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

  9. opengl开启垂直同步_你的下一部鼠标,何必是鼠标?来试试肯辛通垂直无线轨迹球?...

    标题无耻的抄袭了苹果的广告语,不过也算是对我这篇文章很好地概括了.我之前体验过的鼠标产品说成百上千有点夸张,小几十个应该也有了,从办公的到游戏的,从有线的到无线的,从功能丰富的到造型奇特的,而这次体验 ...

最新文章

  1. ROS中使用摄像头的问题
  2. vue 循环tabs 标签页 组件_vue学习笔记--v-for循环标签页label并实现根据不同label表格切换...
  3. java数组类型转换_java数据类型转换和数组总结
  4. 停止了IIS服务为什么apache还是启动不了呢?
  5. 解决undefined reference to symbol ‘sem_close@@GLIBC_2.2.5‘问题
  6. KMP算法代学习之(二)代码深入学习
  7. Flask 开发填坑
  8. OpenGL秒安装及显示
  9. oracle 树形结构表,树结构表递归查询在ORACLE和MSSQL中的实现方法
  10. Openbiz 推进PHP进入大App Store时代
  11. Java RMI远程方法调用学习总结
  12. 二进制空间权重矩阵_“生成空间权重矩阵”的工作原理
  13. 计算机窗体视频教程,计算机二级Access2010视频教程
  14. 软件测试电脑内存适配,利用Memtest86 测试你电脑的内存
  15. 服务器xp系统网页打不开,xp系统打不开网页的具体方案
  16. (转帖) cglib和jdk的区别
  17. 七、微信小程序运行报错:Error: AppID 不合法,invalid appid
  18. 七层网络模型(大致信息讲解)
  19. 天气预报插件使用教程
  20. 这个高薪行业正在大量招人,你会考虑吗?

热门文章

  1. 最短路径问题----Dijkstra算法的解释
  2. MetaSploit攻击实例讲解------攻击445端口漏洞(kali linux 2016.2(rolling))(详细)
  3. GPS 气压计高度测量
  4. Classical Inheritance in JavaScript
  5. supersocket中quickstart文件夹下的MultipleCommandAssembly的配置文件分析
  6. SharePoint文档库,如何在新窗口打开中的文件
  7. 递归遍历文件夹,并添加到TreeView控件中
  8. 一个通用Makefile详解
  9. C++ Primer 5th笔记(chap 18 大型程序工具)虚继承
  10. 区块链BaaS云服务(14)华大BGI区块链“碎片分布式存储“