對Android opengl ES全局座標系和紋理座標系的理解
初學opengl ES,每一個教你在螢幕上貼圖的opengl版hello world都有這麼兩數組:
static final float COORD[] = { -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, }; static final float TEXTURE_COORD[] = { 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, };
但是幾乎都不解釋,所以我學的時候都不明白這些點為什麼要這麼寫,前後順序有沒有什麼規律。於是各種查資料實驗,終於搞懂了。
1.座標系
PS:本人學opengl es主要是為了2D貼圖,所以不涉及Z軸
,圖一是opengl的全局座標系,這個基本沒啥問題,主要是很多教程說紋理座標是左下原點。實踐得出在Android上應該是最右邊的圖那樣,以左上為原點。
個人猜測紋理吧其實就是一組顏色點組成的數組,Android由於UI座標是以左上為原點,所以把數組裡顏色點的儲存順序改了一下,於是座標系就不一樣了。
2.範例程式碼
public class Filter { protected static final String VERTEX_SHADER = "" + "attribute vec4 position;\n" + "attribute vec4 inputTextureCoordinate;\n" + " \n" + "varying vec2 textureCoordinate;\n" + " \n" + "void main()\n" + "{\n" + " gl_Position = position;\n" + " textureCoordinate = inputTextureCoordinate.xy;\n" + "}"; protected static final String FRAGMENT_SHADER = "" + "varying highp vec2 textureCoordinate;\n" + " \n" + "uniform sampler2D inputImageTexture;\n" + " \n" + "void main()\n" + "{\n" + " gl_FragColor = texture2D(inputImageTexture, textureCoordinate);\n" + "}"; static final float COORD1[] = { -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, }; static final float TEXTURE_COORD1[] = { 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, }; static final float COORD2[] = { -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, }; static final float TEXTURE_COORD2[] = { 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, }; static final float COORD3[] = { 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 1.0f, }; static final float TEXTURE_COORD3[] = { 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, }; static final float COORD4[] = { 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 1.0f, }; static final float TEXTURE_COORD4[] = { 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, }; static final float COORD_REVERSE[] = { 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 1.0f, }; static final float TEXTURE_COORD_REVERSE[] = { 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, }; static final float COORD_FLIP[] = { 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 1.0f, }; static final float TEXTURE_COORD_FLIP[] = { 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, }; private String mVertexShader; private String mFragmentShader; private FloatBuffer mCubeBuffer; private FloatBuffer mTextureCubeBuffer; protected int mProgId; protected int mAttribPosition; protected int mAttribTexCoord; protected int mUniformTexture; public Filter() { this(VERTEX_SHADER, FRAGMENT_SHADER); } public Filter(String vertexShader, String fragmentShader) { mVertexShader = vertexShader; mFragmentShader = fragmentShader; } public void init() { loadVertex(); initShader(); GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA); } public void loadVertex() { float[] coord = COORD1; float[] texture_coord = TEXTURE_COORD1; mCubeBuffer = ByteBuffer.allocateDirect(coord.length * 4) .order(ByteOrder.nativeOrder()) .asFloatBuffer(); mCubeBuffer.put(coord).position(0); mTextureCubeBuffer = ByteBuffer.allocateDirect(texture_coord.length * 4) .order(ByteOrder.nativeOrder()) .asFloatBuffer(); mTextureCubeBuffer.put(texture_coord).position(0); } public void initShader() { mProgId = GLHelper.loadProgram(mVertexShader, mFragmentShader); mAttribPosition = GLES20.glGetAttribLocation(mProgId, "position"); mUniformTexture = GLES20.glGetUniformLocation(mProgId, "inputImageTexture"); mAttribTexCoord = GLES20.glGetAttribLocation(mProgId, "inputTextureCoordinate"); } public void drawFrame(int glTextureId) { if (!GLES20.glIsProgram(mProgId)) { initShader(); } GLES20.glUseProgram(mProgId); mCubeBuffer.position(0); GLES20.glVertexAttribPointer(mAttribPosition, 2, GLES20.GL_FLOAT, false, 0, mCubeBuffer); GLES20.glEnableVertexAttribArray(mAttribPosition); mTextureCubeBuffer.position(0); GLES20.glVertexAttribPointer(mAttribTexCoord, 2, GLES20.GL_FLOAT, false, 0, mTextureCubeBuffer); GLES20.glEnableVertexAttribArray(mAttribTexCoord); if (glTextureId != GLHelper.NO_TEXTURE) { GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, glTextureId); GLES20.glUniform1i(mUniformTexture, 0); } GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); GLES20.glDisableVertexAttribArray(mAttribPosition); GLES20.glDisableVertexAttribArray(mAttribTexCoord); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); GLES20.glDisable(GLES20.GL_BLEND); }}
其中
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
由於openglES本身就是opengl的縮減版,所以能直接畫的形狀就只有三角形,別的複雜的都要由三角形來組成。GLES20.GL_TRIANGLE_STRIP指的就是一種三角形的繪製模式,對應這個頂點數組:
static final float COORD[] = { -1.0f, -1.0f, //1 1.0f, -1.0f, //2 -1.0f, 1.0f, //3 1.0f, 1.0f, //4 };
實際繪製的就是頂點1,2,3組成的三角形和頂點2,3,4組成的三角形合并成的一個矩形,如果有更多點,依次類推(比如有5個點,就是1,2,3 2,3,4 3,4,5三個三角形組成的圖案)。如:
3.紋理頂點順序
紋理的點和全局座標的點之間是對應的:
static final float COORD1[] = { -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, }; static final float TEXTURE_COORD1[] = { 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, };
顯示結果
中箭頭,opengl會把紋理中顏色頂點繪到對應的全局座標頂點上,中間的點則按一定的規律取個平均值什麼的,所以可見實際顯示的圖被上下展開了,因為原圖是1:1,而在該程式裡
GLES20.glViewport(0, 0, width, height);
賦予的顯示地區是高大於寬的(這裡涉及到opengl全局座標和螢幕座標的映射,和本文主旨關係不大就不多說了)。
其實也就是只要全局座標和紋理座標數組裡的點能夠對的上,順序不是問題
代碼裡的四組座標的顯示效果都是一樣的:
static final float COORD1[] = { -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, }; static final float TEXTURE_COORD1[] = { 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, }; static final float COORD2[] = { -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, }; static final float TEXTURE_COORD2[] = { 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, }; static final float COORD3[] = { 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 1.0f, }; static final float TEXTURE_COORD3[] = { 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, }; static final float COORD4[] = { 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 1.0f, }; static final float TEXTURE_COORD4[] = { 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, };
不信的可以在這裡都替換了試試:
float[] coord = COORD1; float[] texture_coord = TEXTURE_COORD1;
為了加深理解,甚至可以玩點花樣出來,比如這樣
static final float COORD_REVERSE[] = { 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 1.0f, }; static final float TEXTURE_COORD_REVERSE[] = { 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, }; 。。。。。。。。。。。。。。。。 float[] coord = COORD_REVERSE; float[] texture_coord = TEXTURE_COORD_REVERSE;
結果如:
4.Demo源碼地址
https://github.com/yellowcath/GLCoordDemo.git