OpenGL 七 - OpenGL 纹理基础与案例演示
Posted zhangying-domy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenGL 七 - OpenGL 纹理基础与案例演示相关的知识,希望对你有一定的参考价值。
纹理基初知识
一、纹理
1)无论是 tga 文件还是 png/jpg 文件,最终图片文件都是要归结到位图文件去处理的。
纹理文件 --> TGA文件 --> OpenGL --> 位图
ios开发中 --> OpenGL ES --> png/jpg --> 位图
2)原始图像数据:
图像存储空间 = 图像高度 * 图像宽度 * 每个像素的字节数
二、相关函数
1)
// 改变像素存储?式
void glPixelStorei(GLenum pname,GLint param);
// 恢复像素存储?式
void glPixelStoref(GLenum pname,GLfloat param);
// 举例 :
// 参数1: GL_UNPACK_ALIGNMENT 指定 OpenGL 如何从数据缓存区中解包图像数据
// 参数2: 表示参数 GL_UNPACK_ALIGNMENT 设置的值
// GL_UNPACK_ALIGNMENT 指内存中每个像素?起点的排列请求,
// 允许设置为1:byte排列列、2:排列为偶数byte的行、4:字word排列、8:行从双字节边界开始
glPixelStorei(GL_UNPACK_ALIGNMENT,1);
2)从颜色缓存区 内容作为像素图 直接读取
// 参数1:x, 矩形左下角的窗?坐标
// 参数2:y, 矩形左下角的窗口坐标
// 参数3:width, 矩形的宽,以像素为单位
// 参数4:height, 矩形的高,以像素为单位
// 参数5:format, OpenGL 的像素格式,参考下面表格: OpenGL像素表格
// 参数6:type, 解释参数 pixels 指向的数据,告诉OpenGL 使用缓存区中的什么数据类型来存储颜?分量,像素数据的数据类型,参考表格:像素数据的像素类型
// 参数7:pixels, 指向图形数据的指针
void glReadPixels(GLint x,GLint y,GLSizei width,GLSizei height, GLenum format, GLenum type,const void * pixels);
glReadBuffer(mode);// 指定读取的缓存
glWriteBuffer(mode);// 指定写入的缓存
3)载入纹理
void glTexImage1D(GLenum target,GLint level,GLint internalformat,GLsizei width,GLint border,GLenum format,GLenum type,void *data);
void glTexImage2D(GLenum target,GLint level,GLint internalformat,GLsizei width,GLsizei height,GLint border,GLenum format,GLenum type,void * data);
void glTexImage3D(GLenum target,GLint level,GLint internalformat,GLSizei width,GLsizei height,GLsizei depth,GLint border,GLenum format,GLenum type,void *data);
/*参数们:
* target:`GL_TEXTURE_1D`、`GL_TEXTURE_2D`、`GL_TEXTURE_3D`
* Level:指定所加载的 mip 贴图层次。一般我们都把这个参数设置为0
* internalformat:每个纹理单元中存储多少颜色成分
* width、height、depth 参数:指加载纹理的宽度、?度、深度。注意: 这些值必须是 2的整数次? --> 这是因为 OpenGL 旧版本上的遗留下的?个要求,当然现在已经可以支持不是 2 的整数次方,但是开发者们还是习惯使?以2的整数次?去设置这些参数。
* border 参数:允许为纹理贴图指定一个边界宽度
* format、type、data 参数:与我们在讲 glDrawPixels 函数对应的参数相同*/
4)更新纹理
void glTexSubImage1D(GLenum target, GLint level, GLint xOffset, GLsizei width, GLenum format, GLenum type, const GLvoid *data);
void glTexSubImage2D(GLenum target, GLint level, GLint xOffset, GLint yOffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data);
void glTexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset, GLint zOffset, GLsizei width, GLsizei height, GLsizei depth, Glenum type, const GLvoid * data);
5)插?替换纹理
void glCopyTexSubImage1D(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsize width);
void glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yOffset, GLint x, GLint y,GLsizei width,GLsizei height);
void glCopyTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yOffset, GLint zOffset, GLint x,GLint y, GLsizei width, GLsizei height);
6)使?颜?缓存区加载数据,形成新的纹理使?
void glCopyTexImage1D(GLenum target,GLint level,GLenum internalformt,GLint x,GLint y,GLsizei width,GLint border);
void glCopyTexImage2D(GLenum target,GLint level,GLenum internalformt,GLint x,GLint y,GLsizei width,GLsizei height,GLint border);
x,y 在颜?缓存区中指定了开始读取纹理数据的位置; 缓存区里的数据,是源缓存区通过 glReadBuffer 设置的。
注意:不存在 glCopyTextImage3D ,因为我们?法从 2D 颜?缓存区中获取体积数据。
7)纹理对象
// 使?函数分配纹理对象
// 指定纹理对象的数量 和 指针 (指针指向?个?符号整形数组,由纹理对象标识符填充)
void glGenTextures(GLsizei n,GLuint * textTures);
// 绑定纹理状态
// 参数target: GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
// 参数texture: 需要绑定的纹理对象
void glBindTexture(GLenum target,GLunit texture);
// 删除绑定纹理对象
// 纹理对象 以及 纹理对象指针 —> 指针指向?个?符号整形数组,由纹理对象标识符填充
void glDeleteTextures(GLsizei n,GLuint *textures);
// 测试纹理对象是否有效
// 如果 texture 是?个已经分配空间的纹理对象,那么这个函数会返回 GL_TRUE, 否则会返回 GL_FALSE。
GLboolean glIsTexture(GLuint texture);
8)设置纹理参数
glTexParameterf(GLenum target,GLenum pname,GLFloat param);
glTexParameteri(GLenum target,GLenum pname,GLint param);
glTexParameterfv(GLenum target,GLenum pname,GLFloat *param);
glTexParameteriv(GLenum target,GLenum pname,GLint *param);
/* 参数1:target, 指定这些参数将要应?在哪个纹理模式上,?如GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D。
参数2:pname,指定需要设置哪个纹理参数
参数3:param,设定特定的纹理参数的值*/
2种纹理过滤方式的比较:纹理放大缩小时,设置是使?线性过滤还是临近过滤
临近过滤-图像锯齿;线性过滤-平滑柔焦 --> 此效果是指图像放大 or 缩小到一定程度的效果,正常图片的显示不会有问题的。
纹理参数设置函数
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // 纹理放大时,使?线性过滤
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // 纹理缩小时,使?线性过滤
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);// 纹理放大时,使?临近过滤
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); // 纹理缩小时,使?临近过滤
8.2)设置环绕方式
设置环绕方式 API
/*
参数1:GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
参数2:GL_TEXTURE_WRAP_S、GL_TEXTURE_T、GL_TEXTURE_R,针对 s、t、r 坐标 -- s -> x
参数3:GL_REPEAT、GL_CLAMP、GL_CLAMP_TO_EDGE、GL_CLAMP_TO_BORDER
GL_REPEAT:OpenGL 在纹理理坐标超过1.0的?方向上对纹理理进?行行重复;
GL_CLAMP:所需的纹理单元取?纹理边界或 TEXTURE_BORDER_COLOR;
GL_CLAMP_TO_EDGE 环绕模式强制对范围之外的纹理坐标沿着合法的纹理单元的最后一?或者最后一列来进?采样。
GL_CLAMP_TO_BORDER:在纹理坐标在 0.0到1.0范围之外的 只使?边界纹理单元。边界纹理单元是作为围绕基本图像的 额外的行和列,并与基本纹理图像?起加载的。
*/
glTextParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAR_S,GL_CLAMP_TO_EDGE);
glTextParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAR_T,GL_CLAMP_TO_EDGE);
二、相关表格
1)OpenGL像素表格:
2)像素数据的像素类型:
UNSIGNED_BYTE_3_3_2
UNSIGNED_BYTE_2_3_3_REV
指定分量 RGBA 的排列顺序根据 format 参数确定。分量按照分量高位到低位排列。
三、纹理坐标
顶点坐标:x、y、z
纹理坐标:s、t、r --> 其实就是对应的 x、y、z
纹理坐标范围是 0~1 之间,更多地是描述一种映射关系。纹理坐标默认左下角为原点 (0,0)。图示:
立方体:
四、案例
效果:
三角锥底部(2个三角形)的坐标纹理对应关系:
其他剩余三角形面同理。
主要代码:
1 /// 绘制金字塔 2 void MakePyramid(GLBatch& pyramidBatch) 3 { 4 /*1、通过pyramidBatch组建三角形批次 5 参数1:类型 6 参数2:顶点数 7 参数3:这个批次中将会应用1个纹理 8 注意:如果不写这个参数,默认为0。 9 */ 10 pyramidBatch.Begin(GL_TRIANGLES, 18, 1); 11 12 /***前情导入 13 14 2)设置纹理坐标 15 void MultiTexCoord2f(GLuint texture, GLclampf s, GLclampf t); 16 参数1:texture,纹理层次,对于使用存储着色器来进行渲染,设置为0 17 参数2:s:对应顶点坐标中的x坐标 18 参数3:t:对应顶点坐标中的y 19 (s,t,r,q对应顶点坐标的x,y,z,w) 20 21 pyramidBatch.MultiTexCoord2f(0,s,t); 22 23 3)void Vertex3f(GLfloat x, GLfloat y, GLfloat z); 24 void Vertex3fv(M3DVector3f vVertex); 25 向三角形批次类添加顶点数据(x,y,z); 26 pyramidBatch.Vertex3f(-1.0f, -1.0f, -1.0f); 27 28 */ 29 30 //塔顶 31 M3DVector3f vApex = { 0.0f, 1.0f, 0.0f }; 32 M3DVector3f vFrontLeft = { -1.0f, -1.0f, 1.0f }; 33 M3DVector3f vFrontRight = { 1.0f, -1.0f, 1.0f }; 34 M3DVector3f vBackLeft = { -1.0f, -1.0f, -1.0f }; 35 M3DVector3f vBackRight = { 1.0f, -1.0f, -1.0f }; 36 37 //金字塔底部 38 // 底部的四边形 = 三角形X + 三角形Y 39 // 三角形X = (vBackLeft,vBackRight,vFrontRight) 40 // vBackLeft 41 pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f); 42 pyramidBatch.Vertex3fv(vBackLeft); 43 44 // vBackRight 45 pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f); 46 pyramidBatch.Vertex3fv(vBackRight); 47 48 // vFrontRight 49 pyramidBatch.MultiTexCoord2f(0, 1.0f, 1.0f); 50 pyramidBatch.Vertex3fv(vFrontRight); 51 52 53 // 三角形Y =(vFrontLeft,vBackLeft,vFrontRight) 54 // vFrontLeft 55 pyramidBatch.MultiTexCoord2f(0, 0.0f, 1.0f); 56 pyramidBatch.Vertex3fv(vFrontLeft); 57 58 //vBackLeft 59 pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f); 60 pyramidBatch.Vertex3fv(vBackLeft); 61 62 //vFrontRight 63 pyramidBatch.MultiTexCoord2f(0, 1.0f, 1.0f); 64 pyramidBatch.Vertex3fv(vFrontRight); 65 66 67 // 金字塔前面 68 //三角形:(Apex,vFrontLeft,vFrontRight) 69 pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f); 70 pyramidBatch.Vertex3fv(vApex); 71 72 pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f); 73 pyramidBatch.Vertex3fv(vFrontLeft); 74 75 pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f); 76 pyramidBatch.Vertex3fv(vFrontRight); 77 78 //金字塔左边 79 //三角形:(vApex, vBackLeft, vFrontLeft) 80 pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f); 81 pyramidBatch.Vertex3fv(vApex); 82 83 pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f); 84 pyramidBatch.Vertex3fv(vBackLeft); 85 86 pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f); 87 pyramidBatch.Vertex3fv(vFrontLeft); 88 89 //金字塔右边 90 //三角形:(vApex, vFrontRight, vBackRight) 91 pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f); 92 pyramidBatch.Vertex3fv(vApex); 93 94 pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f); 95 pyramidBatch.Vertex3fv(vFrontRight); 96 97 pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f); 98 pyramidBatch.Vertex3fv(vBackRight); 99 100 //金字塔后边 101 //三角形:(vApex, vBackRight, vBackLeft) 102 pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f); 103 pyramidBatch.Vertex3fv(vApex); 104 105 pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f); 106 pyramidBatch.Vertex3fv(vBackRight); 107 108 pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f); 109 pyramidBatch.Vertex3fv(vBackLeft); 110 111 //结束批次设置 112 pyramidBatch.End(); 113 } 114 115 /// 将TGA文件加载为2D纹理。 116 //参数1:纹理文件名称 117 //参数2&参数3:需要缩小&放大的过滤器模式设置 118 //参数4:纹理坐标 环绕模式 119 bool LoadTGATexture(const char *szFileName, GLenum minFilter, GLenum magFilter, GLenum wrapMode) 120 { 121 GLbyte *pBits; 122 int nWidth, nHeight, nComponents; 123 GLenum eFormat; 124 125 //1、读纹理位,读取像素 --> 将图片读取为 位图 126 //参数1:纹理文件名称 127 //参数2:文件宽度地址 128 //参数3:文件高度地址 129 //参数4:文件组件地址 130 //参数5:文件格式地址 131 //返回值:pBits,指向图像数据的指针 132 pBits = gltReadTGABits(szFileName, &nWidth, &nHeight, &nComponents, &eFormat); 133 134 if(pBits == NULL)// 读取图片数据失败直接返回 false 135 return false; 136 137 //2、设置纹理参数 138 //参数1:纹理维度 139 //参数2:为 S/T 坐标设置模式 140 //参数3:wrapMode,环绕模式 141 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode); 142 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode); 143 144 // z设置过滤方式:纹理放大缩小时怎么显示 145 //参数1:纹理维度 146 //参数2:过滤的放大缩小 147 //参数3: 缩小/放大过滤方式. 148 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter); 149 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter); 150 151 152 //3.载入纹理 153 //参数1:纹理维度 154 //参数2:mip贴图层次 155 //参数3:纹理单元存储的颜色成分(从读取像素图是获得) 156 //参数4:加载纹理宽 157 //参数5:加载纹理高 158 //参数6:加载纹理的深度 159 //参数7:像素数据的数据类型(GL_UNSIGNED_BYTE,每个颜色分量都是一个8位无符号整数) 160 //参数8:指向纹理图像数据的指针 161 glTexImage2D(GL_TEXTURE_2D, 0, nComponents, nWidth, nHeight, 0, 162 eFormat, GL_UNSIGNED_BYTE, pBits); 163 164 //使用完毕释放pBits 165 free(pBits); 166 167 //只有minFilter 等于以下四种模式,才可以生成Mip贴图 168 //GL_NEAREST_MIPMAP_NEAREST具有非常好的性能,并且闪烁现象非常弱 169 //GL_LINEAR_MIPMAP_NEAREST常常用于对游戏进行加速,它使用了高质量的线性过滤器 170 //GL_LINEAR_MIPMAP_LINEAR 和GL_NEAREST_MIPMAP_LINEAR 过滤器在Mip层之间执行了一些额外的插值,以消除他们之间的过滤痕迹。 171 //GL_LINEAR_MIPMAP_LINEAR 三线性Mip贴图。纹理过滤的黄金准则,具有最高的精度。 172 if(minFilter == GL_LINEAR_MIPMAP_LINEAR || 173 minFilter == GL_LINEAR_MIPMAP_NEAREST || 174 minFilter == GL_NEAREST_MIPMAP_LINEAR || 175 minFilter == GL_NEAREST_MIPMAP_NEAREST) 176 //4.纹理生成所有的Mip层 177 //参数:GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D 178 glGenerateMipmap(GL_TEXTURE_2D); 179 180 return true; 181 } 182 183 // 初始化 184 void SetupRC() 185 { 186 //1. 187 glClearColor(0.7f, 0.7f, 0.7f, 1.0f ); 188 shaderManager.InitializeStockShaders(); 189 190 //2. 191 glEnable(GL_DEPTH_TEST); 192 193 //3. 194 //分配纹理对象 参数1:纹理对象个数,参数2:纹理对象指针 195 glGenTextures(1, &textureID); 196 197 //绑定纹理状态 参数1:纹理状态2D 参数2:纹理对象 198 glBindTexture(GL_TEXTURE_2D, textureID); 199 200 //将TGA文件加载为2D纹理 --> LoadTGATexture:自定义方法 201 //参数1:纹理文件名称 202 //参数2&参数3:需要缩小&放大的过滤器 203 //参数4:纹理坐标环绕模式 204 LoadTGATexture("stone.tga", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR, GL_CLAMP_TO_EDGE); 205 206 //4.创造金字塔 pyramidBatch --> MakePyramid:自定义方法 207 MakePyramid(pyramidBatch); 208 209 //5. 210 /**相机frame MoveForward(平移) 211 参数1:Z,深度(屏幕到图形的Z轴距离) 212 */ 213 cameraFrame.MoveForward(-10); 214 } 215 216 // 渲染绘制 217 void RenderScene(void) 218 { 219 //1.颜色值&光源位置 220 static GLfloat vLightPos [] = { 1.0f, 1.0f, 0.0f }; 221 static GLfloat vWhite [] = { 1.0f, 1.0f, 1.0f, 1.0f }; 222 223 //2.清理缓存区 224 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); 225 226 //3.当前模型视频压栈 227 modelViewMatrix.PushMatrix(); 228 229 //添加照相机矩阵 230 M3DMatrix44f mCamera; 231 //从camraFrame中获取一个4*4的矩阵 232 cameraFrame.GetCameraMatrix(mCamera); 233 //矩阵乘以矩阵堆栈顶部矩阵,相乘结果存储到堆栈的顶部 将照相机矩阵 与 当前模型矩阵相乘 压入栈顶 234 modelViewMatrix.MultMatrix(mCamera); 235 236 //创建mObjectFrame矩阵 237 M3DMatrix44f mObjectFrame; 238 //从objectFrame中获取矩阵,objectFrame保存的是特殊键位的变换矩阵 239 objectFrame.GetMatrix(mObjectFrame); 240 //矩阵乘以矩阵堆栈顶部矩阵,相乘结果存储到堆栈的顶部 将世界变换矩阵 与 当前模型矩阵相乘 压入栈顶 241 modelViewMatrix.MultMatrix(mObjectFrame); 242 243 //4.绑定纹理,因为我们的项目中只有一个纹理。如果有多个纹理。绑定纹理很重要 244 glBindTexture(GL_TEXTURE_2D, textureID); 245 246 //5.纹理替换矩阵着色器 247 /* 248 参数1:GLT_SHADER_TEXTURE_REPLACE(着色器标签) 249 参数2:模型视图投影矩阵 250 参数3:纹理层 251 */ shaderManager.UseStockShader(GLT_SHADER_TEXTURE_REPLACE, transformPipeline.GetModelViewProjectionMatrix(), 0); 252 253 //pyramidBatch 绘制 254 pyramidBatch.Draw(); 255 256 //模型视图出栈,恢复矩阵(push一次就要pop一次) 257 modelViewMatrix.PopMatrix(); 258 259 //6.交换缓存区 260 glutSwapBuffers(); 261 }
以上是关于OpenGL 七 - OpenGL 纹理基础与案例演示的主要内容,如果未能解决你的问题,请参考以下文章