OpenGL实验(二)桌子的坐标系变换 矩阵 视图
在图形学中通常使用4X4矩阵来表示三维物体的空间几何变换
要求
最左边的桌子循环上移(即匀速上移到一定位置后回到原点继续匀速上移),中间的桌子不断旋转(即绕自身中间轴旋转),最右边的桌子循环缩小(即不断缩小到一定大小后回归原来大小继续缩小)。
尺寸要求:
参考:
绘制桌子
其实也不太清楚怎么画算比较好……因为是 一个矩形一个矩形画的 所以这一步感觉还是蛮机械的
void Draw_Leg() // This function draws a triangle with RGB colors
{
//四面
glBegin(GL_QUADS);
glVertex3f(-0.5f, 0.5f, 3.0f);
glVertex3f(0.5f, 0.5f, 3.0f);
glVertex3f(0.5f, 0.5f, 0.0f);
glVertex3f(-0.5f, 0.5f, 0.0f);
//...
//底边
glVertex3f(0.5f, 0.5f, 0.0f);
glVertex3f(0.5f, -0.5f, 0.0f);
glVertex3f(-0.5f, -0.5f, 0.0f);
glVertex3f(-0.5f, 0.5f, 0.0f);
glEnd();
}
void Draw_Table() // This function draws a triangle with RGB colors
{
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); //空心
//glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); //实心
glColor3f(1.0f, 1.0f, 1.0f); //选择颜色
//上下
glBegin(GL_QUADS);
glVertex3f(2.5f, 2.0f, 4.0f);
glVertex3f(2.5f, -2.0f, 4.0f);
glVertex3f(-2.5f, -2.0f, 4.0f);
glVertex3f(-2.5f, 2.0f, 4.0f);
glVertex3f(2.5f, 2.0f, 3.0f);
glVertex3f(2.5f, -2.0f, 3.0f);
glVertex3f(-2.5f, -2.0f, 3.0f);
glVertex3f(-2.5f, 2.0f, 3.0f);
//四面
glVertex3f(2.5f, 2.0f, 4.0f);
glVertex3f(2.5f, -2.0f, 4.0f);
glVertex3f(2.5f, -2.0f, 3.0f);
glVertex3f(2.5f, 2.0f, 3.0f);
glEnd();
//...
//画桌子腿
glPushMatrix();
glTranslatef(1.5f, 1.0f, 0.0f);
Draw_Leg();
//.....
}
子窗口-视点
http://www.cnblogs.com/live41/p/3395902.html
很大一部分是按着上面那个链接改的(所以这次的作业干的事情就是拼拼凑凑)还有很多要学的哇
在双缓存的情况下,对一个窗口的颜色进行初始化时,glClear一定要调用后SwapBuffers()
感觉子窗口是相对独立的概念。如果我们希望的话,可以对某个单独的子窗口进行操作。然后建立在子窗口上的一些操作就需要多次的初始化。这里的Init操作包括各种键盘鼠标事件。
//创建的代码
//sub windows
subWindow1 = glutCreateSubWindow(mainWindow, border, border, w - 2 * border, h / 2 - border * 3 / 2);
glutDisplayFunc(renderScenesw1);
init();
subWindow2 = glutCreateSubWindow(mainWindow, border, (h + border) / 2, w / 2 - border * 3 / 2, h / 2 - border * 3 / 2);
glutDisplayFunc(renderScenesw2);
init();
subWindow3 = glutCreateSubWindow(mainWindow, (w + border) / 2, (h + border) / 2, w / 2 - border * 3 / 2, h / 2 - border * 3 / 2);
glutDisplayFunc(renderScenesw3);
init();
对窗口的操作默认是对当前选中的窗口进行的。所以在有子窗口的情况下,需要通过比如glutSetWindow(subWindow1)来确定当前选中的窗口。这里我的subWindow1等变量都设置为全局变量(居然是int类型233)
对于每一个窗口都要进行glutPostRedisplay()标记它需要进行重绘(如果对mainWindow不进行的话在缩放的时候可能会出现问题)
glutSwapBuffers()用于显示,如果设置的是单缓存则没作用
绘制的过程中(至少在这个例子里)相当于对于每一个子窗口对图形进行重复绘制,通过设置不同的视点使得显示有所区别。(但是总感觉大概可以对于同一个图形设置,比如说三个不同的视点?这样感觉起来应该会节省存储的空间,毕竟每点的位置都是需要存放的)
还有例子用到的函数
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
虽然我不是很清楚它是怎么工作的但在这里这两行没啥必要,感觉更接近于模仿真实场景(比如这个GL_FOG:雾化效果 例如距离越远越模糊)(看起来蛮好玩的)
有子窗口的情况下reshape更加繁琐(计算复杂),不过应该没什么需要调整的
键盘事件-平移
这一块以及下两块主要参考的是:http://blog.csdn.net/bill_ming/article/details/7662809
传说中的茶壶= =
键盘事件主要函数是
void glutKeyboardFunc(void (*func)(unsigned char key, int x, int y));
void glutSpecialFunc(void (*func)(int key, int x, int y));
前者是有ASCII码的键盘输入,后者处理没有ASCII的特殊输入,比如上下左右、F1等,格式为GLUT_KEY_*,具体可以参考https://www.opengl.org/resources/libraries/glut/spec3/node54.html
接收到对应的事件调整全局变量的值。
和参考相似并且比较容易理解,在这里不赘述了。
鼠标事件-旋转
鼠标主要用到的两个函数:
void glutMouseFunc(void (*func)(int button, int state,
int x, int y));
button :
- GLUT_LEFT_BUTTON
- GLUT_MIDDLE_BUTTON
GLUT_RIGHT_BUTTON
state:
- GLUT_UP
GLUT_DOWN
所以说这个函数不会记录鼠标是不是处于被按下状态。
如果我们需要设置(针对某个特定的键,比如左键)拖动,则需要另外设置变量来记录(左键)是否处于按下状态,经历GLUT_DOWN则改为TRUE,经历GLUT_UP则改为FALSE结合下面一个函数就可以对拖动进行处理:
void glutMotionFunc(void (*func)(int x, int y)); void glutPassiveMotionFunc(void (*func)(int x, int y));
前者当有一或多个按钮按下,并且在窗口内移动的时候被调用。后者在没有按钮按下,并且进行移动的时候被调用。
基本思路是,设置全局量记录鼠标的位置,当左键按下时记录(按下)以及更新鼠标的xy坐标记录。
在鼠标进行拖动的时候,记录xy坐标的该变量,以此作为旋转角度的参考。
(代码包含下面一块延时的内容:右键的时候开启循环)
调用:
glutMouseFunc(mouse);
glutMotionFunc(motion);
void mouse(int button, int state, int x, int y)
{
if (button == GLUT_LEFT_BUTTON)
{
if (state == GLUT_DOWN)
{
mouseisdown = true;
loopr = false;
tx = x;
ty = y;
}
else
{
mouseisdown = false;
}
}
if (button == GLUT_RIGHT_BUTTON)
if (state == GLUT_DOWN)
{
loopr = true;
glutTimerFunc(200, timer, 0);
}
}
void motion(int x, int y)
{
if (mouseisdown == true)
{
rz += x - tx;
rx += y - ty;
//rx rz 为传给坐标变换的转角度
tx = x;
ty = y;
glutPostRedisplay();
}
}
并看不到鼠标QwQ
自发事件-延时
这一块最主要用到的函数是
void glutTimerFunc(unsigned int msecs, void (*func)(int value), value);
它的性质类似于定时器,在多少时间后改变一个量。
然后比如茶壶的不断旋转的操作是通过定时器自身的调用。此外有一个全局bool量来控制这个循环是否终止。
void timer(int p)
{
//具体操作
if (loopr)
{
glutTimerFunc(200, timer, 0);
}
}
这个性质还是蛮神奇的,这意味着,要是有多个这种定时器,那个操作的执行次数会越来越快。
比如下图 我在这个过程中多次的单击右键
接下来说说这一块的实现
这里的变换是简单的在前两块已经有的基础上增加了缩放。就是说在绘制前,对坐标系进行调整。
其中time是一个由(定时器)调整的值,在这里我设置它的范围是0~29变化
这里通过接受键盘1 2 3来设置变换方式(旋转/平移/缩放)
void drawMain()
{
glPushMatrix();
if (loopr)
{
switch (state)
{
case 1:
mz = 0.1 * time;
state = 1;
break;
case 2:
rx = 12 * time;
break;
case 3:
glScalef(1 + 0.01*time, 1 + 0.01*time, 1 + 0.01*time);
break;
}
}
glTranslatef(mx, 0.0f, mz); // 平移
//旋转
glRotatef(rx, 1, 0, 0);
glRotatef(ry, 0, 1, 0);
glRotatef(rz, 0, 0, 1);
Draw_Table(); // Draw table
glPopMatrix();
}
其他
初始化的时候有一个函数是
glutIgnoreKeyRepeat( c_int(ignore) );
意思//确认是否忽略自动的连续击键,也就是一直按着一个键的时候,比如向上,会处理成一次事件还是连续事件。
代码再开一篇附吧好像太长了..