OpenGL学习教程 --- 纹理

OpenGL学习教程 — 纹理

什么是纹理?

在前一节的教程中,我们使用了OpenGL完成了基本的图形绘制,这只是一个很简单基础图形,既不形象,也不逼真,为了使我们绘制的物体更加形象而生动,引入纹理这个东西,何为纹理,你可以理解为一张二维平面 贴图数据,我们使用顶点坐标绘制物体形状,顶点坐标 对应纹理坐标,在光栅化阶段将纹理坐标插值到着色器中,最后在片段着色器中,使用采样器取出纹理坐标下的颜色元素,绘制到物体的片段中去,完成纹理绘制

纹理坐标

由上一段简单的阐释得知,我们需要了解纹理坐标是几何?

顶点坐标的原点是基于屏幕中心原点,X轴正方向向右,Y轴正方向向上,Z轴垂直于屏幕指向我们,而纹理坐标是以左上角为原点,范围在【0,1】,如下图:

纹理坐标

顶点坐标如何对应纹理坐标?

即以屏幕上实际位置对应,两个坐标都应该对应屏幕上的同一个物理点,举个例子最容易懂,如上图我们把这个美女的纹理应用到屏幕上,我们使用条带Triangle方式绘制这个矩形,顶点坐标应该是:

vertex = {
	-1, -1,  //左下
	1,  -1,  //右下
	-1,  1,  //左上
	1,   1   //右上
};

而纹理坐标则应该是:

texture = {
	0, 1,
	1, 1,
	0, 0,
	1, 0
};

很简单吧!

如何将图片的数据贴到纹理上去?

纹理不仅仅用于图片数据显示,还可以把视频的一帧图像数据(也是图片)绑定,显示到图像上去,要知道如何绑定,需要掌握纹理的创建及使用过程,主要分为以下几个步骤

  • glsl编程纹理信息
  • 创建纹理
  • 激活绑定使用纹理

glsl编程

------------vertex shader--------------
varying vec2 v_texture; 		//varying传递纹理坐标至片段着色器	
attribute vec2 a_texture;	
void main(){
    v_texture = a_texture;
}

------------fragment shader--------------
precision mediump float;
varying vec2 v_texture;     		//传递过来的纹理坐标
uniform sampler2D u_TextureUnit;    //对纹理进行取色的取色器
void main() {
    gl_FragColor = texture2D(u_TextureUnit, v_texture);     //取色函数
}

创建纹理

public static int[] loadTexture(Context context, int drawId){
        int[] textureId = new int[1];
        GLES20.glGenTextures(1, textureId, 0);      //创建纹理

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inScaled = false;
        Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), drawId, options);

        //绑定纹理对象,当把纹理对象绑定到GL_TEXTURE_2D上,对它的操作都会影响到textureID上
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId[1]);
        //设置缩小放大过滤方式
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR_MIPMAP_LINEAR);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);

        //设置图片
        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
        bitmap.recycle();

        //  为当前绑定的纹理自动生成所有需要的多级渐远纹理
        // 生成 MIP 贴图
        GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D);

        // 解除与纹理的绑定,避免用其他的纹理方法意外地改变这个纹理
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
        return textureId;
    }

创建纹理对象如上所示,重要的有两点,绑定图像数据到纹理对象和设置纹理过滤方式
绑定图像:

GLUtils.java
public static void texImage2D(int target, int level, Bitmap bitmap, int border);
target: 指定纹理单元的类型是哪一种,必须指定为 GL_TEXTURE_2D, GL_PROXY_TEXTURE_2D, GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, or GL_PROXY_TEXTURE_CUBE_MAP中的一个。二维纹理需要指定为GL_TEXTURE_2D
level: 暂时不知道,默认0,填1我测试图像显示不出来
bitmap: 图片
border: 纹理边框宽度,必须是0

除使用以上bitmap,也可以直接使用图像数据对纹理进行绑定,如下:
GLES20.glTexImage2D方法

过滤:
当我们设置纹理元素的每个单元到像素坐标上去时,不可能是一一对应,当3D物体旋转、缩放时有可能出现一对多和多对一的情况,简单来说当纹理放大时,4X4的纹理坐标元素,如何去对应一个8X8的像素坐标,怎么把纹理元素填充进去,这里就需要纹理过滤,过滤方式有4中:

  • 最近采样点过滤 GL_NEAREST
  • 线性纹理过滤 GL_LINEAR
  • 三线性过滤 GL_LINEAR_MIPMAP_LINEAR
  • 各向异性过滤

GL_NEAREST:
不采取任何处理,对每一个像素单元使用最接近他的纹理坐标单元进行采样,速度快,效果也差

GL_LINEAR:
对每一个像素单元采样最接近他的纹理坐标单元以及它附件2*2的纹理采样,使用他们的平均值,效果比第一种好

GL_LINEAR_MIPMAP_LINEAR:
三线过滤要主要要经过三次计算,使用这种过滤方式要先生成mipmap图,如果一张图片是3232大小,那么它会依次生成3232、1616、88、44、22、1*1的6张图,当我们使用像素单元时,选择最接近他的两层图进行线性过滤,最后生成线性插值到着色器中

以上是自己的一点理解,可以参考大神写得过滤介绍

激活使用纹理

int samper = GLES20.glGetAttribLocation(glProgram, "u_TextureUnit");
int textureIds = GLHelper.loadTexture(context);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);          	  //激活纹理单元	
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds);   //绑定纹理目标
GLES20.glUniform1i(samper, 0);				//配置采样器对应采样哪个纹理单元
//解绑纹理目标
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);

注意:GLES20.glUniform1i(samper, 0),这句为片段着色器中的采样器配置纹理单元,而纹理单元里面有已经激活的纹理,这样采样器就可以去着色了,所以如果有多个纹理,需要配置多个采样器分别对应他们的纹理单元

使用总结

每个设备纹理单元有限,使用GLES20.glActiveTexture(GLES20.GL_TEXTURE0)进行激活,纹理单元有 GLES20.GL_TEXTURE0、GLES20.GL_TEXTURE1等,每个纹理单元下有许多纹理目标GL_TEXTURE_2D、GL_CULL_FACE等,纹理目标又与我们的glGenTextures生成的纹理进行绑定,纹理则又通过texImage2D绑定图片数据

最后,献上我的纹理demo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

帅气好男人_Jack

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值