<>1. 纹理介绍

使用 OpenGL ES 绘制简单的几何形状还不够,OpenGL
更多地是用来显示而纹理图像,比如本地图片、相机画面。简单说,纹理(texture)就是一个图像或照片,它们可以被加载进 OpenGL 中。

OpenGL
中的纹理可以用来表示图像、照片等,每个二维的纹理都由许多小的纹理元素组成,它们是小块的数据,类似片段和像素。要使用纹理,最常用的方式是直接从一个图像文件加载数据。


纹理不会被直接绘制,它们要被绑定到纹理单元,然后把这些纹理单元传递给着色器。纹理映射的基本思想就是:首先为图元中的每个顶点指定恰当的纹理坐标,然后通过纹理坐标在纹理图中可以确定选中的纹理区域,最后将选中纹理区域中的内容根据纹理坐标映射到指定的图元上。

纹理的坐标系和顶点着色器的坐标系是不一样的。纹理坐标用浮点数来表示,范围 [0, 1],左上角坐标为 (0.0, 0.0),右上角坐标为 (1.0,
0.0)。注意:要将纹理坐标对应到正确的顶点上,才能使纹理正确地显示。

<>2. 使用纹理显示图片

定义顶点和纹理坐标,两者的顺序必须一一对应。
private static final int COORDS_PER_VERTEX = 2; private static final int
COORDS_PER_TEXTURE= 2; private static final float[] VERTEX = { 1, 1, // top
right -1, 1, // top left 1, -1, // bottom right -1, -1,// bottom left }; private
static final float[] TEXTURE = { 1, 0, // top right 0, 0, // top left 1, 1, //
bottom right 0, 1, // bottom left };
定义着色器。
private static final String VERTEX_SHADER = "uniform mat4 uMVPMatrix;" +
"attribute vec4 aPosition;" + "attribute vec2 aTexCoord;" + "varying vec2
vTexCoord;" + "void main() {" + " gl_Position = uMVPMatrix * aPosition;" + "
vTexCoord = aTexCoord;" + "}"; private static final String FRAGMENT_SHADER =
"precision mediump float;" + "uniform sampler2D uTextureUnit;" + "varying vec2
vTexCoord;" + "void main() {" + " gl_FragColor = texture2D(uTextureUnit,
vTexCoord);" + "}";
加载图片到 OpenGL 中。
// 加载图片并且保存在 OpenGL 纹理系统中 Bitmap bitmap = BitmapFactory.decodeFile(mImagePath);
mBitmapWidth= bitmap.getWidth(); mBitmapHeight = bitmap.getHeight(); int[]
texture= new int[1]; // 生成纹理 GLES20.glGenTextures(1, texture, 0); // 绑定纹理 GLES20
.glBindTexture(GLES20.GL_TEXTURE_2D, texture[0]); // 激活纹理 GLES20.glActiveTexture
(GLES20.GL_TEXTURE0); // 设置缩小过滤为三线性过滤 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); // 加载纹理到 OpenGL,读入 Bitmap
定义的位图数据,并把它复制到当前绑定的纹理对象 GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
// 生成 MIP 贴图 GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D); // 把选定的纹理单元传给片段着色器
GLES20.glUniform1i(texUnitHandle, 0);
计算变换矩阵,采用 CenterInside 或者 CenterCrop 的方式显示。
public static float[] changeMvpMatrixInside(float viewWidth, float viewHeight,
float textureWidth, float textureHeight) { float scale = viewWidth *
textureHeight/ viewHeight / textureWidth; float[] mvp = new float[16]; Matrix.
setIdentityM(mvp, 0); Matrix.scaleM(mvp, 0, scale > 1 ? (1F / scale) : 1F, scale
> 1 ? 1F : scale, 1F); return mvp; } public static float[] changeMvpMatrixCrop(
float viewWidth, float viewHeight, float textureWidth, float textureHeight) {
float scale = viewWidth * textureHeight / viewHeight / textureWidth; float[] mvp
= new float[16]; Matrix.setIdentityM(mvp, 0); Matrix.scaleM(mvp, 0, scale > 1 ?
1F : (1F / scale), scale > 1 ? scale : 1F, 1F); return mvp; }
显示图片。
GLES20.glUseProgram(mProgram); GLES20.glUniformMatrix4fv(mMvpMatrixHandle, 1,
false, mMvpMatrix, 0); // 传入顶点坐标 GLES20.glEnableVertexAttribArray(
mPositionHandle); GLES20.glVertexAttribPointer(mPositionHandle,
COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, mVertexBuffer); // 传入纹理坐标 GLES20.
glEnableVertexAttribArray(mTexCoordHandle); GLES20.glVertexAttribPointer(
mTexCoordHandle, COORDS_PER_TEXTURE, GLES20.GL_FLOAT, false, 0, mTextureBuffer);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, VERTEX.length /
COORDS_PER_VERTEX); GLES20.glDisableVertexAttribArray(mPositionHandle); GLES20.
glDisableVertexAttribArray(mTexCoordHandle); GLES20.glUseProgram(0);
源码在 GitHub
<https://links.jianshu.com/go?to=https%3A%2F%2Fgithub.com%2Fisuperqiang%2FMultiMediaLearning%2Ftree%2Fmaster%2Fapp%2Fsrc%2Fmain%2Fjava%2Fcom%2Frichie%2Fmultimedialearning%2Fopengl>
上。