原博主博客地址:http://blog.csdn.net/qq21497936
本文章博客地址:http://blog.csdn.net/qq21497936/article/details/79184344
《OpenGL学习笔记》系列博客目录地址:http://blog.csdn.net/qq21497936/article/category/7315532
上一章节,编写了我们自己的着色器类,接下来进行纹理的学习。
本章节基础程序使用《OpenGL学习笔记(十一):封装自己的着色器类》完成的demo。(CSDN目前无法设置免积分下载,读者也可以使用笔记十的,按照笔记十一完成即可):
文章地址:http://blog.csdn.net/qq21497936/article/details/79181818
demo下载:http://download.csdn.net/download/qq21497936/10227234
下载地址:http://download.csdn.net/download/qq21497936/10227600
为了让图形更加生动,需要添加更多的顶点和图形的细节,才能创建出图像。但是,如果想让图形看起来更加真实,我们则必须使用足够多的顶点,从而指定足够多的颜色,即每个图形需要更多的顶点,每个顶点又需求一个颜色属性。
为了减少工作量,我们使用纹理(Texture),纹理是一个2D图片(甚至也有1D和3D的纹理),它可以用来添加物体的细节;我们可以想象一张画有窗户纸(window.jpg),把他贴在墙上,这样墙上看起来就像有窗户了,而这张图片我们可以插入非常多的细节,这样就可以让物体非常精细而不用指定额外的顶点。
下面是一张窗户图片:
为了能够把纹理映射(Map)到三角形上,我们需要指定三角形的每个顶点各自对应纹理的哪个部分。这样每个顶点就会关联一个纹理坐标(Texture Coordinate),用来标明该从纹理图像的哪个部分采样。之后再图形的其他片段上进行片段插值(FragmentInterpolation)。
纹理坐标再x和y轴上,范围为0-1之间(使用的是2D纹理),使用纹理坐标获取纹理颜色叫做采样(Sampling),纹理坐标起始于(0,0)也就是纹理图片的左下角,终于右上角(1,1):
我们为三边形指定3个坐标纹理,如上图,我们只要给顶点着色器传递这三个纹理坐标就行了,接下来它们会被传到片段着色器中,它会为每个片段进行纹理坐标的插值。
float vertices[] = {
// 三角形坐标 颜色 纹理坐标
-0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,0.0f,
0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,0.0f,
0.0f, 0.866f, 0.0f, 0.0f, 0.0f, 1.0f, 0.5f,1.0f
};
对纹理采样的解释非常宽松,它可以采用集中不同的插值方式,所以我们需要自己告诉OpenGL该怎样对纹理采样,即纹理环绕方式,这个笔记将会在后续章节统一对纹理的处理进行笔记讲解,现在我们直接加载与创建纹理,并查看实现的效果。
使用纹理,需要把纹理加载到应用程序当中。纹理图像可被存储为各种各样的格式,每种都有自己的数据结构和排列。有两种方案:
使用一个支持多种流行格式的图像加载库来为我们加载,我们用stb_image.h库。
stb_image.h是一个非常流行的单头文件图像加载库,它能够加载大部分流行的文件格式,并且能够简单的整合到工程当中。
stb_image.h下载地址:https://github.com/nothings/stb/blob/master/stb_image.h
新建一个C++头文件,stb_image.h,直接将内容复制进去(C++头文件预编译宏留着),添加一个宏定义STB_IMAGE_IMPLEMENTATION。
通过定义STB_IMAGE_IMPLEMENTATION,预处理器会修改头文件,让其只包含相关的函数定义源码,等于是将这个头文件变为一个 .cpp 文件了。现在只需要在你的程序中包含stb_image.h并编译就可以了。
int width, height, nrChannels;
unsigned char *data = stbi_load("./image/window.jpg", &width, &height, &nrChannels, 0);
这个函数首先接受一个图像文件的位置作为输入。接下来它需要三个int作为它的第二、第三和第四个参数,stb_image.h将会用图像的宽度、高度和颜色通道的个数填充这三个变量。
我们之后生成纹理的时候会用到的图像的宽度和高度的。
和之前生成其他对象一样,也是使用ID引用的。
unsigned int texture;
glGenTextures(1, &texture);
glGenTextures函数首先需要输入生成纹理的数量,然后把它们储存在第二个参数的unsigned int数组中(我们的例子中只是单独的一个unsigned int),就像其他对象一样,我们需要绑定它,让之后任何的纹理指令都可以配置当前绑定的纹理:
glBindTexture(GL_TEXTURE_2D, texture);
现在纹理已经绑定了,我们可以使用前面载入的图片数据生成一个纹理了。纹理可以通过glTexImage2D来生成:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
当调用glTexImage2D时,当前绑定的纹理对象就会被附加上纹理图像。
我们添加了一个额外的顶点属性,我们必须告诉OpenGL我们新的顶点格式:
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); glEnableVertexAttribArray(2);
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;
out vec3 ourColor; // 向着色器输出一个颜色
out vec2 texCoord; // 向着色器输出一个纹理坐标
void main()
{
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
ourColor = aColor; // 将ourColor设置为我们从顶点数据那里得到的输入颜色
texCoord = aTexCoord; // 将textCoord设置为我们从顶点数据那里得到的输入纹理坐标
}
#version 330 core
out vec4 FragColor;
in vec3 ourColor;
in vec2 texCoord;
uniform sampler2D ourTexture;
void main()
{
FragColor = texture(ourTexture, texCoord);
}
效果截图
看图是不是比我们预期的效果Y轴反了?
因为OpenGL纹理贴图左下角是(0.0),y轴向上是正,而stb_image.h加载图片是以左上角的(0,0),y轴向下是正,庆幸的是,stb_image.h提供给我们翻转Y轴的方法(不然我们就得修改顶点缓存或者对着色器动刀子了):
stbi_set_flip_vertically_on_load(true);
#if 1
float vertices[] = {
// 三角形坐标 颜色 纹理坐标
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,0.0f,
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,0.0f,
0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.5f,1.0f
};
#else
float vertices[] = {
// 三角形坐标 颜色 纹理坐标
-0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,0.0f,
0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,0.0f,
0.0f, 0.866f, 0.0f, 0.0f, 0.0f, 1.0f, 0.5f,1.0f
};
#endif
原博主博客地址:http://blog.csdn.net/qq21497936
本文章博客地址:http://blog.csdn.net/qq21497936/article/details/79184344