0
点赞
收藏
分享

微信扫一扫

OpenGL实现3D模型自由旋转——之代码解析


OpenGL实现模型自由旋转——之代码解析

 

实现一个OpenGL模型旋转的例子代码。请参考文章“OpenGL实现3DS文件中的模型自由旋转”和本例的代码以期更还理解原文。“OpenGL实现3DS文件中的模型自由旋转”

 

基本原理在文章中说的很清楚,遗憾的是没有实现的代码,理解起来就很困难。我尝试补全它,参考下面的代码:

 

// oglTest.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"

#include <math.h> // sqrt

typedef struct
{
float x,
y,
z;
}Vector3f;

typedef struct
{
float x,
y,
z,
w;
}Vector4f, Quat4f;

typedef struct
{
float x,
y;
}Vector2f;

// 屏幕鼠标点的坐标转为模拟球的球面坐标。
// 球的半径总是 1, 所以模拟球的球面坐标为单位矢量
//
void Screen2Sphere(float w, // 窗口区域宽度(像素)
float h, // 窗口区域高度(像素)
float x, // 鼠标点的位置(x, y)
float y,
Vector3f *v // 球面坐标单位矢量
)
{
// 鼠标点映射到 [-1,1]
//
if (w<2) w=2; // 防止被 0 除
if (h<2) h=2;

x = ((x/((w-1)/2))-1);
y = -((y/((h-1)/2))-1); // OGL需要, Y 轴反向
float d2 = x*x + y*y;
float d = sqrt(d2);

if (d2>1.0f){
v->x = x/d;
v->y = y/d;
v->z = 0.f;
}
else{
v->x = x;
v->y = y;
v->z = sqrt(1.0f-d2);
}
}

// 使向量成为单位向量, 即模为1: x*x+y*y+z*z=1
void MakeNormal(Vector3f *v)
{
float m = 1.0f/sqrt(v->x*v->x + v->y*v->y + v->z*v->z);
v->x *= m;
v->y *= m;
v->z *= m;
}

// 计算2矢量叉积,叉积的结果就是2矢量构成的平面的法向量n
// n=a cross b;
void VectorCross(const Vector3f *a, const Vector3f *b, Vector3f *n)
{
n->x = a->y*b->z - b->y*a->z;
n->y = a->z*b->x - b->z*a->x;
n->z = a->x*b->y - b->x*a->y;
MakeNormal(n);
}

// 计算2矢量点积,点积的结果就是2矢量构成的夹角的余弦
// n=a cross b;
void VectorDot(const Vector3f *a, const Vector3f *b, float *dot)
{
*dot = a->x*b->x+a->y*b->y+a->z*b->z;
}

// 计算起点(start)到拖动点(dragging)的旋转矩阵
// 说明: 鼠标左键第1次按下的点记为起点, 然后鼠标左键不松开, 移动鼠标, 此时的鼠标轨迹点都是
// 一系列拖动点(draggings).
// 程序需要在WM_MOUSEMOVE事件处理中得到拖动点的窗口区域位置, 通过下面的函数计算实时的变换矩阵
// 下面的函数计算从上一点(start)到当前点(drag)拖动的过程所产生的变换结果(trans):
// 旋转轴 (x,y,z) 和夹角(a)
void OnDragging(float w, // 窗口的宽度(像素)
float h, // 窗口的高度(像素)
const Vector2f *start,
const Vector2f *drag,
Vector4f *trans)
{
// 变换到球面单位矢量
Vector3f startV;
Screen2Sphere(w, h, start->x, start->y, &startV);

Vector3f dragV;
Screen2Sphere(w, h, drag->x, drag->y, &dragV);

// 在球上给定两个点后(起始点和终点)怎样确定旋转的轴和角度?
// 旋转轴是两个鼠标矢量所张成的平面的法向量, 所以可以通过2矢量的叉积得到,即:
VectorCross(&startV, &dragV, (Vector3f*)trans);

// 而旋转角度就是m1和m2之间的夹角a, 点积得到:
VectorDot(&startV, &dragV, &trans->w);
}

/**
四元数到3x3旋转矩阵的变换:

公式参考:http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToMatrix/index.htm

一个变换的计算器:http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToMatrix/program/index.htm
可以用来验证你的代码是否正取

m3x3=[
1 - 2*qy*qy - 2*qz*qz, 2*qx*qy - 2*qz*qw, 2*qx*qz + 2*qy*qw
2*qx*qy + 2*qz*qw, 1 - 2*qx*qx - 2*qz*qz, 2*qy*qz - 2*qx*qw
2*qx*qz - 2*qy*qw, 2*qy*qz + 2*qx*qw, 1 - 2*qx*qx - 2*qy*qy ];
*/
void QuatToMatrix(Quat4f *q, float m[3][3])
{
float sqw = q->w*q->w;
float sqx = q->x*q->x;
float sqy = q->y*q->y;
float sqz = q->z*q->z;

// invs (inverse square length) is only required if quaternion is not already normalised
float invs = 1.0f / (sqx + sqy + sqz + sqw);

// since sqw + sqx + sqy + sqz =1/invs*invs
m[0][0] = ( sqx - sqy - sqz + sqw)*invs;

m[1][1] = (-sqx + sqy - sqz + sqw)*invs;
m[2][2] = (-sqx - sqy + sqz + sqw)*invs;

float tmp1 = q->x*q->y;
float tmp2 = q->z*q->w;

m[1][0] = 2.0f * (tmp1 + tmp2)*invs;
m[0][1] = 2.0f * (tmp1 - tmp2)*invs;

tmp1 = q->x*q->z;
tmp2 = q->y*q->w;

m[2][0] = 2.0f * (tmp1 - tmp2)*invs;
m[0][2] = 2.0f * (tmp1 + tmp2)*invs;

tmp1 = q->y*q->z;
tmp2 = q->x*q->w;

m[2][1] = 2.0f * (tmp1 + tmp2)*invs;
m[1][2] = 2.0f * (tmp1 - tmp2)*invs;
}

int _tmain(int argc, _TCHAR* argv[])
{
int i,j;
float width=400;
float height=300;

Vector3f Vp;
Screen2Sphere(width, height, 50, 50, &Vp);

Vector2f start={50,0};
Vector2f drag={0,50};

Vector4f trans;

OnDragging(width, height, &start, &drag, &trans);

float m[3][3];
QuatToMatrix(&trans, m);

for(i=0; i<3; i++){
for(j=0; j<3; j++){
printf(" %.4f ", m[i][j]);
}
printf("/n");
}

return 0;
}

举报

相关推荐

OpenGL学习14——3D模型

3D旋转菜单

3D旋转 相册

HTML实现3D图片旋转效果

3D旋转tab图

CSS 3D旋转效果

0 条评论