[OpenGL] 兔子与顶点数组、拾取

来源:互联网 发布:java图形接口 编辑:程序博客网 时间:2024/05/23 22:58

       普通模式                                                  顶点数组模式                                           显示列表模式


顶点数组

 

        观察bunny的绘制,发现它是由法线向量和顶点信息构成的,这两个信息分别由normals数组和vertexs数组维护,它们存储了bunny的具体信息。

        此外,还有一个数组face_indices,它是作为索引数组存在的,里面存储的是下标信息,用于指向normals数组和vertex数组的信息。

        在顶点数组中,我们只需告诉OpenGL这三个数组分别是什么,就可以完成快速绘制,减少函数调用消耗,让数据一次性传输,而不是一个个传输,而且数据可以是非静态的。

        使用顶点数组的时候,我们需要先开启特定的顶点数组,然后再指定具体的顶点坐标与下标索引。

 

        开启顶点数组:

 

        glEnableClientState(GL_VERTEX_ARRAY);//启用顶点坐标数组

        glEnableClientState(GL_NORMAL_ARRAY);//启用法线向量数组

 

        指定顶点数组:

 

        glVertexPointer(3,GL_FLOAT, 0, vertices);//指定顶点

        glNormalPointer(GL_FLOAT, 0, normals);//指定法向量

 

          指定下标数组:


         glDrawElements(shape_type,count , array_type, array);

 

 显示列表

 

        显示列表的使用比较简单,生成显示列表并分配后,把需要加载到显示列表的物体代码写上即可。

         lid = glGenLists(1)

        glNewList(lid, GL_COMPILE);

        ……

        glEndList();

 

 

拾取

 

        拾取也就是判断是否点中某个物体的方法。它和鼠标的操作相关,所以它是基于鼠标回调事件的,在此之前,我们需要先指定我们需要拾取哪些物体,以及它们的编号。

        指定拾取物体在绘制物体时完成,首先,需要进行初始化:

 

        glInitNames();//初始化名字

 

        具体指定的代码如下:

 

        glPushName(Name1);

        draw1();

        glPopName();

        glPushName(Name2);

        draw2();

        glPopName();

 

        也可以用glLoadName(Name),它等价于glPopName()glLoadName(Name)

        glPushNameglPushMatrix一样,是可以嵌套调用的,在这次实验我们没有使用这个功能。

        在渲染模式下,以上函数调用都会被忽略,所以可以实现两种模式共用一个函数而不发生冲突。

 

        然后,我们关注回调事件,当我们鼠标左键点击了屏幕时,触发鼠标回调事件,开始我们需要指定选中的结果存储到哪个数组,并且需要给出数组大小:

        glSelectBuffer(BUFSIZE, selectBuf);

 

        之后,开启选择模式

        glRenderMode(GL_SELECT);

 

        在选择模式下,我们先把矩阵模式切换为投影,然后指定投影方式,设置选择矩阵,这时候再进行重绘,重绘过程中又要把矩阵模式切换为模型,之后再切换为投影。这时,OpenGL会返回选中物体的下标以及深度信息等。

 

        gluPickMatrix((GLdouble)x,(GLdouble)(viewport[3] -y), 1, 1, viewport);//设置选择矩阵

 

        由于为3D图形,所以一次点击可能点中多个物体,我们需要按照深度排序,然后得到深度最小的物体作为当前选中物体,对于选中物体,把物体的颜色数组进行修改,这时就完成了拾取物体并修改颜色的功能。

        对于颜色,我们采取了一开始产生随机数生成。

 

实验数据记录和处理 

 

  

模式

FPS

Naive

45

Vertex array

190

Display list

65

 

   可以看到,显示列表略胜于普通模式,顶点数组明显优于前两者。



// glutEx1.cpp : 定义控制台应用程序的入口点。//#include <stdlib.h>#include <stdio.h>#include <string.h>#include <time.h>#include "glut.h"#define BUFSIZE 100float whRatio;float fTranslate;float fRotate = 156.5f;float fScale     = 1.0f;    // set inital scale value to 1.0fbool bPersp = true;bool bAnim = false;bool bWire = false;bool isTableWhite = true;bool isBunnyWhite[19];int wHeight = 0;int wWidth = 0;GLfloat bunny_diffuse[19][3];GLfloat table_diffuse[3];GLint dl = 0;GLenum mode = GL_RENDER;GLuint selectBuf[BUFSIZE];void Draw_Leg();int  drawMode = 0;extern void drawNaive();extern void drawVA();extern GLint Gen3DObjectList();void drawDL() {    glCallList(dl);}void drawBunny(){    glRotatef(90, 1, 0, 0);    glScalef(3, 3, 3);    if (drawMode == 0)        drawNaive();    else if (drawMode == 1)        drawVA();    else        drawDL();}void Draw_Desk();void setColor(int index){    if (isBunnyWhite[index] == false) {                glMaterialfv(GL_FRONT, GL_DIFFUSE, bunny_diffuse[index]);//设置多边形正面漫反射属性      }    else {        GLfloat mat_diffuse[] = { 1.0f,1.0f,1.0f };        glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);//设置多边形正面漫反射属性     }}void Draw_Triangle() // This function draws a triangle with RGB colors{    glInitNames();//初始化名字    glPushName(0);//初始化名字栈    glLoadName(1);//加载名字    setColor(1);//设置颜色    glPushMatrix();    glTranslatef(-1, -1, 5.5);    drawBunny();    glPopMatrix();    glLoadName(2);//加载名字    setColor(2);//设置颜色    glPushMatrix();    glTranslatef(0, -1, 5.5);    drawBunny();    glPopMatrix();    glLoadName(3);//加载名字    setColor(3);//设置颜色    glPushMatrix();    glTranslatef(1, -1, 5.5);    drawBunny();    glPopMatrix();    glLoadName(4);//加载名字    setColor(4);//设置颜色    glPushMatrix();    glTranslatef(-1, 1, 5.5);    drawBunny();    glPopMatrix();    glLoadName(5);//加载名字    setColor(5);//设置颜色    glPushMatrix();    glTranslatef(0, 1, 5.5);    drawBunny();    glPopMatrix();    glLoadName(6);//加载名字    setColor(6);//设置颜色    glPushMatrix();    glTranslatef(1, 1, 5.5);    drawBunny();    glPopMatrix();    glLoadName(7);//加载名字    setColor(7);//设置颜色    glPushMatrix();    glTranslatef(-1, 0, 5.5);    drawBunny();    glPopMatrix();    glLoadName(8);//加载名字    setColor(8);//设置颜色    glPushMatrix();    glTranslatef(0, 0, 5.5);    drawBunny();    glPopMatrix();    glLoadName(9);//加载名字    setColor(9);//设置颜色    glPushMatrix();    glTranslatef(1, 0, 5.5);    drawBunny();    glPopMatrix();    glLoadName(10);//加载名字    setColor(10);//设置颜色    glPushMatrix();    glTranslatef(-1, -1, 7.5);    drawBunny();    glPopMatrix();    glLoadName(11);//加载名字    setColor(11);//设置颜色    glPushMatrix();    glTranslatef(0, -1, 7.5);    drawBunny();    glPopMatrix();    glLoadName(12);//加载名字    setColor(12);//设置颜色    glPushMatrix();    glTranslatef(1, -1, 7.5);    drawBunny();    glPopMatrix();    glLoadName(13);//加载名字    setColor(13);//设置颜色    glPushMatrix();    glTranslatef(-1, 1, 7.5);    drawBunny();    glPopMatrix();    glLoadName(14);//加载名字    setColor(14);//设置颜色    glPushMatrix();    glTranslatef(0, 1, 7.5);    drawBunny();    glPopMatrix();    glLoadName(15);//加载名字    setColor(15);//设置颜色    glPushMatrix();    glTranslatef(1, 1, 7.5);    drawBunny();    glPopMatrix();    glLoadName(16);//加载名字    setColor(16);//设置颜色    glPushMatrix();    glTranslatef(-1, 0, 7.5);    drawBunny();    glPopMatrix();    glLoadName(17);//加载名字    setColor(17);//设置颜色    glPushMatrix();    glTranslatef(0, 0, 7.5);    drawBunny();    glPopMatrix();    glLoadName(18);//加载名字    setColor(18);//设置颜色    glPushMatrix();    glTranslatef(1, 0, 7.5);    drawBunny();    glPopMatrix();    glLoadName(19);//加载名字    Draw_Desk();    glPopName();}void Draw_Desk(){    if (isTableWhite == true) {        GLfloat mat_diffuse[] = { 1.0f,1.0f,1.0f };        glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);//设置多边形正面漫反射属性      }    else {        glMaterialfv(GL_FRONT, GL_DIFFUSE, table_diffuse);//设置多边形正面漫反射属性     }    glPushMatrix();    glTranslatef(0, 0, 3.5);    glScalef(5, 4, 1);    glutSolidCube(1.0);    glPopMatrix();    glPushMatrix();    glTranslatef(1.5, 1, 1.5);    Draw_Leg();    glPopMatrix();    glPushMatrix();    glTranslatef(-1.5, 1, 1.5);    Draw_Leg();    glPopMatrix();        glPushMatrix();    glTranslatef(1.5, -1, 1.5);    Draw_Leg();    glPopMatrix();        glPushMatrix();    glTranslatef(-1.5, -1, 1.5);    Draw_Leg();    glPopMatrix();}void Draw_Leg(){    glScalef(1, 1, 3);    glutSolidCube(1.0);}void updateView(int width, int height){    glViewport(0, 0, width, height);//设置视窗大小        glMatrixMode(GL_PROJECTION);//设置矩阵模式为投影         glLoadIdentity();   //初始化矩阵为单位矩阵            whRatio = (GLfloat)width / (GLfloat)height;  //设置显示比例          if (bPersp) {        gluPerspective(45.0f, whRatio, 0.1f, 100.0f); //透视投影                                                            //glFrustum(-3, 3, -3, 3, 3,100);        }    else {        glOrtho(-3, 3, -3, 3, -100, 100);  //正投影        }    glMatrixMode(GL_MODELVIEW);  //设置矩阵模式为模型    }void reshape(int width, int height){    if (height == 0)      //如果高度为0        {        height = 1;   //让高度为1(避免出现分母为0的现象)        }    wHeight = height;    wWidth = width;    updateView(wHeight, wWidth); //更新视角    }void idle(){    glutPostRedisplay();}float center[] = {0, -0.8, -6};float eye[] = {0, 1.2, 2};void redraw();//鼠标点击事件void mouse(int mouse, int status, int x, int y){    //左键点击 鼠标按下状态时    if (mouse == GLUT_LEFT_BUTTON && status == GLUT_DOWN) {        //存储视角        GLint viewport[4];                glSelectBuffer(BUFSIZE, selectBuf);//选择返回数据的数组        glGetIntegerv(GL_VIEWPORT, viewport);//得到视角        glRenderMode(GL_SELECT);//进入选择模式                glMatrixMode(GL_PROJECTION);//设置矩阵模式为投影        glPushMatrix();//矩阵入栈        glLoadIdentity();//初始化为单位矩阵        gluPickMatrix((GLdouble)x,            (GLdouble)(viewport[3] - y), 1, 1, viewport);//设置选择矩阵        if (bPersp) {            gluPerspective(45.0f, whRatio, 0.1f, 100.0f); //透视投影                                                                //glFrustum(-3, 3, -3, 3, 3,100);            }        else {            glOrtho(-3, 3, -3, 3, -100, 100);  //正投影            }        redraw(); //重绘        int hits = glRenderMode(GL_RENDER); //进入渲染模式,得到选中物体个数        if (hits >= 1) //如果选中物体个数大于1        {            int choose = selectBuf[3]; //得到选中物体名字            int depth = selectBuf[1]; //得到选中物体深度            printf("2 = %d\n", selectBuf[2]);            printf("4 = %d\n", selectBuf[4]);            for (int i = 0; i < hits; i++)            {                if (selectBuf[i * 4 + 1] < (GLuint)depth)//获取深度最小的物体(selectBuff是按照ID从小到大排列的)                {                    choose = selectBuf[i * 4 + 3];                    depth = selectBuf[i * 4 + 1];                }            }            //更改选中物体的颜色            if (choose >= 1 && choose <= 18) {                isBunnyWhite[choose] = !isBunnyWhite[choose];            }            else if (choose == 19) {                isTableWhite = !isTableWhite;            }        }        //设置矩阵模式为投影        glMatrixMode(GL_PROJECTION);        glPopMatrix();//抛出矩阵        //设置矩阵模式为模型        glMatrixMode(GL_MODELVIEW);    }}void key(unsigned char k, int x, int y){    switch(k)    {    case 27:    case 'q': {exit(0); break; } //退出    case 'p': {bPersp = !bPersp; break; } //切换投影模式    case ' ': {bAnim = !bAnim; break;} //旋转    case 'o': {bWire = !bWire; break;} //切换线、面显示    case '0': {drawMode++; drawMode %= 3; break;}//切换渲染方式    case 'a': { //左移        eye[0] += 0.2f;        center[0] += 0.2f;        break;              }    case 'd': { //右移        eye[0] -= 0.2f;        center[0] -= 0.2f;        break;              }    case 'w': { //上移        eye[1] -= 0.2f;        center[1] -= 0.2f;        break;              }    case 's': {  //下移        eye[1] += 0.2f;        center[1] += 0.2f;        break;              }    case 'z': { //前移        eye[2] -= 0.2f;        center[2] -= 0.2f;        break;              }    case 'c': {  //后移        eye[2] += 0.2f;        center[2] += 0.2f;        break;              }    }    updateView(wHeight, wWidth); //更新视角}void getFPS(){    static int frame = 0, time, timebase = 0;    static char buffer[256];//字符串缓冲区      char mode[64];//模式      if (drawMode == 0) //普通        strcpy(mode, "naive");    else if (drawMode == 1) //顶点数组        strcpy(mode, "vertex array");    else //显示列表        strcpy(mode, "display list");    frame++;    time=glutGet(GLUT_ELAPSED_TIME);    //返回两次调用glutGet(GLUT_ELAPSED_TIME)的时间间隔,单位为毫秒    if (time - timebase > 1000) {//时间间隔差大于1000ms时          sprintf(buffer,"FPS:%4.2f %s",            frame*1000.0/(time-timebase), mode);//写入buffer中          timebase = time; //上一次的时间间隔                  frame = 0;    }    glutSetWindowTitle(buffer);//设置窗口标题}void redraw(){    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//清除颜色和深度缓存      glMatrixMode(GL_MODELVIEW);    glLoadIdentity();    //初始化矩阵为单位矩阵      gluLookAt(eye[0], eye[1], eye[2],        center[0], center[1], center[2],        0, 1, 0);                // 场景(0,0,0)的视点中心 (0,5,50),Y轴向上    if (bWire) {        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);        //设置多边形绘制模式:正反面,线型    }    else {        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);        //设置多边形绘制模式:正反面,填充       }    glEnable(GL_DEPTH_TEST);//开启深度测试          glEnable(GL_LIGHTING);  //开启光照模式      GLfloat white[] = { 1.0, 1.0, 1.0, 1.0 };//白色    GLfloat light_pos[] = {5,5,5,1};//光源位置    glLightfv(GL_LIGHT0, GL_POSITION, light_pos);//光源位置      glLightfv(GL_LIGHT0, GL_AMBIENT, white);//环境光白色       glEnable(GL_LIGHT0);//开启第0号光源         glRotatef(fRotate, 0, 1.0f, 0);    //旋转      glRotatef(-90, 1, 0, 0);    glScalef(0.2, 0.2, 0.2);//缩放      Draw_Triangle();//绘制场景      if (bAnim) fRotate += 0.5f;//旋转因子改变      getFPS();//得到fps    glutSwapBuffers();//交换缓冲区}void generateColor(){    for (int i = 1; i <= 18; i++) {        //随机生成兔子颜色        GLfloat x = (float)(rand() % 1001) * 0.001f;        GLfloat y = (float)(rand() % 1001) * 0.001f;        GLfloat z = (float)(rand() % 1001) * 0.001f;        bunny_diffuse[i][0] = x;        bunny_diffuse[i][1] = y;        bunny_diffuse[i][2] = z;    }    //随机生成桌子颜色    GLfloat x = (float)(rand() % 1001) * 0.001f;    GLfloat y = (float)(rand() % 1001) * 0.001f;    GLfloat z = (float)(rand() % 1001) * 0.001f;    table_diffuse[0] = x;    table_diffuse[1] = y;    table_diffuse[2] = z;}int main(int argc, char *argv[]){    srand(unsigned(time(nullptr)));    glutInit(&argc, argv);//对glut的初始化           glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);    //初始化显示模式:RGB颜色模型,深度测试,双缓冲             glutInitWindowSize(480, 480);//设置窗口大小           int windowHandle = glutCreateWindow("Simple GLUT App");//设置窗口标题                 glutDisplayFunc(redraw); //注册绘制回调函数           glutReshapeFunc(reshape);   //注册重绘回调函数           glutKeyboardFunc(key); //注册按键回调函数         glutIdleFunc(idle);//注册全局回调函数:空闲时调用         glutMouseFunc(mouse); //注册鼠标回调函数      memset(isBunnyWhite, true, sizeof(isBunnyWhite));//初始化兔子颜色数组    generateColor();//随机生成兔子和桌子的颜色    dl = Gen3DObjectList();//生成显示列表    glutMainLoop();  // glut事件处理循环         return 0;}

bunny.cpp

#ifdef _WIN32#include <windows.h>#endif#include <GL/gl.h>#include <GL/glu.h>#include <stdio.h>#pragma warning(disable: 4305)// 8146 Verticies// 8146 Normals// 16301 Triangles//数组里的数据非常害怕,它们吓得躲了起来short face_indicies[16301][3];GLfloat normals [8146][3];GLfloat vertices [8146][3];#define STR2(x)        #x#define STR(x)        STR2(x)#define MSG(desc)        message(__FILE__ "(" STR(__LINE__) ") : ------- " desc " -------")void drawVA(){    glEnableClientState(GL_VERTEX_ARRAY);//启用顶点坐标数组    glEnableClientState(GL_NORMAL_ARRAY);//启用法线向量数组    glVertexPointer(3, GL_FLOAT, 0, vertices);            glNormalPointer(GL_FLOAT, 0, normals);    glDrawElements(GL_TRIANGLES, sizeof(face_indicies) / sizeof(face_indicies[0])*3, GL_UNSIGNED_SHORT, face_indicies);}void drawNaive(){    glBegin (GL_TRIANGLES);      for(int i=0;i<(sizeof(face_indicies)/sizeof(face_indicies[0]));i++)      {          for(int j=0;j<3;j++)          {           int idx=face_indicies[i][j];           glNormal3fv(&normals[idx][0]);           glVertex3fv(&vertices[idx][0]);         }       }    glEnd ();}GLint Gen3DObjectList(){    GLint lid = glGenLists(1); //生成一个空的显示列表      glNewList(lid, GL_COMPILE);  // 用于创建和替换一个显示列表函数原型                                   // 指定显示列表的名称,编译模式:只编译      glBegin(GL_TRIANGLES);    for (int i = 0; i<(sizeof(face_indicies) / sizeof(face_indicies[0])); i++)    {        for (int j = 0; j<3; j++)        {            int idx = face_indicies[i][j];            glNormal3fv(&normals[idx][0]);            glVertex3fv(&vertices[idx][0]);        }    }    glEnd();                               glEndList();    return lid; //返回显示列表编号};



0 0