OpenGL ES 2.0 - 纹理映射

OpenGL ES 2.0 - 纹理映射

以下内容概括:纹理映射的基本原理,两种不同的拉伸方式,两种不同的采样方式,
MipMap纹理,多重纹理与过程纹理等.

初识纹理映射

纹理坐标用浮点数表示,范围一般从 0.0 到 1.0.
OpenGL ES 2.0 - 纹理映射_第1张图片

纹理坐标系

原点在左上侧,向右为S轴,向下为T轴,两个轴的取值范围都是 0.0 到 1.0.
eg:若实际图为512x256(单位:像素),则横边第512个像素对应纹理坐标为1,
竖边第256个像素对应纹理坐标为1.

纹理映射剖析

纹理映射的基本思想:
- 首先,为图元中的每个顶点指定恰当的纹理坐标,
- 然后,通过纹理坐标在纹理图中可以确定选中的纹理区域,
- 最后,将选中纹理区域中的内容根据纹理坐标映射到指定的图元上.

纹理映射的过程

  • 首先,图元中的每个顶点都需要在顶点着色器中通过易变变量将纹理坐标传入片元着色器.
  • 经过,顶点着色器后渲染管线的固定功能部分会根据情况进行插值计算,产生对应到每个片元的用于记录纹理坐标的易变变量.
  • 最后,最后每个片元在片元着色器中根据其接收到的记录纹理坐标的易变变量值到纹理图中去提取对应位置的颜色即可.
  • 补充说明: 提取颜色的过程一般称之为纹理采样.

    注意:OpenGL ES 中进行纹理映射时,规定纹理图片尺寸的宽高必须为满足$2^n$,否则运行时会有问题.
    

    eg: 8x8 16x8 32x32 64x64 32x256.

纹理映射的简单案例

重要代码

public void initShader() {//自定义初始化着色器的initShader方法
    //获取程序中顶点纹理坐标属性引用
    maTexCoorHandle = GLES20.glGetAttribLocation(mProgram, "aTexCoor");
}

public void initTexture() {
    int[] textures = new int[1];//生成纹理ID
    GLES20.glGenTextures(
        1,//产生的纹理id的数量
        textures,//纹理id的数组
        0//偏移量
        );
    textureId = textures[0];//获取产生的纹理id
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);//绑定纹理Id
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,//设置MIN采样方式
        GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,//设置MAG采样方式
        GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,//设置S轴拉伸方式
        GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, //设置T轴拉伸方式
        GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
    //通过输入流加载图片
    InputStream is = context.getResources().openRawResource(//建立只想纹理图的流
        R.drawable.wall);
    Bitmap bitmapTmp;
    try {bitmapTmp = BitmapFactory.decodeStream(is);}//从流中加载图片内容
    finally{try{is.close();}catch(IOException e){e.printStackTrace();}}
    GLUtils.texImage2D(//实际加载纹理进显存
        GLES20.GL_TEXTURE_2D,//纹理类型
        0,//纹理的层次,0表示基本图像层,可以理解为直接贴图
        bitmapTmp,//纹理图像
        0//纹理边框尺寸
        );
    bitmapTmp.recycle();//纹理加载成功后释放内存中的纹理图,否则纹理较多时会造成内存崩溃
}

public void drawSelf(int textureId) {
    //...
    //绑定纹理
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);//设置使用的纹理编号
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);//绑定指定的纹理id
    //...
}

顶点着色器脚本代码

uniform mat4 uMVPMtrix;//总变换矩阵
attribute vec3 aPosition;//顶点位置
attribute vec2 aTexCoor;//顶点纹理坐标
varying vec2 vTextureCoord;//用于传递片元着色器的易变变量
void main() {
    gl_Position = uMVPMtrix * vec4(aPosition, 1);//根据总变换矩阵计算此次绘制此顶点的位置
    vTextureCoord = aTexCoor;//将接收的纹理坐标传递给片元着色器
}

片元着色器脚本代码

precision mediump float; //指定默认浮点精度
varying vec2 vTextureCoord;//接收从顶点着色器过来的纹理坐标易变变量
uniform smapler2D sTexture; //纹理采样器,代表一副纹理
void main() {
    gl_FragColor = texture2D(sTexture, vTextureCoord); //进行纹理采样
}

纹理拉伸

重复拉伸方式

若设置的拉伸方式为重复,当顶点纹理坐标大于1时,则实际起作用的纹理坐标为纹理坐标的小数部分.
即:纹理坐标为 3.3,则起作用的纹理坐标为 0.3 示例如下两图
OpenGL ES 2.0 - 纹理映射_第2张图片
OpenGL ES 2.0 - 纹理映射_第3张图片
重要代码

GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, //设置S轴的拉伸方式为重复
    GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,//设置T轴的拉伸方式为重复
    GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);

截取拉伸方式

截取方式中当纹理坐标的值大于1时都看作1,因此会产生边缘被拉伸的效果
OpenGL ES 2.0 - 纹理映射_第4张图片
重要代码

GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, //设置S轴的拉伸方式为截取
    GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, //设置T轴的拉伸方式为截取
    GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);

纹理采样

所谓纹理采样,就是根据片元的纹理坐标到纹理图中提取对应位置颜色的过程.但是图元中的片元与
纹理图中的像素并不总是一一对应的.
例如,将较小的纹理图映射到较大的图元或将较大的纹理图映射到较小的图元,这种情况就会产生.因此
通过纹理坐标在纹理图中并不一定能找到与之完全对应的像素,这时候就需要采用一些策略使得纹理采样可以
顺利进行下去.通常采用的策略有最近点采样,线性采样两种.

最近点采样

各种采样算法中最快的.
基本原理图
OpenGL ES 2.0 - 纹理映射_第5张图片

效果特点

当纹理图较小,图元较大时,容易产生明显的锯齿.
当纹理图较大,图元较小时,也会有锯齿产生,但是由于图元整体较小,视觉上不明显.
重要代码

GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,//设置MIN时为最近点采样
    GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, //设置MAG时为最近点采样
    GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);

线性纹理采样

线性纹理采样比最近点采样更能满足高视觉效果的要求.
基本原理图
OpenGL ES 2.0 - 纹理映射_第6张图片
线性采样的结果颜色不一定仅来自于纹理图中的一个像素.其在采样时会考虑到片元对应的纹理坐标点附近的几个像素.

效果特点

采样时对采样范围内的多个像素进行了加权平均.因此在将较小的纹理图映射到较大的图元上时,不再会有锯齿的现象,
而是平滑过渡的.
重要代码

GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,//设置MIN时为线性采样
    GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,//设置MAG时为线性采样
    GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);

MIN与MAG采样

MIN:当一副尺寸较大的纹理图映射到一个较小的图元时,系统采用GL_TEXTURE_MIN_FILTER对应的纹理采样算法设置;
MAG:当一副尺寸较小的纹理图映射到一个较大的图元时,系统采用GL_TEXTURE_MAG_FILTER对应的纹理采样算法设置;

mipmap纹理技术

当需要处理的场景很大时(如一大片铺满相同纹理的丘陵地形),若不采用一些技术手段,可能会出现远处地形视觉上更
清除,近处地形更模糊的反真实现象.这主要是由于透视投影中有近大远小的效果.远处的地形投影到屏幕上尺寸比较小,
近处的尺寸比较大,而整个场景使用的是同一副纹理图.
因此远处的山体纹理图被缩小进行映射,所以会比较清楚;而近处的山体可能纹理图需要被拉大进行映射,所以比较模糊.
所以mipmap就是解决了,对远处地形采用尺寸较小分辨率的纹理,近处的采用尺寸较大分辨率高的纹理.

mipmap的工作

应用中若需要自己开发根据场景视觉大小自动选择恰当分辨率的纹理进行映射的功能会非常复杂.
mipmap仅需要加载纹理时进行一些设置,并且只需要提供一副原始纹理图,系统会在纹理加载的时候,
自动生成一系列由大到小的纹理图.每幅纹理图是前一副尺寸的 12 ,直至纹理图的尺寸缩小到1x1.
一系列纹理图中的第一幅就是原始纹理图,因此可以计算出,一些列的mipmap纹理图占用的空间接近原始图的2倍.
- 重要代码

GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, //设置MIN情况为mipmap最近点采样
    GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR_MIPMAP_NEAREST);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, //设置MAG情况为mipmap线性采样
    GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR_MIPMAP_LINEAR);
GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D);//自动生成mipmap系列纹理

多重纹理与过程纹理

  • 对同一个图元采用多幅纹理图,这种技术称之为多重纹理.
  • 在多重纹理变化的边界根据某种规则进行平滑过渡,这种技术称之为过程纹理.

将2D纹理映射到球面上的策略 - 未理解待研究

OpenGL ES 2.0 - 纹理映射_第7张图片

拓扑变换 - 待研究.

你可能感兴趣的:(android,opengl,es,opengl,es)