本篇在讲什么 本篇章记录对OpenGL中纹理使用的学习 本篇适合什么 适合初学OpenGL的小白 本篇需要什么 对C++语法有简单认知 对OpenGL有简单认知 最好是有OpenGL超级宝典蓝宝书 依赖Visual Studio编辑器 本篇的特色 具有全流程的图文教学 重实践,轻理论,快速上手 提供全流程的源码内容 |
★提高阅读体验★ ♠ 一级标题♥ 二级标题♣ 三级标题♦ 四级标题 |
纹理是一种结构化的存储形式,可供着色器读写,常用于存储图像数据,我们常见的png、jpg等图片存储的也是图像数据,只不过是经过编码的数据,不能直接被GPU使用,而纹理格式可以直接被GPU读取渲染
在下面的代码中我们对一个纹理进行生成、初始化和绑定的操作
// The type used for names in OpenGL is GLuint
GLuint texture;
// Generate a name for the texture
glGenTextures(1, &texture);
// Now bind it to the context using the GL_TEXTURE_2D binding point
glBindTexture(GL_TEXTURE_2D, texture);
// Specify the amount of storage we want to use for the texture
glTexStorage2D(GL_TEXTURE_2D, // 2D texture
1, // 1 mipmap level
GL_RGBA32F, // 32-bit floating-point RGBA data
256, 256); // 256 x 256 texels
要点1:
通过glGenTextures
接口创建一个纹理
要点2:
通过glBindTexture
将纹理绑定到上下文
要点3:
通过glTexStorage2D
分配纹理的格式和需要的内存空间
在纹理初始化完成后,我们为其更新数据
// Define some data to upload into the texture
float * data = new float[256 * 256 * 4];
// generate_texture() is a function that fills memory with image data
generate_texture(data, 256, 256);
// Assume the texture is already bound to the GL_TEXTURE_2D target
glTexSubImage2D(GL_TEXTURE_2D, // 2D texture
0, // Level 0
0, 0, // Offset 0, 0
256, 256, // 256 x 256 texels, replace entire image
GL_RGBA, // Four channel data
GL_FLOAT, // Floating point data
data); // Pointer to data
// Free the memory we allocated before - \GL now has our data
delete [] data;
要点1:
通过generate_texture
用图像数据填充内存
要点2:
通过glTexSubImage2D
更新纹理数据
在上文中我们展示了通过GL_TEXTURE_2D
来创建和绑定2D纹理目标,其实还有更多的类型,例如可以通过GL_TEXTURE_2D
和GL_TEXTURE_3D
创建一维和三维纹理,下图展示纹理目标类型
纹理目标(GL_TEXTURE_*) | 说明 |
---|---|
1D | 一维纹理 |
2D | 二维纹理 |
3D | 三维纹理 |
RECTANGLE | 矩形纹理 |
1D_ARRAY | 一维数组纹理 |
2D_ARRAY | 二维数组纹理 |
CUBE_MAP | 立方体贴图纹理 |
CUBE_MAP_ARRAY | 立方体贴图数组纹理 |
BUFFER | 缓冲纹理 |
2D_MULTISAMPLE | 二维多重采样纹理 |
2D_MULTISAMPLE_ARRAY | 二维数组多重采样纹理 |
存储数据后我们就可以读取数据,用于着色片段,着色器中的纹理以采样器
变量形式存在,通用以取样器
类型声明统一变量与外界相连接
表示二维纹理的采样器类型为sampler2D
即对应我们上文绑定的目标GL_TEXTURE_2D
,下列片段着色器代码演示如何读取纹理
"#version 430 core \n"
" \n"
"uniform sampler2D s; \n"
" \n"
"out vec4 color; \n"
" \n"
"void main(void) \n"
"{ \n"
" color = texture(s, gl_FragCoord.xy / textureSize(s, 0)); \n"
"}
要点1:
采样器类型sampler2D
是二维纹理类型
要点2:
内置函数texture
从纹理中抓取颜色数据
如前文所述,纹理的每个维度都有一个目标点用于绑定纹理对象,每个目标都有对应的采样器类型,下面列表展示目标点和其对应的采样器类型
纹理目标 | 采样器类型 |
---|---|
GL_TEXTURE_1D | sample1D |
GL_TEXTURE_2D | sample2D |
GL_TEXTURE_3D | sample3D |
GL_TEXTURE_RECTANGLE | sample2Drect |
GL_TEXTURE_1D_ARRAY | sample1Darray |
GL_TEXTURE_2D_ARRAY | sample2Darray |
GL_TEXTURE_CUBE_MAP | sampleCube |
GL_TEXTURE_CUBE_MAP_ARRAY | sampleCubeArray |
GL_TEXTURE_BUFFER | sampleBuffer |
GL_TEXTURE_2D_MULTISAMPLE | sample2DMS |
GL_TEXTURE_2D_MULTISAMPLE_ARRAY | sample2DMSArray |
全文代码这里粘了,具体参考OpenGL蓝宝书官方演示示例simpletexture
,示例内包含和上文相同的代码,下图为效果图
在上文我们已经学习到了通过编码生成纹理数据,但这并不符合实际应用中的情况,现实中我们往往是通过文件载入纹理数据,这一块,我们学习如何通过加载.ktx
文件,显示纹理
#include
// generate_texture() is a function that fills memory with image data
//generate_texture(data, 256, 256);
texture = sb7::ktx::file::load("media/textures/pattern1.ktx");
这里我们只需要通过sb7::ktx::file::load
方法加载ktx文件即可,返回值附加给我们的texture
注意:
上述代码修改自超级宝典第七版例子simpletexture
,generate_texture生成纹理数据替换成文件加载,就可以得到上图所示的效果,我们文件的纹理填充到三角形里面了
官方提供了一个加载外星人图片的例子alienrain
,运行效果如下图所示,我们简单分析一下
for (int i = 0; i < 256; i++)
{
droplet_x_offset[i] = random_float() * 2.0f - 1.0f;
droplet_rot_speed[i] = (random_float() + 0.5f) * ((i & 1) ? -3.0f : 3.0f);
droplet_fall_speed[i] = random_float() + 0.2f;
}
给数组内生成了一组随机的速度、角度、偏移值,并绑定到统一变量的缓存内
tex_alien_array = sb7::ktx::file::load("media/textures/aliens.ktx");
glBindTexture(GL_TEXTURE_2D_ARRAY, tex_alien_array);
从本地ktx文件内加载一组外星人纹理数据,绑定到纹理缓存数据内
"layout (location = 0) in int alien_index;
用来从纹理数组取数据的下标
out VS_OUT
{
flat int alien;
vec2 tc;
} vs_out;
记录外星人纹理数据的下标
layout (std140) uniform droplets
{
droplet_t droplet[256];
};
存储随机数值的统一变量
layout (location = 0) out vec4 color;
in VS_OUT
{
flat int alien;
vec2 tc;
} fs_in;
uniform sampler2DArray tex_aliens;
void main(void)
{
color = texture(tex_aliens, vec3(fs_in.tc, float(fs_in.alien)));
}
根据顶点着色器传过来的外星人纹理数据的下标,从纹理数据缓存里取对应的纹理数据显示
int alien_index;
for (alien_index = 0; alien_index < 256; alien_index++)
{
glVertexAttribI1i(0, alien_index);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
在render方法里没帧都会循环给alien_index赋0-255的值,对应顶点着色器里的外星人的alien_index,所以没帧都会有255个外星人生成
https://github.com/KingSun5
若是觉得博主的文章写的不错,不妨关注一下博主,点赞一下博文,另博主能力有限,若文中有出现什么错误的地方,欢迎各位评论指摘。