您的位置:首页 > 运维架构

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

2016-05-16 15:36 483 查看












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

顶点数组

观察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)。

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

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

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

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 100

float whRatio;

float fTranslate;
float fRotate = 156.5f;
float fScale     = 1.0f;    // set inital scale value to 1.0f

bool 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; //返回显示列表编号
};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: