OpenGL学习06_顶点数组VertexArray

什么是顶点数组?

OpenGL提供了一些顶点数组函数,允许只用少数几个数组指定大量的与顶点相关的数据,并用少量函数调用(与顶点数组的数量相仿)访问这些数据。使用顶点数组函数,一个拥有20条边的多边形的20个顶点可以放在1个数组中,并且只通过1个函数进行调用。如果每个顶点还有一条法线向量,所有20条法线向量可以放在另一个数组中,也可以只通过1个函数进行调用。把数据放在顶点数组中可以提高应用程序的性能。使用顶点数组可以减少函数调用的次数,从而提高性能。另外,使用顶点数组还可以避免共享顶点的冗余处理

使用顶点数组对几何图形进行渲染需要3个步骤:

1) 激活(启用)最多可达8个数组,每个数组用于存储不同类型的数据:顶点坐标、表面法线、RGBA颜色、辅助颜色、颜色索引、雾坐标、纹理坐标以及多边形的边界标志。

void glEnableClientState(GLenum array)
指定了需要启用的数组。array参数可以使用下面这些符号常量:
GL_VERTEX_ARRAY、GL_COLOR_ARRAY、GL_SECONDARY_COLOR_ARRAY、GL_INDEX_ARRAY、GL_NORMAL_ARRAY、GL_FOG_COORDINATE_ARRAY、GL_TEXTURE_COORD_ ARRAY和GL_EDGE_FLAG_ARRAY。
开启和关闭顶点数组的用法如下:
//启用顶点数组
glEnableClientState(GL_VERTEX_ARRAY);
//关闭顶点数组
glDisableClientState(GL_VERTEX_ARRAY);

2) 把数据放入数组中。这些数组是通过它们的内存位置的地址(即指针)进行访问的。在客户-服务器模型中,这些数组存储在客户机的地址空间中,除非选择使用缓冲区对象,这时候,数组存储在服务器内存中。

可以通过一种简单的方法,用一条命令指定客户空间中的一个数组。共有8个不同的函数可以用来指定数组,每个函数用于指定一个不同类型的数组。另外,还有一个函数可以一次指定客户空间中的几个数组,它们均来源于一个混合数组。

void glVertexPointer(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);

指定了需要访问的空间坐标数据。pointer是数组包含的第一个顶点的第一个坐标的内存地址。type指定了数组中每个坐标的数据类型(GL_SHORT、GL_INT、GL_FLOAT或GL_DOUBLE)。size是每个顶点的坐标数量,它必须是2、3或4。stride是连续顶点之间的字节偏移量。如果stride是0,数组中的顶点便是紧密相邻的。
为了访问其他几个数组,可以使用下面这些类似的函数:

void glColorPointer(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
void glSecondaryColorPointer(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
void glIndexPointer(GLenum type, GLsizei stride, const GLvoid *pointer);
void glNormalPointer(GLenum type, GLsizei stride, const GLvoid *pointer);
void glFogCoordPointer(GLenum type, GLsizei stride, const GLvoid *pointer);
void glTexCoordPointer(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
void glEdgeFlagPointer(GLsizei stride, const GLvoid *pointer);

3) 用这些数据绘制几何图形。OpenGL通过指针从所有的被激活数组中获取数据。在客户端-服务器模型中,数据被传输到服务器的地址空间中。有3种方式可以完成这个任务:
• 访问单独的数组元素(随机存取)。
• 创建一个单独数组元素的列表(系统存取)。
• 线性地处理数组元素。

在顶点数组的内容被解引用(即提取指针所指向的数据)之前,数组一直保存在客户端,它们的内容很容易进行修改。在步骤3中,数组中的数据被提取,接着发送到服务器,然后发送到图形处理管线进行渲染。可以从单个数组元素(索引位置)提取数据,也可以从一个有序的数组元素列表(可能被限制为整个顶点数组数据的一个子集)中提取数据,或者从一个数组元素序列中提取数据。

解引用单个数组元素

void glArrayElement(GLint ith)
获取当前所有已启用数组的一个顶点(第ith个)的数据。对于顶点坐标数组,对应的函数是glVertex[size][type]v(),其中size是[2, 3, 4]之一。type是[s, i, f, d]之一,分别表示GLshort、GLint、GLfloat和GLdouble。size和type都是由glVertexPointer()函数定义的。对于其他启用的数组,glArrayElement()分别调用glEdgeFlagv()、glTexCoord[size][type]v()、glColor[size][type]v()、glSecondaryColor3[type]v()、glInde[type]v()、glNormal3[type]v()和glFogCoord[type]v()。如果启用了顶点坐标数组,在其他几个数组(如果启用)相对应的函数(与数组值相对应,最多可达7个)被执行之后,glVertex*v()函数在最后执行。
glArrayElement()通常是在glBegin()和glEnd()之间调用。否则,glArrayElement()函数就会设置所有启用的数组的当前状态(顶点除外,因为它不存在当前状态)

解引用数组元素的一个列表

void glDrawElements(GLenum mode,GLsizei count,GLenum type,void *indices);

mode(图元的类型)、count(元素的数量)、type(数据类型)和indices(顶点数据的数组位置)。glDrawRangeElements()引入了两个新参数:start和end,它们指定了indices可以接受的值的范围。indices数组中的值必须位于start和end之间才是合法的(包含start和end)。

调用glDrawArrays()函数的效果差不多相当于下面这段代码:
glBegin (mode);
for (i = 0; i < count; i++)
    glArrayElement(first + i);
glEnd();
和glDrawElements()相似,glDrawArrays()也会对它的参数值执行错误检查,如果对应的数组被启用,它会导致当前的RGB颜色、辅助颜色、颜色索引、法线坐标、雾坐标、纹理坐标和边界标志处于不确定状态。

解引用一个数组元素序列

glArrayElements()、glDrawElements()和glDrawRangeElements()能够对数据数组进行随机存取,但是glDrawArrays()只能按顺序访问它们。

void glDrawArrays(GLenum mode, GLint first, GLsizei count);
创建一个几何图元序列,使用每个被启用的数组中从first开始,到first + count-1结束的数组元素。mode指定了创建的图元类型,它的值和glBegin()函数所接受的参数值相同。例如:GL_POLYGON、GL_LINE_LOOP、GL_LINES和GL_POINTS等。
调用glDrawArrays()函数的效果差不多相当于下面这段代码:

glBegin (mode);
for (i = 0; i < count; i++)
    glArrayElement(first + i);
glEnd();

和glDrawElements()相似,glDrawArrays()也会对它的参数值执行错误检查,如果对应的数组被启用,它会导致当前的RGB颜色、辅助颜色、颜色索引、法线坐标、雾坐标、纹理坐标和边界标志处于不确定状态。

一个小例子

//
//  main.cpp
//  OpenGL_05_VertexArray
//
//  Created by apple on 14/12/30.
//  Copyright (c) 2014年 cc. All rights reserved.
//

#include <iostream>
#include <GLUT/GLUT.h>

#define POINTER 1           //使用分别指定坐标和颜色顶点数组方式
#define INTERLEAVED 2       //使用混合指定坐标和颜色顶点数组方式


#define DRAWARRAY 1         //使用DrawArray方式
#define ARRAYELEMENT  2     //使用ArrayElement方式
#define DRAWELEMENTS 3      //使用DrawElement方式

//当前设置的模式
int setupMethod = POINTER;
int derefMethod = DRAWARRAY;

/**
 *  设置顶点坐标数组和顶点颜色数组(分别指定)
 */
void setupPointers() {
    
    //顶点坐标数组
    static GLint vertices[] = {25, 25,
        100, 325,
        175, 25,
        175, 325,
        250, 25,
        325, 325
    };
    
    //顶点颜色RBG数组
    static GLfloat colors[] = {1.0, 0.2, 0.2,
        0.2, 0.2, 1.0,
        0.8, 1.0, 0.2,
        0.75, 0.75, 0.75,
        0.35, 0.35, 0.35,
        0.5, 0.5, 0.5
    };
    
    //启用顶点坐标数组
    glEnableClientState(GL_VERTEX_ARRAY);
    //启用顶点颜色数组
    glEnableClientState(GL_COLOR_ARRAY);
    
    //指定顶点坐标数据
    glVertexPointer(2, GL_INT, 0, vertices);
    //指定顶点颜色数据
    glColorPointer(3, GL_FLOAT, 0, colors);
    
}

/**
 *  设置顶点坐标和顶点颜色数组(混合数组)
 */
void setupInterleave() {
    
    /**
     *  intertwined[0~2]* n 为顶点坐标
     *  intertwined[3~5]* n 为顶点颜色
     */
    static GLfloat intertwined[] = {
        1.0, 0.2, 1.0, 100.0, 100.0, 0.0,
        1.0, 0.2, 0.2, 0.0, 200.0, 0.0,
        1.0, 1.0, 0.2, 100.0, 300.0, 0.0,
        0.2, 1.0, 0.2, 200.0, 300.0, 0.0,
        0.2, 1.0, 1.0, 300.0, 200.0, 0.0,
        0.2, 0.2, 1.0, 200.0, 100.0, 0.0
    };
    
    //指定混合顶点数组
    glInterleavedArrays (GL_C3F_V3F, 0, intertwined);
}

/**
 *  初始化操作
 */
void init() {
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    //设置着色模式
    //GL_SMOOTH 制定的两点颜色进行插值,绘制之间的其他点
    //如果两点的颜色相同,使用两个参数效果相同
    //如果两点颜色不同,会出现过渡效果
    glShadeModel(GL_SMOOTH);
    //设置顶点数组,分别指定颜色和坐标
    setupPointers();
}

/**
 *  展示绘制效果
 */
void display() {
    
    //清理颜色缓冲区
    glClear(GL_COLOR_BUFFER_BIT);
    
    if (derefMethod == DRAWARRAY) {
        //从顶点数组中的第一个顶点开始连续取6个顶点,绘制三角形
        glDrawArrays(GL_TRIANGLES, 0, 6);
    } else if (derefMethod == ARRAYELEMENT) {
        //取顶点坐标数组中的任意3个点进行绘制三角形
        glBegin(GL_TRIANGLES);
        glArrayElement(2);
        glArrayElement(3);
        glArrayElement(5);
        glEnd();
    } else if (derefMethod == DRAWELEMENTS) {
        //指定需要绘制的顶点的索引,绘制多边形
        GLuint indices[4] = {0, 1, 3, 4};
        glDrawElements(GL_POLYGON, 4, GL_UNSIGNED_INT, indices);
    }
    
    glFlush ();
    
}

/**
 *  调整窗口尺寸
 *
 *  @param width  宽度
 *  @param height 高度
 */
void reshape(int width, int height) {
    //设置视口矩形区域,在默认情况下,视口被设置为占据打开窗口的整个像素矩形
    glViewport(0, 0, (GLsizei)width, (GLsizei)height);
    //对投影矩阵应用随后的矩阵操作
    glMatrixMode(GL_PROJECTION);
    //等于是将之前矩阵变换导致变化过的栈顶矩阵重新归位,置为单位矩阵!等于是之前的矩阵变换带来的影响到此为止了!
    glLoadIdentity();
    //指定2D裁剪坐标系,naer和far使用默认值-1和1
    gluOrtho2D(0.0, (GLdouble)width, 0.0, (GLdouble)height);
}

/**
 *  键盘事件回调
 *
 *  @param key 键位
 *  @param x   x坐标
 *  @param y   y坐标
 */
void keyboard(unsigned char key, int x, int y) {
    switch (key) {
            //ESC
        case 27:
            exit(0);
            break;
    }
}

/**
 *  鼠标事件回调
 *
 *  @param button 按钮类型
 *  @param state  点击状态
 *  @param x      x坐标
 *  @param y      y坐标
 */
void mouse (int button, int state, int x, int y) {
    switch (button) {
        //左键
        case GLUT_LEFT_BUTTON:
            if (state == GLUT_DOWN) {
                //点击左键,(使用颜色和坐标分离的数组方式) 和 (使用混合数组的方式) 切换
                if (setupMethod == POINTER) {
                    setupMethod = INTERLEAVED;
                    setupInterleave();
                } else if (setupMethod == INTERLEAVED) {
                    setupMethod = POINTER;
                    setupPointers();
                }
                //重绘
                glutPostRedisplay();
            }
            break;
        //右键
        case GLUT_RIGHT_BUTTON:
            if (state == GLUT_DOWN) {
                if (derefMethod == DRAWARRAY) {
                    derefMethod = ARRAYELEMENT;
                } else if (derefMethod == ARRAYELEMENT) {
                    derefMethod = DRAWELEMENTS;
                } else if (derefMethod == DRAWELEMENTS) {
                    derefMethod = DRAWARRAY;
                }
                //重绘
                glutPostRedisplay();
            }
            break;
        default:
            break;
    }
}

int main(int argc, const char * argv[]) {
    
    //初始化GLUT库
    glutInit(&argc, (char**)argv);
    //设置单缓冲,RGB像素格式的窗口
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    //设置窗口大小
    glutInitWindowSize(350, 350);
    //设置窗口坐标
    glutInitWindowPosition (100, 100);
    //创建窗口
    glutCreateWindow("VertexArray");
    
    //初始化操作
    init();
    
    //设置展示的回调方法
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutKeyboardFunc(keyboard);
    glutMouseFunc(mouse);

    //绘制线程开始循环
    glutMainLoop();
    
    return 0;
}


本文由CC原创总结,如需转载请注明出处:http://blog.csdn.net/oktears/article/details/42269263

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: OpenGL 可以使用索引来表示顶点坐标,这样就可以避免在渲染图形时重复地发送相同的顶点数据。 使用索引的方法是,首先在程序中创建一个索引数组,其中每个元素都是一个顶点的索引。然后,可以使用 glDrawElements 函数来渲染图形。这个函数需要三个参数:图形的类型、索引数组的大小和索引数组的指针。例如,如果要渲染一个三角形,可以这样调用 glDrawElementsglDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, indices); 在这个调用中,图形的类型是三角形,索引数组的大小是 3(因为一个三角形有 3 个顶点),索引数组的指针是 indices。 注意,在使用索引时,还需要在程序中创建一个顶点数组对象(VAO)并将顶点坐标数组作为它的一个属性。然后,在调用 glDrawElements 时,可以通过绑定 VAO 来使用这个顶点数组。例如: glBindVertexArray(vao); glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, indices); glBindVertexArray(0); ### 回答2: OpenGL中使用索引来表示顶点坐标是一种优化的方法,可以减少顶点数据的冗余,提高渲染性能。 具体实现时,首先需要定义一个顶点数组(Vertex Array),顶点数组中保存了所有的顶点坐标数据。然后,再定义一个索引数组(Index Array),索引数组中保存了顶点数组中每个顶点的索引值。 使用索引数组的好处在于,可以通过索引值来引用顶点数组中的顶点坐标,从而减少重复的顶点数据。例如,一个三角形的顶点坐标可以通过三个索引值来表示,而不是重复存储三次顶点坐标。 在渲染过程中,OpenGL会根据索引数组中的索引值,从顶点数组中获取对应的顶点坐标,并进行渲染。这样做可以有效地减少传输和处理的数据量,提高性能。 使用索引来表示顶点坐标有几个需要注意的地方。首先,需要确保索引和顶点数组的长度相匹配,否则会出现数组越界的错误。其次,顶点的绘制顺序需要正确设置,以保证绘制出正确的图形。最后,索引数组中的索引值需要按照一定的规则进行排序,以确保绘制出正确的图形形状。 总之,通过使用索引来表示顶点坐标,可以提高渲染性能,减少数据冗余,并方便地实现各种图形的绘制。 ### 回答3: OpenGL使用索引来表示顶点坐标可以通过以下方法实现。 首先,在定义顶点坐标时,需要提供一个顶点数组。每个顶点都有一个索引值,表示其在顶点数组中的位置。 然后,可以使用一个索引数组来表示绘制图形的顺序。索引数组中的每个元素都对应着顶点数组中的一个索引值。 接下来,通过使用glDrawElements函数来绘制图形。这个函数可以接受三个参数:绘制的图元类型、绘制的元素数量和索引数组的类型。 在绘制图形之前,需要使用glEnableVertexAttribArray函数启用顶点属性数组。然后,可以使用glVertexAttribPointer函数指定顶点属性数组的格式。 最后,使用glDrawElements函数来绘制图形。该函数会根据索引数组中的索引值来找到对应的顶点,并按照索引数组中的顺序来绘制图形。 使用索引来表示顶点坐标的好处是可以减少内存的使用,特别是对于一个图形中有大量相同顶点坐标的情况。通过使用索引数组,可以将重复的顶点数据去除,只保存不重复的顶点坐标,从而节省了内存空间。 此外,使用索引可以提高绘制图形的效率。因为绘制图形时只需要按照索引数组中的顺序来绘制,而不需要每次都重新指定顶点坐标,从而减少了CPU和GPU之间的数据传输量,提高了绘制的速度。 综上所述,OpenGL使用索引来表示顶点坐标可以通过定义顶点数组和索引数组,并使用相应的绘制函数来实现。这种方式可以节省内存空间,提高绘制效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值