關(guān)于 OpenGL ES 的介紹,請先看上篇:Android 音視頻之使用 OpenGL ES 繪制三角形。
1. 紋理介紹
使用 OpenGL ES 繪制簡單的幾何形狀還不夠,OpenGL 更多地是用來顯示而紋理圖像,比如本地圖片、相機畫面。簡單說,紋理(texture)就是一個圖像或照片,它們可以被加載進 OpenGL 中。
OpenGL 中的紋理可以用來表示圖像、照片等,每個二維的紋理都由許多小的紋理元素組成,它們是小塊的數(shù)據(jù),類似片段和像素。要使用紋理,最常用的方式是直接從一個圖像文件加載數(shù)據(jù)。
紋理不會被直接繪制,它們要被綁定到紋理單元,然后把這些紋理單元傳遞給著色器。紋理映射的基本思想就是:首先為圖元中的每個頂點指定恰當(dāng)?shù)募y理坐標(biāo),然后通過紋理坐標(biāo)在紋理圖中可以確定選中的紋理區(qū)域,最后將選中紋理區(qū)域中的內(nèi)容根據(jù)紋理坐標(biāo)映射到指定的圖元上。
紋理的坐標(biāo)系和頂點著色器的坐標(biāo)系是不一樣的。紋理坐標(biāo)用浮點數(shù)來表示,范圍 [0, 1],左上角坐標(biāo)為 (0.0, 0.0),右上角坐標(biāo)為 (1.0, 0.0)。注意:要將紋理坐標(biāo)對應(yīng)到正確的頂點上,才能使紋理正確地顯示。
2. 使用紋理顯示圖片
定義頂點和紋理坐標(biāo),兩者的順序必須一一對應(yīng)。
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 紋理系統(tǒng)中
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);
// 設(shè)置縮小過濾為三線性過濾
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR_MIPMAP_LINEAR);
// 設(shè)置放大過濾為雙線性過濾
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
// 加載紋理到 OpenGL,讀入 Bitmap 定義的位圖數(shù)據(jù),并把它復(fù)制到當(dāng)前綁定的紋理對象
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);
// 傳入頂點坐標(biāo)
GLES20.glEnableVertexAttribArray(mPositionHandle);
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, mVertexBuffer);
// 傳入紋理坐標(biāo)
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 上。
參考資料: