第三章,另一半简单讲述了opengl的工作模式,方法。
显示列表:
书中解释,可以将显示列表看作一种类型的图形文件,在需要的时候调用该文件进行绘制。我觉得,把显示列表理解成理解成“opengl的特殊代码块”更好些。我们就是在显示列表中码好田,需要时候调用即可。当然,调用时有opengl规定的方式去调用,并不像普通函数那样使用。
显示列表的使用步骤:
1 创建/命名显示列表
2添加命令
3关闭显示列表
4调用显示列表
示例代码田:
我觉得,显示列表的一个作用是提供了间接的面向对象的方法。显示列表支持内部调用,即可以在添加命令的部分中加入glCallList()函数。于是,可以定义不同的显示模块,然后拼出一盘大拌菜来。如果您有才,您可以让地里长出对象来(,,,如果你单身的话 :P)。
拾取模式:
当你鼠标在窗口中选择了某个东西,你想让opengl反应一下,那么你需要使用拾取模式。
以下是通过鼠标获取“命中数据”的关键代码。命中数据,简单的说,就是你用鼠标点到了啥。
再来看看drawObjecs()和processHits()函数,这两个都是我们自定义的函数。不是opengl的接口,我们可以替换成其他任何函数,来完成类似的功能。
从processHit中可以看出名称堆栈的大致结构:
显示列表:
书中解释,可以将显示列表看作一种类型的图形文件,在需要的时候调用该文件进行绘制。我觉得,把显示列表理解成理解成“opengl的特殊代码块”更好些。我们就是在显示列表中码好田,需要时候调用即可。当然,调用时有opengl规定的方式去调用,并不像普通函数那样使用。
显示列表的使用步骤:
1 创建/命名显示列表
2添加命令
3关闭显示列表
4调用显示列表
示例代码田:
# define LIST_NAME 1 //定义一个显示列表的名字。
glNewList(LIST_NAME, GL_COMPILE); //创建/命名显示列表。
//显示列表的“名字”是 GLuint类型。
//第二个参数mode。表示显示列表是否在创建时就显示。GL_COMPILE_AND_EXCUTE表示创建立即显示。
glPushAttrib(GL_CURRENT_BIT); //添加命令:保存当前属性?需要调查一下,这个函数和参数值。。
glColor3f(1.0, 0.0, 0.0); //添加命令:红色
glRecf(-1.0, -1.0, 1.0, 1.0); //添加命令:方形
glPopAttrib(); //添加命令:回复修改前的属性?
glEndList(); //关闭显示列表
//...
glCallList(LIST_NAME); //调用显示列表,参数是显示列表的名字。
我觉得,显示列表的一个作用是提供了间接的面向对象的方法。显示列表支持内部调用,即可以在添加命令的部分中加入glCallList()函数。于是,可以定义不同的显示模块,然后拼出一盘大拌菜来。如果您有才,您可以让地里长出对象来(,,,如果你单身的话 :P)。
拾取模式:
当你鼠标在窗口中选择了某个东西,你想让opengl反应一下,那么你需要使用拾取模式。
以下是通过鼠标获取“命中数据”的关键代码。命中数据,简单的说,就是你用鼠标点到了啥。
void mouse(int button, int state, int x, int y)
{
GLuint selectBuf[SIZE];
GLint hits;
GLint viewport[4];
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
{
glGetIntegerv (GL_VIEWPORT, viewport); //获得当前的视点
glSelectBuffer (SIZE, selectBuf); //将拾取的数据存在大小为SIZE的selectBuf中
glRenderMode(GL_SELECT); //开始拾取模式。在使用glRenderMode(GL_SELECT)前,必须先确定glSelectBuffer()
glInitNames(); //之前的selectBuf被称作名称堆栈,拾取模式需要初始化该堆栈。
glPushName(0); //拾取模式不允许在空堆栈中加载“名称”(glLoadName)。所以先push一个不会使用的名称,比如我不用0,那就push个0进去。
glMatrixMode (GL_PROJECTION); //选择GL_PROJECTION模式
glPushMatrix (); //保存当前状态
glLoadIdentity (); //单位化当前矩阵?
/* 在viewport中,建立一个以鼠标为中心,5x5像素的区域。 */
gluPickMatrix ((GLdouble) x, (GLdouble) (viewport[3] - y),
5.0, 5.0, viewport);
gluOrtho2D (-2.0, 2.0, -2.0, 2.0); //设置与初始化时相同的正交投影
drawObjects(GL_SELECT); //在5X5区域中重新绘制需要的图像。拾取需要重新绘制图像,opengl以此计算哪些对象的名称被选中。
//这是一个自定义的函数
glMatrixMode (GL_PROJECTION);
glPopMatrix (); //还原GL_PROJECT状态
glFlush(); //重绘
hits = glRenderMode (GL_RENDER); //退出拾取模式,回复渲染模式。函数返回值是命中信息的个数。所有命中信息存在selectBuf中
processHits (hits, selectBuf); //处理所有命中信息(selectBuf)。这是一个自定义的函数
glutPostRedisplay(); //如果处理命中信息时改变了绘制状态,那么当前的窗口需要重新绘制。
}
}
再来看看drawObjecs()和processHits()函数,这两个都是我们自定义的函数。不是opengl的接口,我们可以替换成其他任何函数,来完成类似的功能。
void drawObjects(GLenum mode)
{
if(mode == GL_SELECT) {
glLoadName(1); //加载名称‘1’
}
glColor3f(1.0, 0.0, 0.0);
glRectf(-0.5, -0.5, 1.0, 1.0); //名称‘1’所属的图元。
if(mode == GL_SELECT) {
glLoadName(2); //加载名称‘2’
}
glColor3f(0.0, 0.0, 1.0);
glRectf(-1.0, -1.0, 0.5, 0.5); //名称‘2’所属的图元。
}
void processHits (GLint hits, GLuint buffer[])
{
unsigned int i, j;
GLint names, *ptr;
printf ("hits = %d\n", hits); //有多少个命中
ptr = (GLint *) buffer;
for (i = 0; i < hits; i++) { /* for each hit */
names = *ptr;
ptr+=3; //书中解释是跳过名称和深度值,没有解释深度值是什么。
for (j = 0; j < names; j++) { /* 处理当前hit中的所有名称*/
if(*ptr==1) printf ("red rectangle\n");
else printf ("blue rectangle\n");
ptr++; //下一个名称
}
printf ("\n");
}
}
从processHit中可以看出名称堆栈的大致结构:
| (第一个hit。当前hit中,命中的名称N个)N | depth | depth | name1 | name2 | ...| nameN | (第二个hit。当前hit中,命中的名称N1个)N1| depth | depth | name1 | name2 | ...| nameN1 |以此类推
其中,每一个| XXX |表示一个GLuint。
最近犯懒,好几天没更新笔记,哎,体现我深刻的劣根性啊。。