实现鼠标对立体图的控制非常重要,比如可以要求转动鼠标使得立体图形跟着旋转,以看清立体图的全貌
首先是转动,就是拖动鼠标,图形可以跟着转
我暂时想到了两种办法可以实现这一点,第一种是不改变立体图形,改变观察点的位置,第二是不改变观察点的位置,对立体进行旋转。
我也是真的是没想到这个问题越来越复杂,说白了还是数学没学好
不过经过一天的努力,好多计划我已经放弃了,不过现在先挖几个坑,之后再填。。当然更希望CSDN上的大神可以帮帮我解决一下。。
第一种是改变观察点的位置,先上代码~~
#include#include #include #include static const GLfloat vertex_list[][3] = { -0.5f, -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 0.5f, }; GLfloat colors[][3] = { { 1.0, 0.0, 0.0 }, { 1.0, 1.0, 0.0 }, { 0.0, 1.0, 0.0 }, { 0.0, 1.0, 1.0 }, { 1.0, 0.0, 1.0 }, { 0.0, 0.0, 1.0 }, { 0.0, 0.5, 0.0 }, { 0.0, 0.5, 0.5 }, }; static const GLint index_list[][4] = { 0, 1, 2, 3,//bottem 0, 3, 7, 4,//left 2, 3, 7, 6,//front 1, 2, 6, 5,//right 0, 1, 5, 4,//back 4, 5, 6, 7//top }; float M_PI=3.14159265f; static float c = M_PI/180.0f; //弧度和角度转换参数 static int du = 90, oldmy = -1, oldmx = -1; //du是视点绕y轴的角度,opengl里默认y轴是上方向 static float r = 1.75f, h = 0.0f; //r是视点绕y轴的半径,h是视点高度即在y轴上的坐标 void display(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(80.0f,1.0f, 1.0f, 1000.0f); glMatrixMode(GL_MODELVIEW); //printf("At:%.2f %.2f %.2f\n",r*cos(c*du),h,r*sin(c*du)); //这就是视点的坐标 glLoadIdentity(); gluLookAt(r*cos(c*du), h, r*sin(c*du), 0, 0, 0, 0, 1, 0); //从视点看远点,y轴方向(0,1,0)是上方向 for (int i = 0; i < 6; ++i) // 有六个面,循环六次 { glColor3f(colors[i][0], colors[i][1], colors[i][2]); glBegin(GL_POLYGON); for (int j = 0; j < 4; ++j) // 每个面有四个顶点,循环四次 glVertex3fv(vertex_list[index_list[i][j]]); glEnd(); } glColor3f(1, 0, 0); for (int i = 0; i < 6; ++i) // 有六个面,循环六次 { glBegin(GL_LINE_LOOP); for (int j = 0; j < 4; ++j) // 每个面有四个顶点,循环四次 glVertex3fv(vertex_list[index_list[i][j]]); glEnd(); } glFlush(); glutSwapBuffers(); } void Mouse(int button, int state, int x, int y) //处理鼠标点击 { if (state == GLUT_DOWN) //第一次鼠标按下时,记录鼠标在窗口中的初始坐标 oldmx = x, oldmy = y; } void onMouseMove(int x, int y) //处理鼠标拖动 { //printf("%d\n",du); du += x - oldmx; //鼠标在窗口x轴方向上的增量加到视点绕y轴的角度上,这样就左右转了 h += 0.03f*(y - oldmy); //鼠标在窗口y轴方向上的改变加到视点的y坐标上,就上下转了 //if (h>1.0f) h = 1.0f; //视点y坐标作一些限制,不会使视点太奇怪 //else if (h<-1.0f) h = -1.0f; oldmx = x, oldmy = y; //把此时的鼠标坐标作为旧值,为下一次计算增量做准备 } int main(int argc, char *argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH); glutInitWindowPosition(100, 100); glutInitWindowSize(400, 400); glutCreateWindow("OpenGL"); glutDisplayFunc(display); glutIdleFunc(display); //设置不断调用显示函数 glutMouseFunc(Mouse); glutMotionFunc(onMouseMove); glutMainLoop(); return 0; }
其中比较重要的是这么几个地方
首先前面的
glLoadIdentity();
gluLookAt(r*cos(c*du), h, r*sin(c*du), 0, 0, 0, 0, 1, 0); //从视点看远点,y轴方向(0,1,0)是上方向
static float c = M_PI/180.0f; //弧度和角度转换参数
static int du = 90, oldmy = -1, oldmx = -1; //du是视点绕y轴的角度,opengl里默认y轴是上方向
gluLookAt函数我们在之前就介绍过了,在绕着y轴旋转的的方向可以保持物体的大小不变,这是因为对y轴的半径没变
然后就是这里
void Mouse(int button, int state, int x, int y) //处理鼠标点击
{
if (state == GLUT_DOWN) //第一次鼠标按下时,记录鼠标在窗口中的初始坐标
oldmx = x, oldmy = y;
}
void onMouseMove(int x, int y) //处理鼠标拖动
{
//printf("%d\n",du);
du += x - oldmx; //鼠标在窗口x轴方向上的增量加到视点绕y轴的角度上,这样就左右转了
h += 0.03f*(y - oldmy); //鼠标在窗口y轴方向上的改变加到视点的y坐标上,就上下转了
//if (h>1.0f) h = 1.0f; //视点y坐标作一些限制,不会使视点太奇怪
//else if (h<-1.0f) h = -1.0f;
oldmx = x, oldmy = y; //把此时的鼠标坐标作为旧值,为下一次计算增量做准备
}
第一个事件记录鼠标点击;
第二个事件记录鼠标点击后移动的偏移量
然后du和h分别是两个方向的偏移量,然后每次移动都记录偏移量,为了避免视角过于夸张,这里限制了两行,对h的大小有了一定的限制
gluLookAt(r*cos(c*du), h, r*sin(c*du), 0, 0, 0, 0, 1, 0); //从视点看远点,y轴方向(0,1,0)是上方向
这个视角其实是圆柱坐标,想要改造成球坐标只需
把刚刚那句改为
gluLookAt(r*cos(c*du)*cos(c*h), r*sin(c*h), r*sin(c*du)*cos(c*h), 0, 0, 0, 0, 1, 0); //从视点看远点,y轴方向(0,1,0)是上方向
但是为了避免绕过北极或者南极的情况,对h也有限制
void Mouse(int button, int state, int x, int y) //处理鼠标点击
{
if (state == GLUT_DOWN) //第一次鼠标按下时,记录鼠标在窗口中的初始坐标
oldmx = x, oldmy = y;
}
void onMouseMove(int x, int y) //处理鼠标拖动
{
//printf("%d\n",du);
du += x - oldmx; //鼠标在窗口x轴方向上的增量加到视点绕y轴的角度上,这样就左右转了
h += (y - oldmy); //鼠标在窗口y轴方向上的改变加到视点的y坐标上,就上下转了
if (h > 90)
{ h = 90; }
else if (h < -90)
{ h = -90;}
//if (h>1.0f) h = 1.0f; //视点y坐标作一些限制,不会使视点太奇怪
//else if (h<-1.0f) h = -1.0f;
oldmx = x, oldmy = y; //把此时的鼠标坐标作为旧值,为下一次计算增量做准备
}
然后把r改成1.85即可
不过这里有第一个坑:怎样设计可以绕过北极和南极
因为sin函数在达到最大后会回去,这样不加限制会很奇怪
第二种方法是对物体进行旋转
#include
#include
#include
#include
static const GLfloat vertex_list[][3] = {
-0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
-0.5f, 0.5f, -0.5f,
-0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
};
GLfloat colors[][3] = {
{ 1.0, 0.0, 0.0 },
{ 1.0, 1.0, 0.0 },
{ 0.0, 1.0, 0.0 },
{ 0.0, 1.0, 1.0 },
{ 1.0, 0.0, 1.0 },
{ 0.0, 0.0, 1.0 },
{ 0.0, 0.5, 0.0 },
{ 0.0, 0.5, 0.5 },
};
static const GLint index_list[][4] = {
0, 1, 2, 3,//bottem
0, 3, 7, 4,//left
2, 3, 7, 6,//front
1, 2, 6, 5,//right
0, 1, 5, 4,//back
4, 5, 6, 7//top
};
float M_PI=3.14159265f;
static float c = M_PI/180.0f; //弧度和角度转换参数
static int du = 90, oldmy = -1, oldmx = -1; //du是视点绕y轴的角度,opengl里默认y轴是上方向
static float r = 1.75f, h = 0.0f; //r是视点绕y轴的半径,h是视点高度即在y轴上的坐标
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(80.0f,1.0f, 1.0f, 1000.0f);
glMatrixMode(GL_MODELVIEW);
//printf("At:%.2f %.2f %.2f\n",r*cos(c*du),h,r*sin(c*du)); //这就是视点的坐标
glLoadIdentity();
gluLookAt(r, 0.2, r, 0, 0, 0, 0, 1, 0); //从视点看远点,y轴方向(0,1,0)是上方向
glRotatef(30*c*du, 0, 1, 0);
glRotatef(-h, -1, 0, 0);
for (int i = 0; i < 6; ++i) // 有六个面,循环六次
{
glColor3f(colors[i][0], colors[i][1], colors[i][2]);
glBegin(GL_POLYGON);
for (int j = 0; j < 4; ++j) // 每个面有四个顶点,循环四次
glVertex3fv(vertex_list[index_list[i][j]]);
glEnd();
}
glColor3f(1, 0, 0);
for (int i = 0; i < 6; ++i) // 有六个面,循环六次
{
glBegin(GL_LINE_LOOP);
for (int j = 0; j < 4; ++j) // 每个面有四个顶点,循环四次
glVertex3fv(vertex_list[index_list[i][j]]);
glEnd();
}
glFlush();
glutSwapBuffers();
}
void Mouse(int button, int state, int x, int y) //处理鼠标点击
{
if (state == GLUT_DOWN) //第一次鼠标按下时,记录鼠标在窗口中的初始坐标
oldmx = x, oldmy = y;
}
void onMouseMove(int x, int y) //处理鼠标拖动
{
//printf("%d\n",du);
du += x - oldmx; //鼠标在窗口x轴方向上的增量加到视点绕y轴的角度上,这样就左右转了
h += (y - oldmy); //鼠标在窗口y轴方向上的改变加到视点的y坐标上,就上下转了
//if (h>1.0f) h = 1.0f; //视点y坐标作一些限制,不会使视点太奇怪
//else if (h<-1.0f) h = -1.0f;
oldmx = x, oldmy = y; //把此时的鼠标坐标作为旧值,为下一次计算增量做准备
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowPosition(100, 100);
glutInitWindowSize(400, 400);
glutCreateWindow("OpenGL");
glutDisplayFunc(display);
glutIdleFunc(display); //设置不断调用显示函数
glutMouseFunc(Mouse);
glutMotionFunc(onMouseMove);
glutMainLoop();
return 0;
}
这样就是把偏移量加到了物体上进行旋转
不过
glRotatef(30*c*du, 0, 1, 0);
glRotatef(-h, -1, 0, 0);
这显然是错的,
第二个坑出现了因为我这里希望围绕物体内部的坐标系(局部坐标系)进行旋转,这样在绕着y进行旋转之后不是对物体进行x轴旋转,而是针对世界坐标系的x轴进行了旋转,效果很难受。。
我找了很多方法都没办法搞,首先是
glPushMatrix();以及glPopMatrix();
搞了半天不对
之后我把旋转轴改到了(cos(c*du),0,sin(c*du))还是不对,因为我想的是经过y轴旋转du*c后,原来的(1,0,0)变成了这个
结果不对。。简直了
后来我又发现了一个神奇的函数
glMultMatrixf(Mc);
这个是可以利用变换的矩阵
然后根据
http://www.bubuko.com/infodetail-435954.html这篇博文写得好
计算旋转矩阵,绕y,绕(cos(c*du),0,sin(c*du))计算不对
绕y为A,绕X为B,结果A^-1BA和ABA^-1也都不对。。。。简直了。。
#include
#include
#include
#include
static const GLfloat vertex_list[][3] = {
-0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
-0.5f, 0.5f, -0.5f,
-0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
};
GLfloat colors[][3] = {
{ 1.0, 0.0, 0.0 },
{ 1.0, 1.0, 0.0 },
{ 0.0, 1.0, 0.0 },
{ 0.0, 1.0, 1.0 },
{ 1.0, 0.0, 1.0 },
{ 0.0, 0.0, 1.0 },
{ 0.0, 0.5, 0.0 },
{ 0.0, 0.5, 0.5 },
};
static const GLint index_list[][4] = {
0, 1, 2, 3,//bottem
0, 3, 7, 4,//left
2, 3, 7, 6,//front
1, 2, 6, 5,//right
0, 1, 5, 4,//back
4, 5, 6, 7//top
};
float M_PI = 3.14159265f;
static float c = M_PI / 180.0f; //弧度和角度转换参数
static int du = 90, oldmy = -1, oldmx = -1; //du是视点绕y轴的角度,opengl里默认y轴是上方向
static float r = 1.75f, h = 0.0f; //r是视点绕y轴的半径,h是视点高度即在y轴上的坐标
float id1[1][3] = {1.0,0.0,0.0};
GLfloat Mc[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(80.0f, 1.0f, 1.0f, 1000.0f);
glMatrixMode(GL_MODELVIEW);
//printf("At:%.2f %.2f %.2f\n",r*cos(c*du),h,r*sin(c*du)); //这就是视点的坐标
glLoadIdentity();
gluLookAt(r, 0.2, r, 0, 0, 0, 0, 1, 0); //从视点看远点,y轴方向(0,1,0)是上方向
//Mc[0] = pow(cos(c*du), 2) + (cos(c*h)* pow(sin(c*du) , 2));
//Mc[1] = sin(c*du)*sin(c*h);
//Mc[2] = (cos(c*du)*sin(c*du)) - (cos(c*du)*cos(c*h)*sin(c*du)) ;
//Mc[3] = 0;
//Mc[4] = -(sin(c*du)*sin(c*h)) ;
//Mc[5] = cos(c*h);
//Mc[6] = (cos(c*du)*sin(c*h)) ;
//Mc[7] = 0;
//Mc[8] = (cos(c*du)*sin(c*du)) - (cos(c*du)*cos(c*h)*sin(c*du)) ;
//Mc[9] = cos(c*du)*sin(c*h);
//Mc[10] = pow(sin(c*du), 2) + (pow(cos(c*du) , 2) * cos(c*h));
//Mc[11] = 0;
//Mc[12] = 0;
//Mc[13] = 0;
//Mc[14] = 0;
//Mc[15] = 1;
Mc[0] = cos(c*du) *cos(c*du) + (cos(c*h)*sin(c*du) *sin(c*du) );
Mc[1] = -(sin(c*du)*sin(c*h));
Mc[2] = (cos(c*du)*cos(c*h)*sin(c*du)) - (cos(c*du)*sin(c*du)) ;
Mc[3] = 0;
Mc[4] = 0 ;
Mc[5] = cos(c*h);
Mc[6] = -cos(c*du)*cos(c*du) * sin(c*h) - sin(c*du) *sin(c*du) * sin(c*h);
Mc[7] = 0;
Mc[8] = sin(c*du)*(cos(c*h) - sin(c*du) *sin(c*du) * (cos(c*h) - 1)) - cos(c*du) *cos(c*du) * sin(c*du)*(cos(c*h) - 1);
Mc[9] = -cos(c*du)*sin(c*h);
Mc[10] = cos(c*du)*(cos(c*h) - sin(c*du) *sin(c*du) * (cos(c*h) - 1)) + cos(c*du)*sin(c*du) *sin(c*du) * (cos(c*h) - 1);
Mc[11] = 0;
Mc[12] = 0;
Mc[13] = 0;
Mc[14] = 0;
Mc[15] = 1;
//Mc[0] = cos(c*du)*(cos(c*h) - cos(c*du) *cos(c*du) * (cos(c*h) - 1)) - cos(c*du)*sin(c*du)*sin(c*du) * (cos(c*h) - 1);
//Mc[1] = -sin(c*du)*sin(c*h);
//Mc[2] = -sin(c*du)*(cos(c*h) - cos(c*du)*cos(c*du) * (cos(c*h) - 1)) - cos(c*du) *cos(c*du) * sin(c*du)*(cos(c*h) - 1);
//Mc[3] = 0;
//Mc[4] = 0;
//Mc[5] = cos(c*h);
//Mc[6] = -cos(c*du) *cos(c*du) * sin(c*h) - sin(c*du) * sin(c*du) * sin(c*h);
//Mc[7] = 0;
//Mc[8] = sin(c*du)*(cos(c*h) - sin(c*du) *sin(c*du) * (cos(c*h) - 1)) - cos(c*du) *cos(c*du) * sin(c*du)*(cos(c*h) - 1);
//Mc[9] = -cos(c*du)*sin(c*h);
//Mc[10] = cos(c*du)*(cos(c*h) - sin(c*du) *sin(c*du) * (cos(c*h) - 1)) + cos(c*du)*sin(c*du)*sin(c*du) * (cos(c*h) - 1);
//Mc[11] = 0;
//Mc[12] = 0;
//Mc[13] = 0;
//Mc[14] = 0;
//Mc[15] = 1;
//glPushMatrix();
//glRotatef(du, 0, 1, 0);
//id1[0][0] = cos(-du*c)*id1[0][0]-sin(-du*c)*id1[0][2];
//id1[0][1] = id1[0][1];
//id1[0][2] = sin(-du*c)*id1[0][0] + cos(-du*c)*id1[0][2];
//glGetFloatv(GL_MODELVIEW_MATRIX, Mc);
//glPushMatrix();
//glRotatef(h, id1[0][0], id1[0][1], id1[0][2]);
//glPopMatrix();
//glPopMatrix();
glMultMatrixf(Mc);
//glRotatef(-h, -cos(-30 * c*du), 0, sin(-30 * c*du));
for (int i = 0; i < 6; ++i) // 有六个面,循环六次
{
glColor3f(colors[i][0], colors[i][1], colors[i][2]);
glBegin(GL_POLYGON);
for (int j = 0; j < 4; ++j) // 每个面有四个顶点,循环四次
glVertex3fv(vertex_list[index_list[i][j]]);
glEnd();
}
glColor3f(1, 0, 0);
for (int i = 0; i < 6; ++i) // 有六个面,循环六次
{
glBegin(GL_LINE_LOOP);
for (int j = 0; j < 4; ++j) // 每个面有四个顶点,循环四次
glVertex3fv(vertex_list[index_list[i][j]]);
glEnd();
}
glFlush();
glutSwapBuffers();
}
void Mouse(int button, int state, int x, int y) //处理鼠标点击
{
if (state == GLUT_DOWN) //第一次鼠标按下时,记录鼠标在窗口中的初始坐标
oldmx = x, oldmy = y;
}
void onMouseMove(int x, int y) //处理鼠标拖动
{
//printf("%d\n",du);
du += (x - oldmx); //鼠标在窗口x轴方向上的增量加到视点绕y轴的角度上,这样就左右转了
h += (y - oldmy); //鼠标在窗口y轴方向上的改变加到视点的y坐标上,就上下转了
//if (h>1.0f) h = 1.0f; //视点y坐标作一些限制,不会使视点太奇怪
//else if (h<-1.0f) h = -1.0f;
oldmx = x, oldmy = y; //把此时的鼠标坐标作为旧值,为下一次计算增量做准备
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowPosition(100, 100);
glutInitWindowSize(400, 400);
glutCreateWindow("OpenGL");
glutDisplayFunc(display);
glutIdleFunc(display); //设置不断调用显示函数
glutMouseFunc(Mouse);
glutMotionFunc(onMouseMove);
glutMainLoop();
return 0;
}
从上面这几段就看得出来。。各种方法我都试过了。。先放放吧。暂时用方案1里的两种方法,如果有网友可以帮助我,那真是感激不尽^^
good night^^