OpenGL中如何实现通过鼠标点击选取对象(正交投影)

最近在用OpenGL写一个魔方程序,考虑到需要选取其中的小方块,于是我研究了半天,发现了这个方法

这种方法暂时只能在正交投影下使用,不需要OpenGL自带的什么glpick之类的方法,说实话,我现在并不想学习那种方法


接下来,我要介绍方法的大概思路:

该方法的核心是对凸包算法的使用以及OpenGL里的矩阵运算()即仿射变换)但是凸包算法已经是一个经典算法,大家尝试自己解决,如果实在不行,我下面会发一下我的土包代码。

首先建立cube对象,cube指的是魔方之中每个小的立方体方块。每个cube对象都维护:


一个相对矩阵(MATRIX[16]),


一个绝对矩阵(ABSOLUTE_MATRIX[16]),


一个初始位置数组origion_position[3],固定不变


一个绝对当前位置数组cur_abs_position[3],可以改变,值由origon_position与MATRIX相乘得出

这组数据中实际上用到的只是z坐标值(因此尚有改进之处),用于判断深度,如果忽略深度条件,鼠标点击可能会选取到多个对象,但是一般来说我们想选的是我们可以看到的,即离我们最近的哪一个对象,因此需要进行深度比较


(其实还有一个相对的当前位置current_position[3],但是这与今天要讲的选取没有关系,所以姑且不多提),


八个顶点的初始位置origion_vertex[24],固定不变


八个顶点的当前位置urrent_vertex[24],可以改变,值由origon_vertex与ABSOLUTE_MATRIX相乘得出

这八组数据主要用到每组数据的x坐标与y坐标,相当于八个二维点,通过这八个点来求出他们的凸包,进而当鼠标点击窗口某个位置时用于判断在哪些凸包之内

因为实际上只需要16个信息,因此尚有改进之处


整个程序还要维护一个矩阵M[16],用于进行整个魔方的旋转与通过与MATRIX结合来求ABSOLUTE_MATRIX;


接下来下介绍各种矩阵的获取方法:

MATRIX的获取方法:

因为魔方有6个面可以先把每个面作为一个组,然后每次旋转魔方的一个面时,对各个组的成员进行重新分配,对每个小方块(cube)的MATRIX重新获取

代码如下:

void rotate(int right, int angle) {

//当time为9时,说明旋转了90度,此刻才会形成完整的一步旋转

for (int i = 0;i < 9;i++) {


glPushMatrix();


//每做一次小的旋转,都要对小方块的当前位置更新一下
glLoadIdentity();


//当coord为零时,说明group所在的面与x轴垂直,则绕x轴旋转
//         为1时,                  y           y
//           2                     z           z
if (coord == 0)
glRotatef(angle, right, 0, 0);
else if (coord == 1)
glRotatef(angle, 0, right, 0);
else if (coord == 2)
glRotatef(angle, 0, 0, right);


glMultMatrixf(cubes[i]->MATRIX);
glGetFloatv(GL_MODELVIEW_MATRIX, cubes[i]->MATRIX);
glPopMatrix();

//每次小的旋转都要改变当前位置
//这样才可以正确画出方块
cubes[i]->changePosition();
mc->firstNine[i] = cubes[i]->index;
cubes[i]->changeAbsData(M);

}

这个不是今天的重点,详情请研究原代码及注释

通过该代码主要了解到,MATRIX的获取是对每个小方块进行如下操作(伪代码):

for(i=0:n)

第一步,保存当前矩阵: glpushMatrix();

第二步,当前矩阵变为单位阵: glloadIdentity();

第三步: 调用一系列变换函数;

第四步,右乘当前cube的MATRIX: glmultMatrix(cube[i]->MATRIX);

第五步,获取新的MATRIX: glGetFloat(GL_MODEL_MATRIX,cube[i]->MATRIX);

第六步:还原之前保存的矩阵: glPopMatrix();



M的获取方法(例如要对图像进行总体的旋转),与MAYTRIX的获取有些类似,可以采用如下代码,改代码时glMotionFunc的一个回掉函数:

void motion(int x, int y) {

//变换一下坐标系
y = 600 - y;
float dx = -stx +x;
float dy = -sty + y;
glLoadIdentity();
glRotatef(2, -dy, dx, 0);
glMultMatrixf(M);
glGetFloatv(GL_MODELVIEW_MATRIX, M);
glutPostRedisplay();
stx = x;
sty = y;
}

其中:

stx,sty是鼠标按下左键时的坐标(已经经过变换,现在以左下角原点),

x,y是鼠标拖动的坐标,以左上角为原点,需要变换使其以左下角为原点

dx,dy是鼠标拖动的位移,通过他的方向来确定物体的旋转方向

有时可能会出现旋转方向与鼠标拖动方向下给你返的情况,则把一下两行代码的正负号变换一下:

float dx = -stx +x;
float dy = -sty + y;

改为:

float dx = stx -x;
float dy = sty - y;

尤其注意,每当拖动一次鼠标之后,在函数最后要更新stx,sty的位置


ABSOLUTE_MATRIX的方法很简单,调用一下下列代码段就可以:

glPushMatrix();//保存当前矩阵
glLoadIdentity();//是当前矩阵变为单位阵
glMultMatrixf(M);//右乘M
glMultMatrixf(MATRIX);//右乘MATRIX
glGetFloatv(GL_MODELVIEW_MATRIX, ABSOLUTE_MATRIX);//获得当前矩阵,即ABSOLUTE_MATRIX
glPopMatrix();//恢复之前保存的矩阵


获得了各种矩阵,就可以求各种绝对位置,相对位置,求取方法就是简单向量与矩阵相称的原理,采用一种简单的函数就可以轻而易举地实现,下面是一种函数:

void changeVector(float *MAT, float* o_v, float *n_v) {
float tm[16];
//矩阵转置
for (int i = 0;i < 4;i++) {
for (int j = 0;j < 4;j++) {
tm[i * 4 + j] = MAT[j * 4 + i];
// printf("%f ", tm[i * 4 + j]);
}
// printf("\n");
}


for (int i = 0;i < 3;i++) {
n_v[i] = 0;
for (int j = 0;j < 3;j++) {
n_v[i] += tm[i * 4 + j] * o_v[j];
}
}
}

函数中,MAT表示要乘的矩阵,o_v表示变换之前的向量,n_v表示变换之后的向量


这样,我们就可以当每次相改变一次图形各部分位置时(例如,我的模仿可以旋转)就可以通过一下步骤来对各个顶点的信息进行更改:

  • 1
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值