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

版权声明:欢迎技术交流和帮助,提供IT相关服务,索要源码请联系博主QQ: 21497936,若该文为原创文章,未经允许不得转载 https://blog.csdn.net/qq21497936/article/details/79184344

原博主博客地址: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目前无法设置免积分下载,读者也可以使用笔记十的,按照笔记十一完成即可):

纹理

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

        为了减少工作量,我们使用纹理(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是一个非常流行的单头文件图像加载库,它能够加载大部分流行的文件格式,并且能够简单的整合到工程当中。

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

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

          当调用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


Demo最终效果截图




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

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

猜你喜欢

转载自blog.csdn.net/qq21497936/article/details/79184344