【openGL2021版】纹理贴图
大家好,我是Lampard猿奋~
欢迎来到船新的openGL基础系列的博客,今天学习的是纹理贴图
(一)回顾
上周我们学习了openGL的固定管线光照,制作了一个“躺着的”三角形(顶点的Z轴坐标不同),并给三个点设置了不同的法线方向以至呈现不同的颜色
代码:
效果图:
今天就是要为这个三角形贴上纹理图片,给它换一个皮肤
(二)封装纹理类
(1)创建工具Utils
要给三角形创建一个皮肤,那么首先就要把我们的纹理图片,从磁盘读取到内存中,然后再从内存读取到显存中去。因此我们需要一个工具类Utils帮助我们去读取信息
代码:
然后我们创建一个txt去测试是否读取数据成功
在main中引入Util库然后读取这个txt即可
(2)创建纹理类Texture
实现从磁盘读取文件之后,我们就可以创建一个纹理类然后利用这个接口去读取磁盘中的纹理。纹理类比较简单,只需要有一个Init初始化的方法,以及一个成员变量记录创建出来的GPU的ID即可
紧接着就是实现这个初始化函数。纹理的格式有png,jpg,bmp等等格式。测试用例中会使用格式简单bmp图片,并对其进行解码。之后使用png或者jpg图片的时候会利用第三方库
(3)解码bmp纹理
既然使用bmp文件,那么我们就需要对读取到的bmp文件内容进行解码。主要分为两步:第一是读出纹理的宽高,第二部是根据宽高拿到每个像素的rgb数据
此时我们可以在main中我们可以实例化一个纹理对象,然后设置断点看看此时的宽高的数据是不是正确的(此时的width喝height都有数据,证明成功读取)
(4)让gpu生成纹理
我们可以使用glGenTextures来让gpu生成一个纹l理ID,然后绑定该Id对其进行初始化。初始化的方法为glTexParameteri一会单独来讲,在初始化完之后,就可以glTexImage2D来生成一张纹理图片了
创建完之后,记得delete掉之前申请的内存,避免内存泄漏。此时的准备工作已经完成,紧接着就是把生成的纹理图片绑定到三角形中去了
(三)把纹理挂载到图形上
(1)生成一个Texture对象
首先我们需要生成一个纹理对象,让它读取我们的bmp纹理,然后进行初始化
(2)开启GL_TEXTURE_2D
通过glEnable(GL_TEXTURE_2D)和glBindTexture(GL_TEXTURE_2D, texture.mTextureId);来把生成的纹理对象设置为当前使用的2d纹理对象
最后再通过glTexCoord2f来设置每个点对应的纹理坐标就可以了,示例代码如下:
(3)不要在openGL初始化之前生成纹理对象
当按照上面的流程走完之后,得到的结果却没有变化。根据调试之后看到,我们通过glGenTextures生成的纹理Id返回值是0,那就是生成不成功,在查阅资料后得知,原因是我们在Main函数的第一行就生成了纹理,正确做法应该是在openGL初始化工作做完之后再去生成
然后我们就得到了一块带上皮肤的三角形,如果两个三角形拼在一起,我们就得到了一个矩形
(四)纹理过滤模式glTexParameteri
我们已经实现了给openGL的面增加纹理的功能,还剩下最后一个知识点就是在纹理对象进行Init的时候使用到的纹理过滤模式glTexParameteri
图象从纹理图象空间映射到帧缓冲图象空间(映射需要重新构造纹理图像,这样就会造成应用到多边形上的图像失真),这时就可用glTexParmeteri()函数来确定如何把纹理象素映射成像素.
(1)target目标纹理
活动纹理单元的目标纹理,GLES20.GL_TEXTURE_2D表示2D纹理,还有其他纹理,比如GLES11Ext.GL_TEXTURE_EXTERNAL_OES,这是Android特有的OES纹理,预览相机或者视频使用此纹理
(2)pname纹理参数
纹理参数主要有四种:
GL_TEXTURE_MAG_FILTER:是指纹理小于渲染屏幕,没有足够的像素映射到屏幕上的处理方法
GL_TEXTURE_MIN_FILTER:是指纹理大于渲染屏幕,需要进行裁剪的处理方法
GL_TEXTURE_WRAP_S和GL_TEXTURE_WRAP_T:指输入的纹理坐标参数大于1或者小于0时候对应的解决办法
(3)param对应的参数
在纹理大于或者小于渲染屏幕的时候,可以使用GL_LINEAR来进行线性过滤,或者使用GL_NEAREST取最接近的颜色作为需要绘制的像素颜色
效果如下(网图左为GL_LINEAR比较模糊):
在输入的纹理坐标参数大于1或者小于0的时候有
GL_CLAMP_TO_EDGE:剩余部分显示纹理临近的边缘颜色值
GL_REPEAT:重复纹理
GL_MIRRORED_REPEAT:镜像重复纹理
比如说,当我把纹理坐标1都该为2的时候,此时是只有左下角是有图的,因为此时我们的参数是设置为GL_CLAMP,右下右上左上,都是取边缘的颜色也就是黑色
当我们把参数改为GL_REPEAT的时候,那么就会出现重复的纹理,出现四个圆