3.OpenGL绘制(笔记)

OpenGL 缓存数据

1. 创建与分配缓存

  1. 使用glCreateBuffers创建缓存,可在buffers中得到一个缓存对象名称的数组。
  2. 使用glNamedBufferStorage为每个缓存对象分配存储空间
  3. 拥有存储空间后,使用glBindBuffer,绑定缓存对象到缓存目标。
  • 创建缓存

    void glCreateBuffers(GLsizei n,GLuint* buffers);
    

    返回n个当前未使用的缓存对象名称(每个都表示一个新创建的缓存对象),并保存到buffers数组中。

  • 绑定缓存

    void glBindBuffer(GLenum target,GLuint buffer);
    

    将名称为buffer的缓存对象绑定到target所指定的缓存结合点。

    buffer必须是通过glCreateBuffers()分配的名称。如果buffer是第一次被绑定,那么它所对应的缓存对象也将被同时创建。

    缓存绑定的目标
    目标 用途
    GL_ARRAY_BUFFER 这个结合点可以用来保存glVertexAttribPointer()设置的顶点数组数据。在实际工程中这一目标可能是最为常用的。
    GL_COPY_READ_BUFFER和GL_COPY_WRITE_BUFFER 这两个目标是一对互相匹配的结合点,用于拷贝缓存之间的数据,并不会引起OpenGL的状态变化,也不会影响任何特殊形式的OpenGL调用。
    GL_DRAW_INDIRECT_BUFFER 如果采取间接绘制(indirect drawing)的方法,那么这个缓存目标用于存储绘制命令的参数。
    GL_ELEMENT_ARRAY_BUFFER 绑定到这个目标的缓存中可以包含顶点索引数据,以便用于glDrawElements()等索引形式的绘制命令。
    GL_PIXEL_PACK_BUFFER 这一缓存目标用于从图像对象中读取数据,例如纹理和帧缓存数据。相关的OpenGL命令包括glGetTextImage()和glReadPixels()等
    GL_PIXEL_UNPACK_BUFFER 这一缓存目标与之前的GL_PIXEL_PACK_BUFFER相反,它可以作为glTexSubImage2D()等命令的数据源使用。
    GL_TEXTURE_BUFFER 纹理缓存也就是直接绑定到纹理对象的缓存,这样就可以直接在着色器中读取它们的数据信息。GL_TEXTURE_BUFFER可以提供一个操控此类缓存的目标,但是我们还需要将缓存关联到纹理,才能确保它们在着色器中可用。
    GL_TRANSFORM_FEEDBACK_BUFFER transform feedback 是OpenGL提供的一种便捷方案,它可以在管线的顶点处理部分结束时(即经过了顶点着色,可能还有几何着色阶段),将经过变换的顶点重新捕获,并且将部分属性写入缓存对象中。这一目标就提供了这样的结合点,可以建立专门的缓存来记录这些属性数据。
    GL_UNIFORM_BUFFER 这个目标可以用于创建uniform缓存对象(uniform buffer object)的缓存数据。

2.向缓存输入和输出数据

将数据输入和输出OpenGL缓存的方法有很多种,例如:

  • 直接显示地传递数据
  • 用新的数据替换缓存对象中已有的部分数据
  • 由OpenGL负责生成数据然后将它记录到缓存对象中。

最简单方法是在分配内存时读入数据:

void glNamedBufferStorage(GLuint buffer,GLsizeiptr size,const void *data,GLbitfield flags);

为缓存对象buffer分配size大小(单位为字节)的存储空间。如果参数data不是NULL,那么将使用data所在内存区域的内容来初始化整个空间。

flags用来设置缓存的预期用途信息。这些信息flags标识量在用户程序和OpenGL之间构建了协议,允许OpenGL尽可能极致地优化缓存的存储信息。

缓存用途标识符
标识符 意义
GL_DYNAMIC_STORAGE_BIT 设置之后,缓存的内容可以随后通过glNamedBufferSubData()直接进行修改。如果没有设置,那么缓存内容的修改只能在GPU端完成,例如通过着色器来写入。
GL_MAP_READ_BIT 设置之后,我们可以映射缓存数据到CPU端进行读取。如果没有设置的话,当前缓存调用glMapNameBufferRange()来获取读取权限的做法都会失败。
GL_MAP_WRITE_BIT 设置之后,我们可以映射缓存数据到CPU端进行写入。如果没有设置的话,当前缓存调用glMapNamedBufferRange()来获取写入权限的做法都会失败。
GL_MAP_PERSISTENT_BIT 设置之后,对缓存数据的映射将是永久性的。也就是说,它们在渲染的过程中始终有效。这个标识必须在映射的时候同时设置才能创建永久的映射表。
GL_MAP_COHERENT_BIT 设置之后,缓存数据在GPU端和CPU端的映射将保持一致。这个标识必须在映射的时候同时进行设置才能创建一致的映射表。
GL_CLIENT_STORAGE_BIT 对于不一致的内存(nonuniform memory)系统架构来说,有些内存可能从宿主机访问更为高效,而有些内存可能从GPU访问效率更高。在确保其他标识量都设置正确之后,我们可以通过这个标识来引导OpenGL优先选择CPU(宿主)端进行访问来提升效率。
缓存部分初始化
  • 数据设置

    void glNameBufferSubData(GLuint buffer,GLintptr offset,GLsizeiptr size,const void* data);
    

    使用新的数据替换缓存对象buffer中的部分数据。缓存中从offset字节处开始需要使用地址为data、大小为size的数据块来进行更新。如果offset和size的总和超出了缓存对象绑定数据的范围,那么将产生一个错误。

    缓存buffer中存储的数据必须经过glNamedBufferStorage()初始化,并且标识量应当设置为GL_DYNAMIC_STORAGE_BIT。

    将glNamedBufferStorage()和glNamedBufferSubData()结合起来使用,例如:

    //顶点位置
    static const GLfloat positions[] = 
    {
        -1.0f,-1.0f,0.0f,1.0f,
        1.0f,-1.0f,0.0f,1.0f,
        1.0f,1.0f,0.0f,1.0f,
        -1.0f,1.0f,0.0f,1.0f,
    };
    
    //顶点颜色
    static const GLfloat colors[] =
    {
        1.0f,0.0f,0.0f,
        0.0f,1.0f,0.0f,
        0.0f,0.0f,1.0f,
        1.0f,1.0f,1.0f,
    };
    
    //缓存对象
    GLuint buffer;
    
    //创建新的缓存对象
    glCreateBuffers(1,&buffer);
    //分配足够的空间(sizeof(positions)+sizeof(colors))
    glNamedBufferStorage(buffer,								//目标
                         sizeof(positions) + sizeof(colors),	//总计大小
                         nullptr,								//无数据
                         GL_DYNAMIC_STORAGE_BIT);				//标识量
    //将位置信息放置在缓存的偏移地址为0的位置
    glNamedBufferSubData(buffer,								//目标
                         0,										//偏移地址
                         sizeof(positions),						//大小
                        positions);								//数据
    //放置在缓存中的颜色信息的偏移地址为当前填充大小值的位置,也就是sizeof(positons)
    glNamedBufferSubData(buffer,								//目标
                        sizeof(positions),						//偏移地址
                        sizeof(colors),							//大小
                        colors);								//数据
    
  • 数据清除

    将缓存对象的数据清除为一个已知的值,也可用如下接口。

    void glClearNamedBufferData(GLuint buffer,GLenum internalformat,GLenum format,GLenum type,const void* data);
    void glClearNamedBufferSubData(GLuint buffer,GLenum internalformat,GLintptr offset,GLsizeiptr size,GLenum format,GLenum type,const void* data);
    

    清除缓存对象中所有或者部分数据。名为buffer的缓存存储空间将使用data中存储的数据进行填充。format和type分别指定了data对应数据的格式和类型。

    首先将数据被转换到internalformat所指定的格式,然后填充缓存数据的指定区域范围。

    glClearNamedBufferData,进行整个区域的指定的数据填充;

    glClearNamedBufferSubData(),填充区域时通过offset和size来指定,分别给出了以字节为单位的起始偏移地址和大小。

  • 数据复制

    缓存对象中的数据也可以使用glCopyNamedBufferSubData()函数相互进行拷贝。

    void glCopyNamedBufferSubData(GLuint readBuffer,GLuint writeBuffer,GLintptr readoffset,GLintptr writeoffset,GLsizeiptr size);
    

    将名为readBuffer的缓存对象的一部分存储数据拷贝到名为wirteBuffer的缓存对象的数据区域上。readBuffer对应的数据从readoffset位置开始复制size个字节,然后拷贝到writeBuffer对应的数据的writeoffset位置。

    如果readoffset或者writeoffset与size的和超出了绑定的缓存对象的范围,那么OpenGL会产生一个GL_INVALID_VALUE错误。

读取缓存的内容
void glGetNameBufferSubData(GLenum target,GLintptr offset,GLsizeiptr size,void* data);

返回当前名为buffer的缓存对象中的部分或者全部数据。起始数据的偏移字节位置为offset,回读的数据大小为size个字节,它们将从缓存的数据区拷贝到data所指向的内存区域中。

如果缓存对象当前已经被映射,或者offset和size的和超出了缓存对象数据区域的范围,那么将提示一个错误。

3.访问缓存的内容

glNamedBufferData()、glCopyNameBufferSunData()和glGetNamedBufferSubData()都存在一个问题,它们都会导致OpenGL进行一次数据的拷贝操作。

  • 根据硬件的配置,也可以通过获取一个指针的形式,直接在应用程序中对OpenGL管理的内存中进行访问。

    void* glMapBuffer(GLenum target,GLenum access);
    

    将当前绑定到target的缓存对象的整个数据区域映射到客户端的地址空间中。之后可以根据给定的access策略,通过返回的指针对数据进行直接读或者写的操作。

    如果OpenGL无法将缓存对象的数据映射出来,那么glMapBuffer将产生一个错误并返回NULL。发生

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值