OpenGL - TBO (Texture Buffer Object) - 缓存纹理 - Instancing Using TBO 前置篇


准备尝试一下 TBO 的方式来实现 Instancing 渲染,所以提前了解一下 TBO 是什么,怎么用,是很有必要的

TBO 就是:Texture Buffer Object 的缩写,意思是:缓存纹理对象

简单的使用,可以参考如下内容

如下内容引用:OpenGL 红宝书 第9版 第6章 - 缓存纹理


缓存纹理

缓存纹理是一种特殊形式的纹理,它允许从着色器中直接访问缓存对象的内容,将它当作一个巨大的以为纹理使用。缓存纹理与一般的以为纹理相比有一些限制和不同,但是在代码中所体现出的形态是非常类似的。我们可以把它当作一般的纹理对象创建,绑定到纹理单元,并使用 glTextureParameteri() 控制它们的参数1。但是,纹理数据的存储实际上是由一个缓存对象(它必须是有名称的)来管理和控制的。此外,缓存纹理也没有内置的采样器,并且采样器对象也不会对缓存纹理产生效果。缓存纹理与一般的一维纹理相比有如下不同之处:

  • 一维纹理的尺寸受限于 GL_MAX_TEXTURE_SIZE 对应的最大值,但是缓存纹理的尺寸受限于 GL_MAX_TEXTURE_BUFFER_SIZE 的值,通常能达到 2GB 甚至更多。
  • 一维纹理支持滤波、mipmap、纹理坐标的截取,以及其他的一些采样器参数,但是缓存纹理不行
  • 一维纹理的纹理坐标是归一化的浮点数值,但是缓存纹理使用的是没有归一化的整数纹理坐标。

我们需要根据自己的实际需求,在应用程序中选中使用缓存纹理或者一维纹理。如果要创建纹理,需要调用 glCreateTexture() 来创建纹理对象,并将 GL_TEXTURE_BUFFER 作为输入的目标参数,然后使用 glTextureBuffer() 函数将纹理与一个缓存对象关联起来。

void glTextureBuffer(GLuint texture, GLenum internalformat, GLuint buffer);

将缓存对象 buffer 的存储空间与缓存纹理 texture 进行关联。buffer 的存储数据将被视为一组数据格式为 internalformat 的元素进行解析,注意数据格式必须是有尺寸后缀的。如果 buffer 为0,那么缓存纹理 texture 中当前已经存在的关联信息将被断开,缓存数据将无法再读取。

例 6.23 是上述缓存创建、初始化数据存储,然后与一个缓存纹理进行关联的全过程示例。

例 6.23 创建和初始化缓存纹理

// 作为数据存储的缓存对象
GLuint buffer;
// 作为缓存纹理使用的纹理对象
GLuint tex;
// 数据应当被保存到程序内存中了
extern const GLvoid* data;

// 生成、绑定和初始化缓存对象,设置绑定点为 GL_TEXTURE_BUFFER。假设这里用到的数据量是 1MB
glGenBuffers(1, &buf);
glBindBuffer(GL_TEXTURE_BUFFER, buf);
glBufferData(GL_TEXTURE_BUFFER, 1024 * 1024,
			 data, GL_STATIC_DRAW);

// 现在创建缓存纹理对象并将它与缓存对象关联
glCreateTexture(1, GL_TEXTURE_BUFFER, &tex);
glTextureBuffer(tex, GL_R32F, buf);

如果只需要关联缓存对象的一部分到缓存纹理中,需要使用 glTextureBufferRange() 函数,它的原型如下所示:

void glTextureBufferRange(GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size);

将缓存对象 buffer 中从 offset 开始,总共 size 字节的一部分存储区域,关联给缓存纹理 texture。buffer 的存储数据将被视为一组数据格式为 internalformat 的元素进行解析,注意数据格式必须是有尺寸后缀的。如果 buffer 为 0,那么缓存纹理 texture 中当前已经存在的关联信息将被断开,缓存数据将无法再读取。offset 必须是一个整数值,并且是系统平台所定义的常量 GL_TEXTURE_BUFFER_OFFSET_ALGINMENT 的倍数。

为了在着色器中访问缓存纹理,还需要创建一个 uniform 变量 samplerBuffer(对于有符号或者无符号整型的变量,还可以使用 isamplerBuffer 和 usamplerBuffer),然后用 texelFetch 函数2来读取单独的纹理采样数据并使用。用于缓存纹理的 texelFetch 函数的定义如下:

vec4 texelFetch(samplerBuffer s, int coord);
ivec4 texelFetch(isamplerBuffer s, int coord);
uvec4 texelFetch(usamplerBuffer s, int coord);

对一个单独的纹素执行查找,纹理当前绑定到 s,而纹理坐标设置为 coord。

有关声明缓存采样器并使用 texelFetch 来读取数据的过程,可以参见例 6.24。

例 6.24 从缓存纹理中查找纹素

#version 450 core

layout (binding = 0) uniform samplerBuffer buf;

in int buf_tex_coord;

layout (location = 0) out vec4 color;

void main(void)
{
	color = texelFetch(buf, buf_tex_coord);
}

References

  • OpenGL 红宝书 第9版 第6章 - 缓存纹理

  1. 并不是所有的纹理参数都适用于缓存纹理,并且缓存纹理也没有采样器,因此会忽略所有的采样器参数。 ↩︎

  2. texelFetch 函数除了用于缓存纹理,也可以用于一般的纹理。如果我们用它来采样一个非缓存纹理,那么纹理的采样器参数将被忽略,并且纹理坐标在这里会被视为一个没有经过归一化的整数数值(这一点与缓存纹理的用法相同)。我们选择在这里单独介绍这个函数,是因为它是使用缓存纹理时最常用到的。 ↩︎

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
本文将介绍计算机图形学中的着色(Shading)技术,包括着色频率、图形管线、纹理映射等知识点,并提供部分代码示例。 ## 着色频率 计算机图形学中的着色可以分为两种频率,分别是顶点着色和像素着色。 顶点着色(Vertex Shading)是在顶点级别对图形进行着色的过程,即在图形的每个顶点上计算颜色值,然后通过插值计算出整个图形的颜色。顶点着色通常用于处理顶点属性,如位置、法向量和颜色等。 像素着色(Pixel Shading)是在像素级别对图形进行着色的过程,即对图形的每个像素计算颜色值。像素着色通常用于处理纹理映射、阴影效果、反射和折射等。 ## 图形管线 图形管线(Graphics Pipeline)是计算机图形学中的一个重要概念,它是将输入的几何形状转化为最终图像的过程,通常包括以下几个阶段: 1. 顶点输入:将输入的顶点数据传入图形管线。 2. 顶点着色:在顶点级别对图形进行着色。 3. 图元装配:将顶点组装成图元,如点、线、三角形等。 4. 光栅化:将图元转化为像素,并计算像素在屏幕上的位置。 5. 像素着色:对图形的每个像素进行着色。 6. 输出合成:将所有像素合成成最终的图像。 以下是一个简单的图形管线示例: ```c++ // 顶点着色器 void vertexShader(in vec3 position, out vec4 color) { // 计算顶点颜色 color = vec4(1.0, 0.0, 0.0, 1.0); // 将顶点位置传递给下一个阶段 gl_Position = vec4(position, 1.0); } // 像素着色器 void pixelShader(in vec4 color, out vec4 fragmentColor) { // 直接输出顶点颜色 fragmentColor = color; } // 主程序 int main() { // 顶点数据 vec3 vertices[] = { vec3(-1.0, -1.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(1.0, -1.0, 0.0) }; // 图元数据 GLuint indices[] = {0, 1, 2}; // 创建着色器程序 GLuint program = createProgram(vertexShader, pixelShader); // 获取顶点着色器输入位置的位置 GLuint positionLocation = glGetAttribLocation(program, "position"); // 创建顶点数组对象 GLuint vao; glGenVertexArrays(1, &vao); glBindVertexArray(vao); // 创建顶点缓冲区对象 GLuint vbo; glGenBuffers(1, &vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 将顶点数据传递给顶点着色器 glEnableVertexAttribArray(positionLocation); glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0); // 创建索引缓冲区对象 GLuint ibo; glGenBuffers(1, &ibo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); // 渲染图元 glUseProgram(program); glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0); // 销毁着色器程序、缓冲区对象和顶点数组对象 glDeleteProgram(program); glDeleteBuffers(1, &vbo); glDeleteBuffers(1, &ibo); glDeleteVertexArrays(1, &vao); return 0; } ``` ## 纹理映射 纹理映射(Texture Mapping)是一种基于图像的着色技术,它可以在三维模型表面上贴上图片,从而增强模型的真实感和细节。 纹理映射通常包括以下几个步骤: 1. 加载纹理图像:从文件中加载纹理图像,并将其存储在计算机内存中。 2. 创建纹理对象:将纹理图像传递给图形硬件,并创建一个纹理对象。 3. 纹理坐标计算:计算每个顶点在纹理图像中对应的位置,通常使用二维纹理坐标表示。 4. 纹理采样:在纹理图像中根据纹理坐标采样像素颜色,并将其作为顶点颜色。 5. 顶点着色:使用顶点颜色进行顶点着色。 以下是一个简单的纹理映射示例: ```c++ // 顶点着色器 void vertexShader(in vec3 position, in vec2 texCoord, out vec2 vTexCoord) { // 将纹理坐标传递给下一个阶段 vTexCoord = texCoord; // 将顶点位置传递给下一个阶段 gl_Position = vec4(position, 1.0); } // 像素着色器 uniform sampler2D texture; void pixelShader(in vec2 vTexCoord, out vec4 fragmentColor) { // 在纹理图像中采样像素颜色 vec4 texel = texture2D(texture, vTexCoord); // 输出纹理像素颜色 fragmentColor = texel; } // 主程序 int main() { // 顶点数据 vec3 vertices[] = { vec3(-1.0, -1.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(1.0, -1.0, 0.0) }; // 纹理坐标数据 vec2 texCoords[] = { vec2(0.0, 0.0), vec2(0.5, 1.0), vec2(1.0, 0.0) }; // 图元数据 GLuint indices[] = {0, 1, 2}; // 加载纹理图像 GLuint texture = loadTexture("texture.png"); // 创建着色器程序 GLuint program = createProgram(vertexShader, pixelShader); // 获取顶点着色器输入位置和纹理坐标的位置 GLuint positionLocation = glGetAttribLocation(program, "position"); GLuint texCoordLocation = glGetAttribLocation(program, "texCoord"); // 创建顶点数组对象 GLuint vao; glGenVertexArrays(1, &vao); glBindVertexArray(vao); // 创建顶点缓冲区对象 GLuint vbo; glGenBuffers(1, &vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 将顶点数据传递给顶点着色器 glEnableVertexAttribArray(positionLocation); glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0); // 创建纹理坐标缓冲区对象 GLuint tbo; glGenBuffers(1, &tbo); glBindBuffer(GL_ARRAY_BUFFER, tbo); glBufferData(GL_ARRAY_BUFFER, sizeof(texCoords), texCoords, GL_STATIC_DRAW); // 将纹理坐标数据传递给顶点着色器 glEnableVertexAttribArray(texCoordLocation); glVertexAttribPointer(texCoordLocation, 2, GL_FLOAT, GL_FALSE, 0, 0); // 创建索引缓冲区对象 GLuint ibo; glGenBuffers(1, &ibo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); // 渲染图元 glUseProgram(program); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture); glUniform1i(glGetUniformLocation(program, "texture"), 0); glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0); // 销毁着色器程序、缓冲区对象、纹理对象和顶点数组对象 glDeleteProgram(program); glDeleteBuffers(1, &vbo); glDeleteBuffers(1, &tbo); glDeleteBuffers(1, &ibo); glDeleteTextures(1, &texture); glDeleteVertexArrays(1, &vao); return 0; } ``` 以上是计算机图形学中着色技术的简单介绍,希望对您有所帮助。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值