標籤:利用 number 卡通 strip b2c 方向 util south port
轉:http://blog.csdn.net/junzia/article/details/52842816
前面幾篇部落格,我們將了Android中利用OpenGL ES 2.0繪製各種形體,並在上一篇部落格中專門講了GLSL語言。但是我們看到的基於OpenGL開發的應用和遊戲,可不僅僅是那些規則形體和一些簡單的色彩構成,而是各種不規則的形體構成了現實世界或者卡通世界的人和事物,他們都是外面穿著漂亮“衣服”的。本篇部落格就是來講解這些“衣服”的基礎的。這些衣服就是紋理貼圖。
什麼是紋理貼圖
一般說來,紋理是表示物體表面的一幅或幾幅二維圖形,也稱紋理貼圖(texture)。當把紋理按照特定的方式映射到物體表面上的時候,能使物體看上去更加真實。當前流行的圖形系統中,紋理繪製已經成為一種必不可少的渲染方法。在理解紋理映射時,可以將紋理看做應用在物體表面的像素顏色。在真實世界中,紋理表示一個對象的顏色、圖案以及觸覺特徵。紋理只表示對象表面的彩色圖案,它不能改變對象的幾何形式。更進一步的說,它只是一種高強度的計算行為。——百度百科
比如我們在利用OpenGL做遊戲的時候,載入了一個人物模型進來了,這個人物模型上是沒有色彩的。我們需要給它繪上需要的色彩才行。但是這些色彩從哪裡來呢?我們不可能像之前處理球體那樣,根據頂點取產生需要的色彩,那樣對於我們給這個人物模型繪色的工作量實在太大了。這個時候我們就需要用到紋理貼圖的技術了——把一個紋理(對於2D貼圖,可以簡單的理解為圖片),按照所期望的方式顯示在諸多三角形組成的物體的表面。
紋理映射原理
啟用紋理映射後,如果想把一幅紋理映射到相應的幾何圖元,就必須告訴GPU如何進行紋理映射,也就是為圖元的頂點指定恰當的紋理座標。紋理座標用浮點數來表示,範圍一般從0.0到1.0,左上方座標為(0.0,0.0),右上方座標為(1.0,0.0),左下角座標為(0.0,1.0),右下角座標為(1.0,1.0),如所示:
左圖為紋理圖和紋理座標,右圖為頂點圖和頂點座標。
將紋理映射到右邊的兩個三角形上(也就是一個矩形),需要將紋理座標指定到正確的頂點上,才能使紋理正確的顯示,否則顯示出來的紋理會無法顯示,或者出現旋轉、翻轉、錯位等情況。
將右圖頂點按照V2V1V4V3傳入,以三角形條帶方式繪製,則紋理座標應按照V2V1V4V3傳入。如果按照V3V4V1V2傳入,會得到一個旋轉了180度的紋理。如果按照V4V3V2V1傳入,則會得到一個左右翻轉的紋理。
顯示圖片
根據紋理映射原理,結合之前繪製正方形的經驗,我們可以根據以下步驟利用OpenGL ES顯示一張圖片:
第一步,修改著色器
首先,我們需要修改我們的著色器,將頂點著色器修改為:
attribute vec4 vPosition;attribute vec2 vCoordinate;uniform mat4 vMatrix;varying vec2 aCoordinate;void main(){ gl_Position=vMatrix*vPosition; aCoordinate=vCoordinate;}
可以看到,頂點著色器中增加了一個vec2變數,並將這個變數傳遞給了片元著色器,這個變數就是紋理座標。接著我們修改片元著色器為:
precision mediump float;uniform sampler2D vTexture;varying vec2 aCoordinate;void main(){ gl_FragColor=texture2D(vTexture,aCoordinate);}
片元著色器中,增加了一個sampler2D的變數,sampler2D我們在前一篇部落格GLSL語言基礎中提到過,是GLSL的變數類型之一的取樣器。texture2D也有提到,它是GLSL的內建函數,用於2D紋理取樣,根據紋理取樣器和紋理座標,可以得到當前紋理取樣得到的像素顏色。
第二步,設定頂點座標和紋理座標
根據紋理映射原理中的介紹,我們將頂點座標設定為:
private final float[] sPos={ -1.0f,1.0f, //左上方 -1.0f,-1.0f, //左下角 1.0f,1.0f, //右上方 1.0f,-1.0f //右下角 };
相應的,對照頂點座標,我們可以設定紋理座標為:
private final float[] sCoord={ 0.0f,0.0f, 0.0f,1.0f, 1.0f,0.0f, 1.0f,1.0f, };
第三步,計算變換矩陣
按照上步設定頂點座標和紋理座標,大多數情況下我們得到的一定是一張拉升或者壓縮的圖片。為了讓圖片完整的顯示,且不被展開和壓縮,我們需要向繪製等腰直角三角形一樣,計算一個合適的變換矩陣,傳入頂點著色器,代碼如下:
@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) { GLES20.glViewport(0,0,width,height); int w=mBitmap.getWidth(); int h=mBitmap.getHeight(); float sWH=w/(float)h; float sWidthHeight=width/(float)height; if(width>height){ if(sWH>sWidthHeight){ Matrix.orthoM(mProjectMatrix, 0, -sWidthHeight*sWH,sWidthHeight*sWH, -1,1, 3, 7); }else{ Matrix.orthoM(mProjectMatrix, 0, -sWidthHeight/sWH,sWidthHeight/sWH, -1,1, 3, 7); } }else{ if(sWH>sWidthHeight){ Matrix.orthoM(mProjectMatrix, 0, -1, 1, -1/sWidthHeight*sWH, 1/sWidthHeight*sWH,3, 7); }else{ Matrix.orthoM(mProjectMatrix, 0, -1, 1, -sWH/sWidthHeight, sWH/sWidthHeight,3, 7); } } //設定相機位置 Matrix.setLookAtM(mViewMatrix, 0, 0, 0, 7.0f, 0f, 0f, 0f, 0f, 1.0f, 0.0f); //計算變換矩陣 Matrix.multiplyMM(mMVPMatrix,0,mProjectMatrix,0,mViewMatrix,0);}
mMVPMatrix即為我們所需要的變換矩陣。
第四步,顯示圖片
然後我們需要做的,就和之前繪製正方形一樣容易了。和之前不同的是,在繪製之前,我們還需要將紋理和紋理座標傳入著色器:
@Overridepublic void onDrawFrame(GL10 gl) { GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT|GLES20.GL_DEPTH_BUFFER_BIT); GLES20.glUseProgram(mProgram); onDrawSet(); GLES20.glUniformMatrix4fv(glHMatrix,1,false,mMVPMatrix,0); GLES20.glEnableVertexAttribArray(glHPosition); GLES20.glEnableVertexAttribArray(glHCoordinate); GLES20.glUniform1i(glHTexture, 0); textureId=createTexture(); //傳入頂點座標 GLES20.glVertexAttribPointer(glHPosition,2,GLES20.GL_FLOAT,false,0,bPos); //傳入紋理座標 GLES20.glVertexAttribPointer(glHCoordinate,2,GLES20.GL_FLOAT,false,0,bCoord); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP,0,4);}public abstract void onDrawSet();public abstract void onDrawCreatedSet(int mProgram);private int createTexture(){ int[] texture=new int[1]; if(mBitmap!=null&&!mBitmap.isRecycled()){ //產生紋理 GLES20.glGenTextures(1,texture,0); //產生紋理 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,texture[0]); //設定縮小過濾為使用紋理中座標最接近的一個像素的顏色作為需要繪製的像素顏色 GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_NEAREST); //設定放大過濾為使用紋理中座標最接近的若干個顏色,通過加權平均演算法得到需要繪製的像素顏色 GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR); //設定環繞方向S,截取紋理座標到[1/2n,1-1/2n]。將導致永遠不會與border融合 GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_CLAMP_TO_EDGE); //設定環繞方向T,截取紋理座標到[1/2n,1-1/2n]。將導致永遠不會與border融合 GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_CLAMP_TO_EDGE); //根據以上指定的參數,產生一個2D紋理 GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, mBitmap, 0); return texture[0]; } return 0;}
這樣我們就可以顯示出我們需要顯示的圖片,並且保證它完整的置中顯示而且不會變形了,如:
源碼
所有的代碼全部在一個項目中,託管在Github上——Android OpenGLES 2.0系列部落格的Demo
歡迎轉載,轉載請保留文章出處。湖廣午王的部落格[http://blog.csdn.net/junzia/article/details/52842816]
(轉)使用OpenGL顯示映像(七)Android OpenGLES2.0——紋理貼圖之顯示圖片