學習內容來源and參考java
opengl es 3.0編程指南git
3D圖形渲染最基本的操做之一是對一個表面進行紋理,紋理能夠表現只從網格的幾何形狀中沒法獲得的附加細節。在opengl es3.0中的紋理有多種形式:2D紋理,2D紋理數組,3D紋理以及立方圖紋理。編程
2D紋理是一個圖像數據的二維數組。一個紋理單獨數據元素稱做「紋素」(Texel)。圖像中的每一個紋素根據基本格式和數據類型指定。若是用2D紋理渲染時,紋理座標用做圖像中的索引。2D紋理的紋理座標用一對2D座標(s,t)或者(u,v)來表示,這些座標用於查找一個紋理貼圖的規範化座標。數組
紋理座標以下所示:函數
紋理圖像的左下座標由(0.0,0.0)決定,右上角座標由(1.0,1.0)指定。在[0.0,1.0]以外的座標是容許的,在區間以外的紋理讀取行爲由紋理包裝模式決定。性能
立方圖是由6個單獨2D紋理面組成的紋理。對於立方圖紋理貼圖,通常使用環境貼圖特效,即在物體上的倒影經過使用一個表示環境的立方圖渲染。。一般,生成環境貼圖所用的立方圖經過在場景中央放置一個攝像機,從6個軸方向(+x,-x,+y,-y,+z,-z)捕捉場景圖像並將結果保存在立方體的每一個面上。學習
3D紋理能夠看作2D紋理多個切片的一個數組,用一個三元座標(s,t,r)訪問,r座標選擇3D紋理須要採樣的切片,(s,t)用來讀取每一個切片中的2D貼圖。動畫
2D紋理數組與3D紋理數組類似,可是用途不一樣,通常用來存儲2D圖像的動畫。數組的每一個切片標識紋理動畫的一幀。座標使用與3D紋理相同,都是使用(s,t,r)來表示,r座標選擇2D數組中須要採樣的切片,(s,t)用來選取切片。ui
紋理對象是一個容器對象,保存所須要渲染的紋理數據,如圖像數據,過濾模式以及包裝模式。紋理對象使用一個無符號整數表示,該整數位紋理對象的句柄,生成紋理對象的函數以下:
void glGenTextures(GLsizei n,GLuint *textures);//native 實現 // C function void glGenTextures ( GLsizei n, GLuint *textures ) java層代碼 public static native void glGenTextures( int n, java.nio.IntBuffer textures );
Java代碼中n表示生成紋理對象數量,textures爲一個保存n個紋理對象id的無符號整數數組。建立時,glGenTextures方法生成的紋理對象是一個空的容器,用於加載紋理數據和參數。紋理對象在不使用時候能夠經過調用glDeleteTextures(...)
方法來刪除。
一旦使用glGenTextures(...)
來生成紋理對象id,應用程序就必須經過glBindTexture()
綁定紋理對象進行操做。一旦綁定到一個特定的目標,紋理對象會一直綁定在此目標中直到刪掉爲止。在綁定完成後,須要去加載圖像了,用於加載立方圖紋理和2D的基本函數是glTexImage2D()
。在opengl es3.0中可使用多種代替方法指定2D紋理,包括不可變紋理(glTexStorage2D)以及glTexSubImage2D的結合。爲了獲得最佳性能,推薦使用不可變紋理。在Android中,系統幫咱們封裝了GLUtils來方便咱們使用:
public static void texImage2D(int target, int level, Bitmap bitmap, int border); //target爲將紋理對象綁定到GL_TEXTURE_2D,GL_TEXTURE_3D,GL_TEXTURE_2D_ARRAY,GL_TEXTURE_CUBE_MAP的標識 //level爲指定要加載mip的級別,第一個級別爲0,後續的遞增 //bitmap爲須要加載的圖像 //border在opengl es中忽略,傳0便可
上述方法使用以下:
private void generateTexture(Bitmap bitmap) { int[] size = new int[1]; GLES30.glGenTextures(size.length, size, 0); if (size[0] == 0) { Log.w(TAG, "建立紋理失敗!"); return; } int target = size[0]; GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, target); GLUtils.texImage2D(target, 0, bitmap, 0); GLES30.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); GLES30.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST); }
上述的glTexParameteri(..)
方法將縮小和放大過濾模式設置爲GL_NEAREST
。這個對於紋理貼圖來講是必須的,由於咱們尚未爲紋理加載完整的mip貼圖鏈,所以,必須選擇非mip貼圖縮小過濾器。其餘的模式還有GL_LINEAR,提供雙線性非mip貼圖過濾。
紋理座標用於生成一個2d索引,用來從紋理貼圖中讀取,當縮小和放大的過濾器設置爲GL_NEAREST的時候,一個紋素將在提供的紋理座標位置上讀取,這個稱爲點採樣或者最近採樣。but,使用該方法採樣可能會形成嚴重的視覺僞像,由於三角形在屏幕空間中變得較小,在不一樣像素間的差值,紋理座標可能有很大的跳躍,從而形成從一個大的紋理塗重取得少許樣本,形成鋸齒僞像,而且可能形成巨大的性能損失。
上述的解決辦法能夠經過mip貼圖來解決僞像的問題。其思路是構建一個mip貼圖鏈,開始於原來指定的圖像,後續的每一個圖像在每一個維度上是前一個圖像的通常,一直持續到最後到達鏈底部的1x1紋理。mip貼圖級別能夠變成生成(上述的level參數),一個mip級別中的每一個像素一般根據上一級別中相同位置的4個像素的平均值計算。
紋理渲染時會發生兩種過濾狀況:縮小和放大。
對於放大而言,mip貼圖是不起做用的,由於咱們老是從最大的可用級別進行採樣。對於縮小來講,可使用不一樣的採樣方式。 過濾模式使用glTexParameteri(...)
進行設置:
public static native void glTexParameteri( int target, int pname, int param ); //紋理對象,GL_TEXTURE_2D,GL_TEXTURE_3D,GL_TEXTURE_2D_ARRAY,GL_TEXTURE_CUBE_MAP //pname通常指定爲GL_TEXTURE_MIN_FILTER(縮小),GL_TEXTURE_MAG_FILTER(放大) //param爲採用的過濾模式
過濾模式的採樣過程如圖所示:
GL_LINEAR
GL_NEAREST
更加詳細介紹關於過濾模式的區別能夠查看這篇博客。
opengls es3.0中提供glGenerateMipmap()
方法來自動生成mip貼圖。
public static native void glGenerateMipmap( int target ); //以前生成的紋理對象,GL_TEXTURE_2D,GL_TEXTURE_3D,GL_TEXTURE_2D_ARRAY,GL_TEXTURE_CUBE_MAP
紋理包裝模式用於指定紋理座標超出[0.0,1.0]方位內所發生的行爲。使用glTexParameter[i|f]v來進行設置:
public static native void glTexParameterfv( int target, int pname, java.nio.FloatBuffer params ); // C function void glTexParameteri ( GLenum target, GLenum pname, GLint param ) public static native void glTexParameteri( int target, int pname, int param );
這些模式能夠爲s,t,r座標進行單獨設置,GL_TEXTURE_WRAP_S設置s座標的模式,GL_TEXTURE_WRAP_T設置t座標的模式,GL_TEXTURE_WRAP_R設置r座標的模式。
在opengl es中有三種模式可供使用:
代碼demo
GLES30.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); GLES30.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
這裏的demo不考慮圖像拉伸的問題,旨在理清展現的邏輯
簡單使用2D紋理展現一下效果,首先封裝一個展現紋理的關鍵方法:
代碼位於 https://github.com/JerryChan123/LearnOEL/tree/gl30 的[2D紋理普通貼圖&&2D紋理立方體貼圖]提交當中
private Bitmap mBitmap; public int loadTexture(Context context, int resId) { int[] textureObjIds = new int[1]; GLES30.glGenTextures(1, textureObjIds, 0); if (textureObjIds[0] == 0) { return 0; } BitmapFactory.Options options = new BitmapFactory.Options(); options.inScaled = false; if (mBitmap == null) { mBitmap = BitmapFactory.decodeResource(context.getResources(), resId, options); if (mBitmap == null) { GLES30.glDeleteTextures(1, textureObjIds, 0); return 0; } } GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureObjIds[0]);//bind GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST); GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR); GLUtils.texImage2D(GLES30.GL_TEXTURE_2D, 0, mBitmap, 0); GLES30.glGenerateMipmap(GLES30.GL_TEXTURE_2D); GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0);//unbind return textureObjIds[0]; }
在SurfaceView的onDraw()方法中進行繪製:
... int texCoord = GLES30.glGetAttribLocation(mProgram, "texCoord"); GLES30.glEnableVertexAttribArray(texCoord); GLES30.glVertexAttribPointer(texCoord, 2, GLES30.GL_FLOAT, false, 0, textBuffer); int textureId = loadTexture(mContext, R.drawable.aa); GLES30.glActiveTexture(GLES30.GL_TEXTURE0); GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureId); int uTextureUnitLocation = GLES30.glGetUniformLocation(mProgram, "s_texture"); GLES30.glUniform1i(uTextureUnitLocation, 0); ...
繪製出來圖像以下所示:
附上次繪製立方體的紋理貼圖效果:
代碼地址:https://github.com/JerryChan123/LearnOEL/tree/gl30 提交信息爲[add the texture for cube]