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,设定特定的纹理参数的值*/

8.1)设置过滤方式
下图,临近过滤取值:+ 号所在位置的色值;
   线性过滤取值:+ 号所在位置的 附近3个挨着的颜色的混合值。

技术图片

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 纹理基础与案例演示的主要内容,如果未能解决你的问题,请参考以下文章

手把手教会OpenGL之纹理贴图包含纹理载入纹理过滤边界处理纹理参数设置(入门级别案例,棋盘)

OpenGL 八 - 纹理案例

在 OpenGL 中重复纹理

OpenGL chapter5 基础纹理

OpenGL ES iOS 入门实例

Android OpenGL基础相机预览及滤镜