OpenGL学习笔记(十二):纹理的使用

简介: OpenGL学习笔记(十二):纹理的使用

原博主博客地址:http://blog.csdn.net/qq21497936

本文章博客地址:http://blog.csdn.net/qq21497936/article/details/79184344

 

《OpenGL学习笔记》系列博客目录地址http://blog.csdn.net/qq21497936/article/category/7315532

 

OpenGL学习笔记(十二):纹理的使用

 

前话

       上一章节,编写了我们自己的着色器类,接下来进行纹理的学习。

 

基础程序

       本章节基础程序使用《OpenGL学习笔记(十一):封装自己的着色器类》完成的demo。(CSDN目前无法设置免积分下载,读者也可以使用笔记十的,按照笔记十一完成即可):

 

文章地址:http://blog.csdn.net/qq21497936/article/details/79181818

demo下载:http://download.csdn.net/download/qq21497936/10227234

 

本章完成的Demo

下载地址:http://download.csdn.net/download/qq21497936/10227600

图片.png

 

纹理

       为了让图形更加生动,需要添加更多的顶点和图形的细节,才能创建出图像。但是,如果想让图形看起来更加真实,我们则必须使用足够多的顶点,从而指定足够多的颜色,即每个图形需要更多的顶点,每个顶点又需求一个颜色属性。

       为了减少工作量,我们使用纹理(Texture),纹理是一个2D图片(甚至也有1D和3D的纹理),它可以用来添加物体的细节;我们可以想象一张画有窗户纸(window.jpg),把他贴在墙上,这样墙上看起来就像有窗户了,而这张图片我们可以插入非常多的细节,这样就可以让物体非常精细而不用指定额外的顶点。

       下面是一张窗户图片:

图片.png

       为了能够把纹理映射(Map)到三角形上,我们需要指定三角形的每个顶点各自对应纹理的哪个部分。这样每个顶点就会关联一个纹理坐标(Texture Coordinate),用来标明该从纹理图像的哪个部分采样。之后再图形的其他片段上进行片段插值(FragmentInterpolation)。

       纹理坐标再x和y轴上,范围为0-1之间(使用的是2D纹理),使用纹理坐标获取纹理颜色叫做采样(Sampling),纹理坐标起始于(0,0)也就是纹理图片的左下角,终于右上角(1,1):

图片.png

 

       我们为三边形指定3个坐标纹理,如上图,我们只要给顶点着色器传递这三个纹理坐标就行了,接下来它们会被传到片段着色器中,它会为每个片段进行纹理坐标的插值。

1. float vertices[] = {
2. //    三角形坐标            颜色           纹理坐标
3. -0.5f, 0.0f, 0.0f,     1.0f, 0.0f, 0.0f,        0.0f,0.0f,
4. 0.5f, 0.0f, 0.0f,     0.0f, 1.0f, 0.0f,        1.0f,0.0f,
5. 0.0f, 0.866f, 0.0f,   0.0f, 0.0f, 1.0f,        0.5f,1.0f
6. };

       对纹理采样的解释非常宽松,它可以采用集中不同的插值方式,所以我们需要自己告诉OpenGL该怎样对纹理采样,即纹理环绕方式,这个笔记将会在后续章节统一对纹理的处理进行笔记讲解,现在我们直接加载与创建纹理,并查看实现的效果。

 

加载和创建纹理

       使用纹理,需要把纹理加载到应用程序当中。纹理图像可被存储为各种各样的格式,每种都有自己的数据结构和排列。有两种方案:

 

  • 写自己的加载器个根据格式转换为字节序列(不难,但挺麻烦);
  • 使用一个支持多种流行格式的图像加载库来为我们加载,我们用stb_image.h库。

 

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。

图片.png

 

       通过定义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);

 

  • 第一个参数指定了纹理目标(Target)。设置为GL_TEXTURE_2D意味着会生成与当前绑定的纹理对象在同一个目标上的纹理(任何绑定到GL_TEXTURE_1D和GL_TEXTURE_3D的纹理不会受到影响)。
  • 第二个参数为纹理指定多级渐远纹理的级别,如果你希望单独手动设置每个多级渐远纹理的级别的话。这里我们填0,也就是基本级别。
  • 第三个参数告诉OpenGL我们希望把纹理储存为何种格式。我们的图像只有RGB值,因此我们也把纹理储存为RGB值。
  • 第四个和第五个参数设置最终的纹理的宽度和高度。我们之前加载图像的时候储存了它们,所以我们使用对应的变量。
  • 第六个参数应该总是被设为0(历史遗留的问题)。
  • 第七第八个参数定义了源图的格式和数据类型。我们使用RGB值加载这个图像,并把它们储存为char(byte)数组,我们将会传入对应值。最后一个参数是真正的图像数据。

 

         当调用glTexImage2D时,当前绑定的纹理对象就会被附加上纹理图像。

 

应用纹理

       我们添加了一个额外的顶点属性,我们必须告诉OpenGL我们新的顶点格式:

 

图片.png

 

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);
}

 

效果截图

 

图片.png

看图是不是比我们预期的效果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

 

Demo最终效果截图

 

图片.png

 

原博主博客地址:http://blog.csdn.net/qq21497936

本文章博客地址:http://blog.csdn.net/qq21497936/article/details/79184344


相关文章
|
数据安全/隐私保护 索引
Opengl ES之纹理数组
Opengl ES连载系列
265 0
|
缓存 API Android开发
Android OpenGL添加纹理
Android OpenGL添加纹理
Android OpenGL添加纹理
|
Android开发 异构计算
Android OpenGL ES(八)----纹理编程框架(二)
Android OpenGL ES(八)----纹理编程框架(二)
194 0
Android OpenGL ES(八)----纹理编程框架(二)
|
存储 Java API
Android OpenGL ES(八)----纹理编程框架(一)
Android OpenGL ES(八)----纹理编程框架(一)
326 0
Android OpenGL ES(八)----纹理编程框架(一)
|
缓存 算法 机器人
Android OpenGL ES(七)----理解纹理与纹理过滤
Android OpenGL ES(七)----理解纹理与纹理过滤
275 0
Android OpenGL ES(七)----理解纹理与纹理过滤
|
图形学
学习OpenGL ES之基本纹理
学习OpenGL ES之基本纹理
学习OpenGL ES之基本纹理
|
缓存 索引
OpenGL学习笔记(十三):将纹理贴图应用到四边形上,对VAO/VBO/EBO/纹理/着色器的使用方式进行总结
OpenGL学习笔记(十三):将纹理贴图应用到四边形上,对VAO/VBO/EBO/纹理/着色器的使用方式进行总结
OpenGL学习笔记(十三):将纹理贴图应用到四边形上,对VAO/VBO/EBO/纹理/着色器的使用方式进行总结
|
7月前
|
XML 小程序 Java
【Android App】三维投影OpenGL ES的讲解及着色器实现(附源码和演示 超详细)
【Android App】三维投影OpenGL ES的讲解及着色器实现(附源码和演示 超详细)
145 0
|
缓存 C++
Opengl ES之FBO
Opengl ES连载系列
157 0