OpenGL-- 纹理加载

前言

这一篇文章的主要内容是用OpenGL去加载纹理,也就是将图片加载到屏幕上。之前几篇文章已经讲解了OpenGL图形绘制的基本图元:点、线、三角形等七种。其实图片加载的过程与基本图元一样,只有一个步骤不同,那就是取色。基本图元绘制时我们给定了颜色值,而加载纹理时是从纹理中取每个像素点的颜色值。要真说区别,那就是颜色值的来源不一样。
然而这篇文章也仅作为纹理加载的入门,该文章用了固定着色器:纹理替换着色器。而在OpenGL ES中我将会自己实现着色器程序,去取纹理中的颜色值,原理一样。

纹理加载流程

这一节实现一个完整的纹理加载过程。

1.纹理坐标

移动端的布局通常以屏幕左上角的点为坐标原点(0,0),而在纹理坐标中,原点是左下角的点,看图1对比一下。
图1-坐标系.png

为什么要先说这个,因为纹理加载时,你告诉OpenGL的不是你的图片容器的尺寸,而是容器与纹理坐标的对应关系。改变映射关系就能够实现图片上下左右的颠倒。

2.纹理相关专用名词

a.临近过滤与线性过滤
个人认为与其说是过滤还不如说是取值,下面用两张图片来解释。


过滤方式.png

很明显临近过滤是取对应像素点的颜色值,而线性过滤则是取它周围最近四个点的颜色混合值,这也是为什么有些图片看上去会模糊,与图片大小和容器大小有关,因为图片大小和容器大小差距太大时过滤方式不一样,导致效果不一样。

b.环绕模式

说到环绕模式这个词大家可能感动陌生,但是做过网页的小伙伴应该了解css中background的repeat属性,repeat、no-repeat会让图片不够撑满背景时进行循环平铺,而环绕模式与这个意义相同。这里通过示意图来枚举一下OpenGL中的环绕模式,仅作了解。
环绕模式.png

3.纹理相关API

a.绑定纹理

// 用来生成纹理的函数。函数根据纹理参数返回n个纹理索引。纹理名称集合不必是一个连续的整数集合,
//(glGenTextures就是用来产生你要操作的纹理对象的索引的,比如你告诉[OpenGL],我需要5个纹理对象,
// 它会从没有用到的整数里返回5个给你)
// params1:纹理对象个数
// params2:纹理对象指针
glGenTextures(1, &textureID);

// 绑定纹理,GL_TEXTURE_2D:个人理解是OpenGL创建颜色缓冲区时创建的一个标识符
// 将纹理名绑定至当前活动纹理单元目标
// 将GL_TEXTURE_2D与textureID关联,后续需要用到textureID
glBindTexture(GL_TEXTURE_2D, textureID);

b.载入纹理

bool loadTGATexture(const char *fileName) {

    GLbyte *pBits;

    int nWidth, nHeight, nComponents;
    GLenum eFormat;
    //1、读纹理位,读取像素
    //参数1:纹理文件名称
    //参数2:文件宽度地址
    //参数3:文件高度地址
    //参数4:文件组件地址
    //参数5:文件格式地址
    //返回值:pBits,指向图像数据的指针
    pBits = gltReadTGABits(fileName, &nWidth, &nHeight, &nComponents, &eFormat);
    if (pBits == NULL) {
        return false;
    }

    // 设置纹理参数

    // 环绕模式,可以试试不设置的效果
    // params1:纹理纬度
    // params2:OpenGL里面的横纵用s/t表示
    // params3:环绕模式,有多种,自行百度,例如:GL_REPEAT
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    // 过滤方式
    // 纹理缩小时,用临近过滤
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    // 纹理放大时,用线性过滤
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    // 载入纹理
    glTexImage2D(GL_TEXTURE_2D, 0, nComponents, nWidth, nHeight, 0, eFormat, GL_UNSIGNED_BYTE, pBits);
    // c,释放
    free(pBits);
    // 加载mip
    glGenerateMipmap(GL_TEXTURE_2D);
    return true;
}

c.设置纹理坐标

GLfloat point00[] = {-0.5, -0.5, 0};
GLfloat point10[] = { 0.5, -0.5, 0};
GLfloat point01[] = {-0.5,  0.5, 0};
GLfloat point11[] = { 0.5,  0.5, 0};

GLBatch squareBatch;
// 与之前画图元不一样
/*
 参数1:类型
 参数2:顶点数
 参数3:这个批次中将会应用1个纹理
 */
squareBatch.Begin(GL_TRIANGLE_FAN, 4, 1);

// 设置顶点对应的纹理坐标
// params1:texture,纹理层次,对于使用存储着色器来进行渲染,设置为0
// params2:s: 对应顶点坐标中的x坐标
// params3:t: 对应顶点坐标中的y
squareBatch.MultiTexCoord2f(0, 0, 0);
// 设置(0,0)对应的屏幕上的坐标点
squareBatch.Vertex3fv(point00);

squareBatch.MultiTexCoord2f(0, 1, 0);
squareBatch.Vertex3fv(point10);

squareBatch.MultiTexCoord2f(0, 1, 1);
squareBatch.Vertex3fv(point11);

squareBatch.MultiTexCoord2f(0, 0, 1);
squareBatch.Vertex3fv(point01);

squareBatch.End();

d.着色器程序
当前使用的着色器是固定着色器:纹理替换着色器(GLT_SHADER_TEXTURE_REPLACE)

// 使用纹理替换矩阵
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_REPLACE, transformLine.GetModelViewProjectionMatrix(), 0);

squareBatch.Draw();

e.销毁

// 删除纹理对象
glDeleteTextures(1, &textureID);

以上几个就是载入纹理的主要API。

总结

让我一起来,回顾那些快遗忘的纹理载入步骤。
假设OpenGL的上下文已经初始化完毕,接下来:

1.绑定:glGenTextures-> glBindTexture
2.载入:gltReadTGABits-> glTexParameteri(4次,两次环绕两次过滤)-> glTexImage2D
3.设置纹理坐标:MultiTexCoord2f->Vertex3fv
4.使用着色器:shaderManager.UseStockShader(GLT_SHADER_TEXTURE_REPLACE, transformLine.GetModelViewProjectionMatrix(), 0);

注意:因为这里用的是固定着色器程序,所以需要用到MVP模型视图矩阵,在OpenGL ES中加载纹理时,会手动实现着色器,流程与这篇文章中的流程略有不同。
最后附上demo地址:https://github.com/zhaoguyixia/OpenGL.git
祝各位生活愉快!!

你可能感兴趣的:(OpenGL-- 纹理加载)