Android OpenGL基础1——常用概念及方法解释

OpenGL 对象

在顶点数据定义好了之后,通常这些属性会作为输入发送给图形渲染管线 的第一个处理阶段:顶点着色器 。它会在GPU上创建内存用于存储顶点数据,同时还要告诉OpenGL如何去解释内存中的这些数据,并且指定如何发送到显卡。顶点着色器接下来会处理内存中指定数量的顶点。通常会采用VBO对象来管理这个GPU上分配的内存。

VBO对象

VBO,即顶点缓冲对象(Vertex Buffer Object),主要作用就是可以一次性发送大批顶点数据到显卡上,而不是每个顶点发送一次。原因是CPU传送数据给GPU其实是比较耗时的,所以尽可能的一次性把需要的顶点数据全部传给GPU,这样顶点着色器几乎能立即访问到顶点,有助于加快顶点着色器效率。

创建步骤

  • 生成一个(或多个)缓冲类型的id

    scss 复制代码
    // 生成一个vbo id
    unsigned int vbo;
    glGenBuffers(1, &vbo);
    
    // 或者 生成3个 vbo id
    unsigned int vbos[3];
    glGenBuffers(3, &vbos);

    glGenBuffers(GLsizei n, GLuint * buffers),生成缓冲对象名字,n为缓冲对象名字个数, buffers为存放名字的数组,n和数组长度需要一直

  • 将缓冲对象id绑定给指定的缓冲类型

    scss 复制代码
    glBindBuffers(GL_ARRAY_BUFFER, vbo)

缓冲类型通常有:GL_ARRAY_BUFFER, GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, GL_ELEMENT_ARRAY_BUFFER, GL_PIXEL_PACK_BUFFER, GL_PIXEL_UNPACK_BUFFER, GL_TEXTURE_BUFFER, GL_TRANSFORM_FEEDBACK_BUFFER, or GL_UNIFORM_BUFFER.

注意:OpenGL中存在多种不同类型的缓冲对象(如上所述),所以只要缓冲对象类型不同,OpenGL是允许我们同时绑定多个缓冲的。

小结

创建VBO对称步骤:

  1. 生成VBO对象id(调用glGenBuffers);
  2. 为生成的id指定缓冲类型(调用glBindBuffers);

赋值操作

VBO 对象的赋值主要利用glBufferData函数来进行:

php 复制代码
/**
 * @param target     指定VBO的缓冲类型
 * @param size        表示缓冲数据的大小     
 * @param data        实际缓冲的数据
 * @param usage        表示数据的在GL 的实现中是如何被访问的
 */
void glBufferData(GLenum target, GLsizeiptr size, const GLvoid * data, GLenum usage);

上面的usage参数,常用的有如下几个值:

  • STREAM,modified once and used at most a few times;
  • STATIC,modified once and used many times;
  • DYNAMIC,modified repeatedly and used many times;

示例:

scss 复制代码
// 定义顶点数据
float vertices[] = {
        // 位置              // 颜色
        0.5f, -0.5f, 0.0f,  1.0f, 0.0f, 0.0f,   // 右下
        -0.5f, -0.5f, 0.0f,  0.0f, 1.0f, 0.0f,   // 左下
        0.0f,  0.5f, 0.0f,  0.0f, 0.0f, 1.0f    // 顶部
};
// 调用glBufferData 来完成 VBO对象 的赋值
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

支持数据就已经推送到GPU中去了,一个VBO过程就结束了。

数据的处理

上面通过一些列的接口调用,最终完成了想GPU数据的推送,那么GL是怎么告诉GPU如何处理这些数据的呢,毕竟上面的vertices数组描述了顶点数据两种类型的信息:位置和颜色。要处理上面的数据(vertices),需要如下代码:

scss 复制代码
// 位置属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// 颜色属性
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
  • glEnableVertexAttribArray,开启对应位置的顶点属性;
  • glDisableVertexAttribArray,关闭随影位置的顶点属性;
  • glVertexAttribPointer,说明顶点属性数据的解析规则;

OpenGL中,主要通过glVertexAttribPointer这个函数来说明该如何解析传递给GPU的数据的。参数说明:

arduino 复制代码
 void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride,
                                  const GLvoid * pointer);
  • index,指定哪个顶点属性奖杯修改,通常对应如顶点Shader中layout(position = 0)这个position;
  • size,表示顶点属性指的大小,上面vertices位置和颜色都是有三个float型数据组成的,所以是3;
  • type,指的是数据的类型,上面vertices对应的时GL_FLOAT;
  • normalized,是否希望数据标准化。如果我们设置为GL_TRUE,所有数据都会被映射到0(对于有符号型signed数据是-1)到1之间。我们把它设置为GL_FALSE;
  • stride,表示一条完整顶点属性(包括location和color)的长度(这里需要6个数来表示),所以这里是6;
  • pointer,表示偏移量,即在一段数据中,指定的数据偏移多少位置开始。在这里,坐标数据都是每段数据的起始位置,所以偏移量是0,而颜色数据在坐标数据之后,坐标数据有3个分量,所以每个颜色数据偏移三个float字节开始算;

VAO对象

VAO,即顶点数组对象(Vertex Array Object),VAO作用就是顶点属性调用都会存储在VAO中。这样的好处就是,当配置顶点属性指针时,你只需要将那些调用执行一次,之后再绘制物体的时候只需要绑定相应的VAO就行了。简单来说有点类似复用的概念。 那么VAO到底复用了哪些过程呢,先看物体绘制的一般过程:

scss 复制代码
// 0. 复制顶点数组到缓冲中供OpenGL使用
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 1. 设置顶点属性指针
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// 2. 当我们渲染一个物体时要使用着色器程序
glUseProgram(shaderProgram);
// 3. 绘制物体
someOpenGLFunctionThatDrawsOurTriangle();

如果使用VAO,任何绑定VAO后的顶点属性调用都会储存在这个VAO中。这样的好处就是,当配置顶点属性指针时,你只需要将那些调用执行一次,之后再绘制物体的时候只需要绑定相应的VAO就行了。

创建及使用

生成一个id并绑定,然后进行VBO的复制操作,再进行顶点指针的设置操作,

scss 复制代码
// 定义一个 id,用来存储生成的VAO对象名称
GLuint m_SkyBoxVboId;
// 生成一个VAO对象,并把这个名称存储到上面定义的id中
glGenVertexArrays(1, &m_SkyBoxVaoId);
// 1. 绑定VAO对象
glBindVertexArray(m_SkyBoxVboId);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// 2. 把顶点数据复制到缓冲数据中供OpenGL使用
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 3. 设置顶点属性指针
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);

[...]

// ..:: 绘制代码(渲染循环中) :: ..
// 4. 绘制物体
glUseProgram(shaderProgram);
glBindVertexArray(m_SkyBoxVboId);
someOpenGLFunctionThatDrawsOurTriangle();

// 当不在使用VAO时,解绑
glBindVertexArray(0);

Sample对象

Sample对象,即采样器对象,它是GLSL有一个供纹理对象使用的内建数据结构,在Shader定义好Sample对象后,后面这个对象就可以接收外面传递进来的纹理对象的值了。

根据纹理的类型,Sample对象也有相对应的类型:

  • 1D->sample1D;
  • 2D->sample2D;
  • 3D->sample3D;

数学基础

要学习好OpenGL,就必须对基本的数学知识有所了解,这里先列出几个点:

  • 向量
  • 矩阵

参考文章

  1. Learn OpenGL中文网站
相关推荐
一条上岸小咸鱼3 小时前
Kotlin 控制流(二):返回和跳转
android·kotlin
Jasonakeke3 小时前
【重学 MySQL】九十二、 MySQL8 密码强度评估与配置指南
android·数据库·mysql
Mertrix_ITCH3 小时前
在 Android Studio 中修改 APK 启动图标(2025826)
android·ide·android studio
人生游戏牛马NPC1号3 小时前
学习 Android (十七) 学习 OpenCV (二)
android·opencv·学习
恋猫de小郭4 小时前
谷歌开启 Android 开发者身份验证,明年可能开始禁止“未经验证”应用的侧载,要求所有开发者向谷歌表明身份
android·前端·flutter
用户094 小时前
Gradle声明式构建总结
android
用户095 小时前
Gradle插件开发实践总结
android
Digitally15 小时前
如何将视频从安卓设备传输到Mac?
android·macos
alexhilton17 小时前
Compose Unstyled:Compose UI中失传的设计系统层
android·kotlin·android jetpack