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 上。


推薦閱讀:
相关文章