OpenGL纹理 – 常用API简介

作者 : 开心源码 本文共7474个字,预计阅读时间需要19分钟 发布时间: 2022-05-12 共219人阅读

原始图像数据与内存包装

图像的存储空间= 图像的宽度 * 图像的高度 * 每个像素的字节数(系统决定)

内存对齐:
字长32位的计算机上,假如数据在内存中按照32位的边界对齐(地址为4字节的倍数),那么硬件提取数据的速度就会快得多,同样在64位计算机上,如数据地址按照8字节对齐,他对数据存取效率会非常高。
在许多硬件平台上,考虑到性能的起因位图和像素图的每一行的数据会从特殊的字节对齐地址开始。绝大多数编译器会自动把变量和缓冲区放置在当前计算机架构优化的对齐地址上。OpenGL默认是4字节对齐的,可以通过glPixelStorei来设置像素的存储方式,通过glPixelStoref来恢复像素的存储方式:
glPixelStorei(GL_UNPACK_ALIGNMENT,1);
参数如下图:

image.png

举个例子了解一下:

  • 假如一个图像每行有99个像素,每个像素有RGB 3个颜色通道,那么该图像每行需要的存储空间为:99*3 = 297个字节。按照默认的4字节对齐,可以算出,每行实际分配的存储空间为 300个字节,尽管这会白费存储空间,但会提升CPU抓取数据的效率。

读取纹理

    /** 图形硬件中复制数据,通常通过总线传输到系统内存     * <#GLint x#> <#GLint y#>: 坐标     * <#GLsizei width#> <#GLsizei height#> : 读取的宽度、高度(以像素为单位)     * <#GLenum format#> : 像素格式(见下图)     * <#GLenum type#> : 告诉OpenGL使用缓冲区中的什么数据类型来存储颜色分量(见下图)     * <#GLvoid *pixels#> :指向图形数据的指针     */    glReadPixels(<#GLint x#>, <#GLint y#>, <#GLsizei width#>, <#GLsizei height#>, <#GLenum format#>, <#GLenum type#>, <#GLvoid *pixels#>)

image.png

    /** 从磁盘中载入Targa文件     * <#const char *szFileName#>: 文件名称     * <#GLint *iWidth#> <#GLint *iHeight#>: 读取文件的宽度地址、高度地址     * <#GLint *iComponents#> :文件组件地址     * <#GLenum *eFormat#> :文件格式地址     * 返回值:pBits,指向图像数据的指针     */    gltReadTGABits(<#const char *szFileName#>, <#GLint *iWidth#>, <#GLint *iHeight#>, <#GLint *iComponents#>, <#GLenum *eFormat#>)

载入纹理

    /** 载入纹理     <#GLenum target#> :纹理纬度,一般都是 GL_TEXTURE_2D     <#GLint level#> : mip贴图层次     <#GLint internalformat#> :纹理单元存储的颜色成分(读取时取得)     <#GLsizei width#> <#GLsizei height#>: 纹理的宽高 (读取时取得)     <#GLint border#> : 为纹理指定一个边界宽度,一般传0     <#GLenum format#> :指定纹理数据的格式 :GL_RGB,GL_RGBA等(见上图)。     <#GLenum type#> :指定纹理数据的数据类型 : GL_UNSIGNED_BYTE (见上图)     <#const GLvoid *pixels#> :指向纹理图像数据的指针     */    glTexImage2D(<#GLenum target#>, <#GLint level#>, <#GLint internalformat#>, <#GLsizei width#>, <#GLsizei height#>, <#GLint border#>, <#GLenum format#>, <#GLenum type#>, <#const GLvoid *pixels#>)    //升级纹理    glTexSubImage2D(<#GLenum target#>, <#GLint level#>, <#GLint xoffset#>, <#GLint yoffset#>, <#GLsizei width#>, <#GLsizei height#>, <#GLenum format#>, <#GLenum type#>, <#const GLvoid *pixels#>)    //插入替换纹理    glCopyTexSubImage2D(<#GLenum target#>, <#GLint level#>, <#GLint xoffset#>, <#GLint yoffset#>, <#GLint x#>, <#GLint y#>, <#GLsizei width#>, <#GLsizei height#>)

设置纹理参数

    // 参数一:纹理纬度     参数二:参数名称     参数三:参数的值    glTexParameteri(<#GLenum target#>, <#GLenum pname#>, <#GLint param#>)    glTexParameterf(<#GLenum target#>, <#GLenum pname#>, <#GLfloat param#>)

过滤方式

常用的过滤方式有两种:

  • 临近过滤:是我们能够选择的最简单、最快速的过滤方法,其最明显的特征就是当纹理被拉伸到特别大的时候所出现的大片斑驳状像素。
  • 线性过滤:更接近真实,没有人工操作的痕迹

    image.png
    image.png

设置方式:

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

环绕方式

image.png

//纹理坐标里的 s t r 分别代表 x y z轴//指定横轴的环绕方式为GL_CLAMP_TO_EDGEglTextParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAR_S,GL_CLAMP_TO_EDGE);//指定纵轴的环绕方式为GL_CLAMP_TO_EDGEglTextParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAR_T,GL_CLAMP_TO_EDGE);

不同的环绕方式效果如下:

image.png

纹理坐标

纹理坐标系以纹理左下角为坐标原点,向右为x正轴方向,向上为y轴正轴方向。他的总长度是1。即纹理图片的四个角的坐标分别是:(0,0)、(1,0)、(0,1)、(1,1),分别对应左下、右下、左上、右上四个顶点。二维纹理常用(s, t)坐标表示:

image.png

//设置纹理坐标//注意这里的参数1:texture,纹理层次,对于使用存储着色器来进行渲染,设置为0//后面两个参数对应  x  yvoid MultiTexCoord2f(GLuint texture, GLclampf s, GLclampf t);

如何把纹理坐标应用到三角形上:其纹理坐标就是:
GLfloat texCoords[] = {
0.0f, 0.0f, // 左下角
1.0f, 0.0f, // 右下角
0.5f, 1.0f // 顶部位置
};

image.png

接下来我们来实践一下,给OpenGL固定管线着色器这篇文章里的金字塔加上纹理:其实渲染的流程基本一致,主要是要给每个顶点增加纹理坐标,并读取纹理图像进行渲染:
我们来分析下金字塔的底面纹理坐标:
底面其实是两个三角形:
纹理坐标如下:

image.png

前面/背面/两个侧面都是单独的三角形,纹理坐标如下:

image.png

具体代码实现如下:(Normal3f是为了光照效果设置的法线,不影响金字塔的纹理实现,可以自行去掉看看效果)

    //塔顶    M3DVector3f vApex = { 0.0f, 1.0f, 0.0f };    M3DVector3f vFrontLeft = { -1.0f, -1.0f, 1.0f };    M3DVector3f vFrontRight = { 1.0f, -1.0f, 1.0f };    M3DVector3f vBackLeft = { -1.0f,  -1.0f, -1.0f };    M3DVector3f vBackRight = { 1.0f,  -1.0f, -1.0f };    M3DVector3f n;        //金字塔底部    //底部的四边形 = 三角形X + 三角形Y    //三角形X = (vBackLeft,vBackRight,vFrontRight)        //1.找到三角形X 法线    m3dFindNormal(n, vBackLeft, vBackRight, vFrontRight);       //vBackLeft    pyramidBatch.Normal3fv(n);//    pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);pyramidBatch.MultiTexCoord2f(0, 0.0f, 1.0f);    pyramidBatch.Vertex3fv(vBackLeft);        //vBackRight    pyramidBatch.Normal3fv(n);//    pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);    pyramidBatch.MultiTexCoord2f(0, 1.0f, 1.0f);    pyramidBatch.Vertex3fv(vBackRight);        //vFrontRight    pyramidBatch.Normal3fv(n);//    pyramidBatch.MultiTexCoord2f(0, 1.0f, 1.0f);    pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);    pyramidBatch.Vertex3fv(vFrontRight);            //三角形Y =(vFrontLeft,vBackLeft,vFrontRight)       //1.找到三角形X 法线    m3dFindNormal(n, vFrontLeft, vBackLeft, vFrontRight);        //vFrontLeft    pyramidBatch.Normal3fv(n);//    pyramidBatch.MultiTexCoord2f(0, 0.0f, 1.0f);    pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);    pyramidBatch.Vertex3fv(vFrontLeft);        //vBackLeft    pyramidBatch.Normal3fv(n);//    pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);    pyramidBatch.MultiTexCoord2f(0, 0.0f, 1.0f);    pyramidBatch.Vertex3fv(vBackLeft);        //vFrontRight    pyramidBatch.Normal3fv(n);//    pyramidBatch.MultiTexCoord2f(0, 1.0f, 1.0f);    pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);    pyramidBatch.Vertex3fv(vFrontRight);        // 金字塔前面    //三角形:(Apex,vFrontLeft,vFrontRight)    m3dFindNormal(n, vApex, vFrontLeft, vFrontRight);       pyramidBatch.Normal3fv(n);    pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);    pyramidBatch.Vertex3fv(vApex);        pyramidBatch.Normal3fv(n);    pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);    pyramidBatch.Vertex3fv(vFrontLeft);    pyramidBatch.Normal3fv(n);    pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);    pyramidBatch.Vertex3fv(vFrontRight);        //金字塔左边    //三角形:(vApex, vBackLeft, vFrontLeft)    m3dFindNormal(n, vApex, vBackLeft, vFrontLeft);    pyramidBatch.Normal3fv(n);    pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);    pyramidBatch.Vertex3fv(vApex);        pyramidBatch.Normal3fv(n);    pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);    pyramidBatch.Vertex3fv(vBackLeft);        pyramidBatch.Normal3fv(n);    pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);    pyramidBatch.Vertex3fv(vFrontLeft);        //金字塔右边    //三角形:(vApex, vFrontRight, vBackRight)    m3dFindNormal(n, vApex, vFrontRight, vBackRight);    pyramidBatch.Normal3fv(n);    pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);    pyramidBatch.Vertex3fv(vApex);        pyramidBatch.Normal3fv(n);    pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);    pyramidBatch.Vertex3fv(vFrontRight);        pyramidBatch.Normal3fv(n);    pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);    pyramidBatch.Vertex3fv(vBackRight);        //金字塔后边    //三角形:(vApex, vBackRight, vBackLeft)    m3dFindNormal(n, vApex, vBackRight, vBackLeft);    pyramidBatch.Normal3fv(n);    pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);    pyramidBatch.Vertex3fv(vApex);        pyramidBatch.Normal3fv(n);    pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);    pyramidBatch.Vertex3fv(vBackRight);        pyramidBatch.Normal3fv(n);    pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);    pyramidBatch.Vertex3fv(vBackLeft);

顶点和纹理坐标设置好之后,开始去读取纹理图像:
使用流程:(4、5顺序不是严格的,也可以放在前面两步)

  1. 读取
  2. 载入
  3. 设置纹理属性参数(过滤方式和环绕方式)
  4. 分配纹理对象
  5. 绑定纹理对象
  6. 清理纹理对象
  7. 判断纹理对象能否清理
    //分配纹理对象 参数1:纹理对象个数,参数2:纹理对象指针    glGenTextures(1, &textureID);    //绑定纹理状态 参数1:纹理状态2D 参数2:纹理对象    glBindTexture(GL_TEXTURE_2D, textureID);    GLbyte *pBits;    int nWidth, nHeight, nComponents;    GLenum eFormat;    //读纹理位,读取像素    //参数1:纹理文件名称    //参数2:文件宽度地址    //参数3:文件高度地址    //参数4:文件组件地址    //参数5:文件格式地址    //返回值:pBits,指向图像数据的指针    pBits = gltReadTGABits(szFileName, &nWidth, &nHeight, &nComponents, &eFormat); if(pBits == NULL)        return false;        //设置纹理参数    //参数1:纹理维度    //参数2:为S/T坐标设置模式    //参数3:wrapMode,环绕模式    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode);            //参数1:纹理维度    //参数2:线性过滤    //参数3:wrapMode,环绕模式    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);        //载入纹理    //参数1:纹理维度    //参数2:mip贴图层次    //参数3:纹理单元存储的颜色成分(从读取像素图是取得)    //参数4:加载纹理宽    //参数5:加载纹理高    //参数6:加载纹理的深度    //参数7:像素数据的数据类型(GL_UNSIGNED_BYTE,每个颜色分量都是一个8位无符号整数)    //参数8:指向纹理图像数据的指针        glTexImage2D(GL_TEXTURE_2D, 0, nComponents, nWidth, nHeight, 0,                 eFormat, GL_UNSIGNED_BYTE, pBits);                //使用完毕释放pBits    free(pBits);            //加载Mip,纹理生成所有的Mip层    //参数:GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D    glGenerateMipmap(GL_TEXTURE_2D);

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » OpenGL纹理 – 常用API简介

发表回复