首页 > 代码库 > 【OpenGL 学习笔记04】顶点数组

【OpenGL 学习笔记04】顶点数组

通过之前的学习,我们知道,如果要绘制一个几何图形,那就要不断的调用绘制函数,比如绘制一个20条边的多边形,起码要调用22条函数(包含glBegin和glEnd)。
所以OpenGL提供了一系列的顶点数组函数减少函数调用的次数来提高性能。而且使用顶点还可以避免顶点共享的冗余处理。

1.简单示例

先来回顾一下之前我们是怎么画直线的:

void drawOneLine(GLfloat x1,GLfloat y1,GLfloat x2,GLfloat y2)
{
   glBegin(GL_LINES);
   glVertex2f (x1,y1); 
   glVertex2f (x2,y2);
   glEnd();
}

对于上面的示例,我们使用顶点数组如下:

void drawLineWithArray()
{
    GLint vertices[]={25,25,
                      100,100
                     };
    glEnableClientState(GL_VERTEX_ARRAY);//启用顶点数组
    glVertexPointer(2,GL_INT,0,vertices);//指定数组数据
    glBegin(GL_LINES);
    glArrayElement(0);                   //解引用和渲染
    glArrayElement(1);
    glEnd();
}

2.使用顶点数组的三个步骤

(1)先启用顶点数组

//指定需要启动的数组(GL_VERTEX_ARRAY,GL_COLOR_ARRAY,GL_INDEX_ARRAY等8个可用数组)
void glEnableClientState(GLenum array);

(2)指定数组数据

//size表示顶点坐标数量(2,3,4),type表示数据类型,stride表示连续顶点之间字节偏移(0表示紧密相邻),pointer表示数组首地址
void glVertexPointer(GLint size,GLenum type,GLsizei stride,const GLvoid* pointer);
//其它几个数组等
void glColorPointer(GLint size,GLenum type,GLsizei stride,const GLvoid* pointer);
void glColorPointer(GLenum type,GLsizei stride,const GLvoid* pointer);
...

(3)解引用和渲染

解引用单个
//获取当前所哟启用的数组第ith个顶点数据(从0开始算)
void glArrayElement(GLint ith);
解引用多个
//mode指定要创建图元的类型(和glBegin参数相同),count为顶点数量,type为顶点数据类型,indices表示索引数组首地址
//glDrawElements的作用相当于多条glArrayElement(indices[i])
void glDrawElements(GLenum mode,GLsize count,GLenum type,const GLvoid* indices);
//相当于primcount条glDrawElements(mode,count[i],type,indices[i])语句
void glMultiDrawElements(GLenum mode,GLsize* count,GLenum type,const GLvoid** indices,GLsizei primcount);
//相当于有范围的glDrawElements,范围为[start,end]
void glDrawRangeElements(GLenum mode,GLuint start,GLuint end,GLsize count,GLenum type,const GLvoid* indices);

//创建一个图元序列,从每个被启用的数组,范围为[first,first + count - 1]
void glDrawArrays(GLenum mode,GLint first,GLsizei count);
//相当于primcount条glDrawArrays(mode,first[i],count[i])
void glMultiDrawArrays(GLenum mode,GLint* first,GLsizei* count,GLsizei primcount);

3.使用顶点数组的一个示例

我们现在有一个顶点数组和一个颜色数组,我们使用它们来两条画线:
#include <GL/glut.h>  

void display(void)  
{  

  glClear (GL_COLOR_BUFFER_BIT);  //清除颜色缓冲区  

  static GLint vertices[]={25,25,
    100,100,
    120,120,
    200,200};
  static GLfloat colors[]={1.0, 0.0, 0.0,
    1.0, 0.0, 0.0,
    0.0, 1.0, 0.0,
    0.0, 1.0, 0.0};
  GLubyte index[]= {0,1,2,3} ;

  glEnableClientState(GL_VERTEX_ARRAY);
  glEnableClientState(GL_COLOR_ARRAY);
  glVertexPointer(2,GL_INT,0,vertices);
  glColorPointer(3,GL_FLOAT,0,colors);
  
  glDrawElements(GL_LINES,4,GL_UNSIGNED_BYTE,index);//这条语句等价于如下注释的语句
//  glBegin(GL_LINES);
//  glArrayElement(0);
//  glArrayElement(1);
//  glArrayElement(2);
//  glArrayElement(3);
//  glEnd();
  glFlush ();  
}  

void reshape (int w, int h)
{
  glViewport (0, 0, (GLsizei) w, (GLsizei) h);//调整绘图的像素矩阵大小
  glMatrixMode (GL_PROJECTION);       //将当前矩阵指定为投影矩阵  
  glLoadIdentity ();              //把当前矩阵设为单位矩阵   
  gluOrtho2D (0.0, (GLdouble) w, 0.0, (GLdouble) h);
}

void init (void)   
{  
  glClearColor (0.0, 0.0, 0.0, 0.0);     //设置窗口将被清除成黑色  
  glMatrixMode(GL_PROJECTION);           //将当前矩阵指定为投影矩阵  
  glLoadIdentity();                      //把当前矩阵设为单位矩阵  
  glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0);//指定绘制图像时使用的坐标系统 glOrtho(Xmin,Xmax,Ymin,Ymax,Zmin,Zmax);     
}  


int main(int argc, char** argv)  
{  
  glutInit(&argc, argv);     
  glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);     
  glutInitWindowSize (250, 250);   
  glutInitWindowPosition (100, 100);  
  glutCreateWindow ("hello");  
  init ();  
  glutDisplayFunc(display);   
  glutReshapeFunc(reshape); 

  glutMainLoop();  
  return 0;     
}  
效果图:


4.顶点数组跨距

来简单回顾一下上面那个例子:

void display()
{
    GLint vertices[]={25,25,
                      100,100,
                      120,120,
                      200,200};
    GLfloat colors[]={1.0, 0.0, 0.0,
                      1.0, 0.0, 0.0,
                      0.0, 1.0, 0.0,
                      0.0, 1.0, 0.0};

    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);
    glVertexPointer(2,GL_INT,0,vertices); //从vertices[0]开始获取,每次获取2个,下一次从vertices[2]开始获取
    glColorPointer(3,GL_FLOAT,0,colors);

    glBegin(GL_LINES);
    glArrayElement(0);
    glArrayElement(1);
    glArrayElement(2);
    glArrayElement(3);
    glEnd();
}

现在我们合并成一个数组,stride参数指定如何跨距数据(由于跨距需要计算数据类型,所以数组的数据类型需要相同),具体如下:

void display()
{
    GLfloat data[]=  {1.0, 0.0, 0.0,25.0,25.0,
                  1.0, 0.0, 0.0,100.0,100.0,
                  0.0, 1.0, 0.0,120.0,120.0,
                  0.0, 1.0, 0.0,200.0,200.0};//合成一个数组

    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);
    glColorPointer(3,GL_FLOAT,5*sizeof(GLfloat),&data[0]);//从data[0]开始获取,每次获取3个,下一次从data[0+5]获取3个...
    glVertexPointer(2,GL_FLOAT,5*sizeof(GLfloat),&data[3]);//

    glBegin(GL_LINES);
    glArrayElement(0);
    glArrayElement(1);
    glArrayElement(2);
    glArrayElement(3);
    glEnd();
}

5.混合数组

glInterleavedArrays的作用相当于:步骤1启用数组 + 步骤2指定数组数据。

//初始化全部的8个数组,并禁用format没有指定的数组。stride为连续顶点之间字节偏移,pointer为数组首地址
void glInterleavedArrays(GLenum format,GLsizei stride,const GLvoid* pointer);


我们用混合数组再来改造一下上面的例子:

void display()
{
    GLfloat data[]=  {1.0, 0.0, 0.0,25.0,25.0,0.0,
                      1.0, 0.0, 0.0,100.0,100.0,0.0,
                      0.0, 1.0, 0.0,120.0,120.0,0.0,
                      0.0, 1.0, 0.0,200.0,200.0,0.0};
    
    
    glInterleavedArrays(GL_C3F_V3F,0,data); //启用颜色数组和顶点数组 并指定数据
    glDrawArrays(GL_LINES,0,4);             //相当于循环调用4次glArrayElement(i),i为0到3
    
}
现在去掉颜色的数组的话,代码如下:

void drawTwoLineWithArray2()
{
    GLfloat data[]=  {25.0,25.0,
                  100.0,100.0,
                  120.0,120.0,
                  200.0,200.0};
    glInterleavedArrays(GL_V2F,0,data);
    glDrawArrays(GL_LINES,0,4);
}

一目了然啊有木有...