OpenGL学习脚印: 绘制一个三角形

写在前面
接着上一节内容,开发环境搭建好后,我们当然想立即编写3D应用程序了。不过我们还需要些耐心,因为OpenGL是一套底层的API,因而我们要掌握的基本知识稍微多一点,在开始绘制3D图形之前,本节我们将通过绘制一个三角形的程序来熟悉现代OpenGL的概念和流程。

通过本节可以了解到:

  • 缓存对象VAO和VBO
  • GLSL着色器程序的编译、链接和使用方法
  • OpenGL绘图的基本流程

绘图流水线简要了解

与使用高级绘图API(例如java里swing绘图,MFC里的绘图)不同,使用OpenGL绘制图形时需要对底层知识有所了解。在现代OpenGL中,完成图形绘制的流水线与旧版的固定流水线有所不同,现代OpenGL程序中允许用户自己定制着色器,这使得绘图更灵活。现代绘图流水线如下图所示(来自:opengl wiki Rendering_Pipeline_Overview):
这里写图片描述
这个绘图流水线是比较复杂的,初学时只需要关注vertex shader顶点着色器和Fragment shader片元着色器即可。顶点着色器负责将用户指定的顶点转换为内部表示,片元着色器决定最终生成图像的颜色。顶点着色器的和片元着色器之间可以通过传递变量来沟通。使用这两个着色器就可以绘制基本的图形了,主要的流程是:
(1) 用户在程序中指定或者加载顶点属性数据
(2) 将顶点属性数据传送到GPU,由顶点着色器处理顶点数据
(3) 由片元着色器负责最终图形的颜色
根据这个步骤,下面逐一熟悉相关概念和操作。

VBO和VAO

在OpenGL程序中指定或者加载的数据是存储在CPU中的,要加快图形渲染,必定要充分利用GPU的优势,因此需要将数据发送到GPU中。在GPU中,VBO即vertex buffer object,顶点缓存对象负责实际数据的存储;而VAO即 vertex array object, 记录数据的存储和如何使用的细节信息。

OpenGL是一个状态机(state machine),我们绘制图形时需要在不同的状态之间切换。例如上一节中通过glClearColor设置清除颜色缓冲区时设定的颜色,OpenGL则记住了这一状态,当调用glClear时则使用这个颜色重置颜色缓冲区。直到再次使用glClearColor设置不同颜色为止,OpenGL会一直使用这个状态值。

使用VAO的优势就在于,如果有多个物体需要绘制,那么我们设置一次绘制物体需要的顶点数据、数据解析方式等信息,然后通过VAO保存起来后,后续的绘制操作不再需要重复这一过程,只需要将VAO设定为当前VAO,那么OpenGL则会使用这些状态信息。当场景中物体较多时,优势十分明显。VAO和VBO的关系如下图所示(图片来自Best Practices for Working with Vertex Data):
这里写图片描述
上图中表示,顶点属性包括位置、纹理坐标、法向量、颜色等多个属性,每个属性的数据可以存放在不同的buffer中。我们可以根据需求,在程序中创建多个VBO和VAO。

使用VAO和VBO的伪代码如下(来自SO):

initialization:
    for each batch
        generate, store, and bind a VAO
        bind all the buffers needed for a draw call
        unbind the VAO

main loop/whenever you render:
    for each batch
        bind VAO
        glDrawArrays(...); or glDrawElements(...); etc.
    unbind VAO

那么如何创建VBO和VAO呢? OpenGL中的对象创建和使用与C++中对象创建不一样,下面代码描述了在C++中创建和使用对象的方式(来自[Learning Modern 3D Graphics Programming]):

struct Object  
{  
    int count;  
    float opacity;  
    char *name;  
};  

//创建对象.  
Object newObject;  

// 设置对象的状态
newObject.count = 5;  
newObject.opacity = 0.4f;  
newObject.name = "Some String";

在OpenGL中创建和使用对象却类似这样:

//创建对象 不允许使用自定义名字  
GLuint objectName;  
glGenObject(1, &objectName);  

// 设置对象状态
glBindObject(GL_MODIFY, objectName);  
glObjectParameteri(GL_MODIFY, GL_OBJECT_COUNT, 5);  
glObjectParameterf(GL_MODIFY, GL_OBJECT_OPACITY, 0.4f);  
glObjectParameters(GL_MODIFY, GL_OBJECT_NAME, "Some String"); 

注意OpenGL中创建一个对象,由GLuint 类型来作为对象标识符,而不允许使用自定义名字,这样就不会导致对象重名了。在OpenGL中每个对象在使用前,要绑定到上下文对象,即所谓的target,例如上例中就是GL_MODIFY这个target。
Step1: 创建VBO 我们这样来创建:

    GLuint VBOId;
    glGenBuffers(1, &VBOId);

API void glGenBuffers( GLsizei n, GLuint * buffers);
这里n指定产生buffer的数目,而buffers则是标识符的地址。一次可以产生一个或者多个buffer.

Step2: 将顶点数据传送到VBO或者为VBO预分配空间。
本节我们绘制一个三角形,对于三角形要在3D空间中指定顶点,必定使用三维坐标。这个顶点坐标需要经过顶点着色器处理后最终才能用于生产三角形,这里面涉及到坐标转换等内容,本节不做深入探讨。经过坐标转换后,顶点坐标最终落在规范化设备坐标系(normalized device coordinate , NDC)中, NDC中坐标范围均为[-1,1],因此这里我们简化处理,将顶点坐标全部定在这个范围内,指定为:

GLfloat vertices[] = {
    -0.5f, 0.0f, 0.0f,
    0.5f, 0.0f, 0.0f,
    0.0f, 0.5f, 0.0f
};

将数据传送到GPU中需要通过函数glBufferData实现。

API void glBufferData( GLenum target,
GLsizeiptr size,
const GLvoid * data,
GLenum usage);
1.函数中target参数表示绑定的目标,包括像GL_ARRAY_BUFFER用于Vertex attributes(顶点属性),GL_ELEMENT_ARRAY_BUFFER用于索引绘制等目标。
2.size参数表示需要分

  • 26
    点赞
  • 63
    收藏
    觉得还不错? 一键收藏
  • 18
    评论
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值