您好,登錄后才能下訂單哦!
使用前面學(xué)過的技術(shù)已經(jīng)可以利用OpenGL ES構(gòu)建立體圖形,并通過頂點著色器和片元著色器對其進(jìn)行各種變化呢和光照等效果使得三維效果更加真實,實際上我看看到很多的3D游戲漂亮多了,那是因為有各種各樣的漂亮的圖像帶給人很多視覺盛宴,這篇文章在前面的基礎(chǔ)上,增加物體的表面貼圖,使得物體更加好看。
紋理概念
紋理用來表示圖像照片或者說一系列的數(shù)據(jù),使用紋理可以使物體用用更多的細(xì)節(jié)。OpenGL ES 2.0 中有兩種貼圖:二維紋理和立方體紋理。
每個二維紋理都由許多小的紋理元素組成,類似與片元和像素,使用紋理最簡單的方式就是直接從一個圖像加載數(shù)據(jù)。在OpenGL中規(guī)定紋理圖像的左下角由stst坐標(biāo)(0.0,0.0)指定,右上角由stst坐標(biāo)(1.0,1.0)指定,不過超過1.0的坐標(biāo)也是允許的,在該區(qū)間之外的紋理在讀取時的時候由紋理拉伸模式?jīng)Q定。
OpenGL ES 2.0不必是正方形,但是每個維度都應(yīng)該是2的冪
在Android中使用的OpenGL ES的紋理坐標(biāo)系跟官方的紋理坐標(biāo)系統(tǒng)不一樣,在Android中使用官方的紋理坐標(biāo)系統(tǒng),得到的結(jié)果是相反的,而是左上角是stst坐標(biāo)(0.0,0.0)點,右下角是stst坐標(biāo)(1.0,1.0)點。
二維紋理映射的原理
使用紋理就是在紋理圖中進(jìn)行采樣,因此需要將選定的紋理坐標(biāo)穿進(jìn)頂點著色器,經(jīng)過插值在片元著色器中從紋理圖中的指定位置采樣即可,紋理圖的數(shù)據(jù)通過往片元插值器傳遞紋理單元指定的。
紋理對象和紋理加載
創(chuàng)建一個紋理對象,保存渲染所需的紋理數(shù)據(jù),例如圖像數(shù)據(jù)、過濾模式、包裝模式。創(chuàng)建生成紋理對象的函數(shù)
public static native void glGenTextures( int n, // 指定要生成的紋理對象的數(shù)量 int[] textures, // 保存紋理對象ID的數(shù)組 int offset );
紋理對象在應(yīng)用程序中不再使用時,需要刪除。
public static native void glDeleteTextures( int n, // 指定要刪除的紋理數(shù)量 int[] textures, // 保存待刪除的紋理ID的數(shù)組 int offset );
紋理對象的 ID 必須是 glGenTextures 產(chǎn)生的,一旦生成紋理ID,就必須綁定紋理對象才能繼續(xù)進(jìn)行后續(xù)的操作。后續(xù)的操作將影響綁定的紋理對象。一旦紋理被綁定到一個特定的紋理目標(biāo),再刪除之前就一直保持著綁定狀態(tài)。
public static native void glBindTexture( int target, // 綁定紋理對象到目標(biāo) GL_TEXTURE_2D 或 GL_TEXTURE_CUBE_MAP int texture // 要綁定的紋理對象ID );
激活某個紋理單元
public static native void glActiveTexture( int texture // 要激活的紋理單元 );
對這兩個函數(shù)的理解:顯卡中有N個紋理單元(GL_TEXTURE0,GL_TEXTURE1,GL_TEXTURE2…),每個紋理單元中保存著很多紋理目標(biāo)(targetTexture1D,targetTexture2D,targetTexture3D,targetTextureCube…),OpenGL ES 2.0貌似只支持了targetTexture2D和targetTextureCube。
紋理單元TextureUnit的定義如下
struct TextureUnit { GLuint targetTexture1D; GLuint targetTexture2D; GLuint targetTexture3D; GLuint targetTextureCube; ... };
glActiveTexture函數(shù)就是設(shè)置當(dāng)前活動的紋理單元
TextureUnit textureUnits[GL_MAX_TEXTURE_IMAGE_UNITS] GLuint currentTextureUnit = 0; // ... void glActiveTexture(GLenum textureUnit) { currentTextureUnit = textureUnit - GL_TEXTURE0 ; }
glBindTexture函數(shù)就是將紋理對象ID賦值給當(dāng)前活動的紋理單元的對應(yīng)的目標(biāo)紋理。
void glBindTexture(GLenum textureTarget, GLuint textureObject) { TextureUnit *texUnit = &textureUnits[currentTextureUnit]; switch(textureTarget) { case GL_TEXTURE_1D: texUnit->targetTexture1D = textureObject; break; case GL_TEXTURE_2D: texUnit->targetTexture2D = textureObject; break; case GL_TEXTURE_3D: texUnit->targetTexture3D = textureObject; break; case GL_TEXTURE_CUBEMAP: texUnit->targetTextureCube = textureObject; break; } }
獲取一副圖片的紋理數(shù)據(jù)
public static void texImage2D(int target, // 常數(shù)GL_TEXTURE_2D int level, // 表示多級分辨率的紋理圖像的級數(shù),若只有一種分辨率,則level設(shè)為0。 Bitmap bitmap, int border // 邊框,一般設(shè)為0 )
其他紋理選項的設(shè)置使用glTexParameterf系列函數(shù)
public static native void glTexParameterf( int target, int pname, // 設(shè)定的參數(shù),可以是GL_TEXTURE_MAG_FILTER,GL_TEXTURE_MIN_FILTER,GL_TEXTURE_WRAP_S,GL_TEXTURE_WRAP_T float param // 參數(shù)對應(yīng)的值 );
應(yīng)用紋理的例子
對前面的立方體的每個面應(yīng)用一張圖片作為紋理貼圖,效果圖(這個紋理圖是哪個老師來著?)
Rectangle.java
public class Rectangle { private FloatBuffer mVertexBuffer; private int mProgram; private int mPositionHandle; private int muMVPMatrixHandle; private int mColorHandle; private int muMMatrixHandle; private int muLightLocationHandle; private int mTextureCoordHandle; private int textureId; private int muTextureHandle; private Context mContext; public Rectangle(Context context) { this.mContext = context; initVetexData(); } public void initVetexData() { float vertices[] = new float[] { // 頂點 顏色 紋理坐標(biāo) //前面 0, 0, 1, 1,1,1,0, 0.5f, 0.5f, 1, 1, 1, 1,0,0,0, 1.0f, 0.0f, -1, 1, 1, 1,0,0,0, 0.0f, 0.0f, 0, 0, 1, 1,1,1,0, 0.5f, 0.5f, -1, 1, 1, 1,0,0,0, 0.0f, 0.0f, -1,-1, 1, 1,0,0,0, 0.0f, 1.0f, 0, 0, 1, 1,1,1,0, 0.5f, 0.5f, -1,-1, 1, 1,0,0,0, 0.0f, 1.0f, 1,-1, 1, 1,0,0,0, 1.0f, 1.0f, 0, 0, 1, 1,1,1,0, 0.5f, 0.5f, 1,-1, 1, 1,0,0,0, 1.0f, 1.0f, 1, 1, 1, 1,0,0,0, 1.0f, 0.0f, //后面 0, 0,-1, 1,1,1,0, 0.5f, 0.5f, 1, 1,-1, 1,0,0,0, 1.0f, 0.0f, 1,-1,-1, 1,0,0,0, 0.0f, 0.0f, 0, 0,-1, 1,1,1,0, 0.5f, 0.5f, 1,-1,-1, 1,0,0,0, 0.0f, 0.0f, -1,-1,-1, 1,0,0,0, 0.0f, 1.0f, 0, 0,-1, 1,1,1,0, 0.5f, 0.5f, -1,-1,-1, 1,0,0,0, 0.0f, 1.0f, -1, 1,-1, 1,0,0,0, 1.0f, 1.0f, 0, 0,-1, 1,1,1,0, 0.5f, 0.5f, -1, 1,-1, 1,0,0,0, 1.0f, 1.0f, 1, 1,-1, 1,0,0,0, 1.0f, 0.0f, //左面 -1, 0, 0, 1,1,1,0, 0.5f, 0.5f, -1, 1, 1, 1,0,0,0, 1.0f, 0.0f, -1, 1,-1, 1,0,0,0, 0.0f, 0.0f, -1, 0, 0, 1,1,1,0, 0.5f, 0.5f, -1, 1,-1, 1,0,0,0, 0.0f, 0.0f, -1,-1,-1, 1,0,0,0, 0.0f, 1.0f, -1, 0, 0, 1,1,1,0, 0.5f, 0.5f, -1,-1,-1, 1,0,0,0, 0.0f, 1.0f, -1,-1, 1, 1,0,0,0, 1.0f, 1.0f, -1, 0, 0, 1,1,1,0, 0.5f, 0.5f, -1,-1, 1, 1,0,0,0, 1.0f, 1.0f, -1, 1, 1, 1,0,0,0, 1.0f, 0.0f, //右面 1, 0, 0, 1,1,1,0, 0.5f, 0.5f, 1, 1, 1, 1,0,0,0, 1.0f, 0.0f, 1,-1, 1, 1,0,0,0, 0.0f, 0.0f, 1, 0, 0, 1,1,1,0, 0.5f, 0.5f, 1,-1, 1, 1,0,0,0, 0.0f, 0.0f, 1,-1,-1, 1,0,0,0, 0.0f, 1.0f, 1, 0, 0, 1,1,1,0, 0.5f, 0.5f, 1,-1,-1, 1,0,0,0, 0.0f, 1.0f, 1, 1,-1, 1,0,0,0, 1.0f, 1.0f, 1, 0, 0, 1,1,1,0, 0.5f, 0.5f, 1, 1,-1, 1,0,0,0, 1.0f, 1.0f, 1, 1, 1, 1,0,0,0, 1.0f, 0.0f, //上面 0, 1, 0, 1,1,1,0, 0.5f, 0.5f, 1, 1, 1, 1,0,0,0, 1.0f, 0.0f, 1, 1,-1, 1,0,0,0, 0.0f, 0.0f, 0, 1, 0, 1,1,1,0, 0.5f, 0.5f, 1, 1,-1, 1,0,0,0, 0.0f, 0.0f, -1, 1,-1, 1,0,0,0, 0.0f, 1.0f, 0, 1, 0, 1,1,1,0, 0.5f, 0.5f, -1, 1,-1, 1,0,0,0, 0.0f, 1.0f, -1, 1, 1, 1,0,0,0, 1.0f, 1.0f, 0, 1, 0, 1,1,1,0, 0.5f, 0.5f, -1, 1, 1, 1,0,0,0, 1.0f, 1.0f, 1, 1, 1, 1,0,0,0, 1.0f, 0.0f, //下面 0,-1, 0, 1,1,1,0, 0.5f, 0.5f, 1,-1, 1, 1,0,0,0, 1.0f, 0.0f, -1,-1, 1, 1,0,0,0, 0.0f, 0.0f, 0,-1, 0, 1,1,1,0, 0.5f, 0.5f, -1,-1, 1, 1,0,0,0, 0.0f, 0.0f, -1,-1,-1, 1,0,0,0, 0.0f, 1.0f, 0,-1, 0, 1,1,1,0, 0.5f, 0.5f, -1,-1,-1, 1,0,0,0, 0.0f, 1.0f, 1,-1,-1, 1,0,0,0, 1.0f, 1.0f, 0,-1, 0, 1,1,1,0, 0.5f, 0.5f, 1,-1,-1, 1,0,0,0, 1.0f, 1.0f, 1,-1, 1, 1,0,0,0, 1.0f, 0.0f }; ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4); vbb.order(ByteOrder.nativeOrder()); mVertexBuffer = vbb.asFloatBuffer(); mVertexBuffer.put(vertices); mVertexBuffer.position(0); int vertexShader = loaderShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode); int fragmentShader = loaderShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode); mProgram = GLES20.glCreateProgram(); GLES20.glAttachShader(mProgram, vertexShader); GLES20.glAttachShader(mProgram, fragmentShader); GLES20.glLinkProgram(mProgram); mPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition"); mColorHandle = GLES20.glGetAttribLocation(mProgram, "aColor"); mTextureCoordHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord"); muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); muMMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMMatrix"); muLightLocationHandle = GLES20.glGetUniformLocation(mProgram, "uLightLocation"); muTextureHandle = GLES20.glGetUniformLocation(mProgram, "uTexture"); initTexture(); } // 初始化紋理 public void initTexture() { int [] textures = new int[1]; GLES20.glGenTextures(1, textures, 0); textureId = textures[0]; // 激活紋理單元,默認(rèn)激活的就是0號紋理單元 //GLES20.glActiveTexture(GLES20.GL_TEXTURE0); // 將紋理對象ID綁定到當(dāng)前活動的紋理單元0上的GL_TEXTURE_2D目標(biāo) GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId); // 后面對紋理的設(shè)置都是對綁定了的紋理所生效的 //縮小采樣使用最近點采樣 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); //紋理包裹拉伸方式在st軸采用截取拉伸方式,這些設(shè)置指的是對坐標(biāo)范圍超過1的部分的限制 GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_CLAMP_TO_EDGE); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_CLAMP_TO_EDGE); Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.texture); GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0); // 圖片已經(jīng)加載到了顯存,可以回收 bitmap.recycle(); } public void draw() { GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 12*6); } public void setValue(float[] mvpMatrix, float[] mMatrix) { GLES20.glUseProgram(mProgram); mVertexBuffer.position(0); GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_FLOAT, false, (4+3+2) * 4, mVertexBuffer); mVertexBuffer.position(3); GLES20.glVertexAttribPointer(mColorHandle, 4, GLES20.GL_FLOAT, false, (4+3+2) * 4, mVertexBuffer); mVertexBuffer.position(7); GLES20.glVertexAttribPointer(mTextureCoordHandle, 2, GLES20.GL_FLOAT, false, (4+3+2) * 4, mVertexBuffer); GLES20.glEnableVertexAttribArray(mPositionHandle); GLES20.glEnableVertexAttribArray(mColorHandle); GLES20.glEnableVertexAttribArray(mTextureCoordHandle); GLES20.glUniform3f(muLightLocationHandle, 0, 0, 20); GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mvpMatrix, 0); GLES20.glUniformMatrix4fv(muMMatrixHandle, 1, false, mMatrix, 0); // 將使用的紋理單元0傳遞給片元著色器 GLES20.glUniform1i(muTextureHandle, 0); } private int loaderShader(int type, String shaderCode) { int shader = GLES20.glCreateShader(type); GLES20.glShaderSource(shader, shaderCode); GLES20.glCompileShader(shader); return shader; } private String vertexShaderCode = "uniform mat4 uMVPMatrix;" + "attribute vec2 aTextureCoord;" + "varying vec2 vTextureCoord;" + "uniform mat4 uMMatrix;" + "uniform vec3 uLightLocation;" + "attribute vec4 aColor;" + "varying vec4 vColor;" + "varying vec4 vDiffuse;" + "attribute vec3 aPosition;" + "void main(){" + "vec3 normalVectorOrigin = aPosition;" + "vec3 normalVector = normalize((uMMatrix*vec4(normalVectorOrigin,1)).xyz);" + "vec3 vectorLight = normalize(uLightLocation - (uMMatrix * vec4(aPosition,1)).xyz);" + "float factor = max(0.0, dot(normalVector, vectorLight));" + "vDiffuse = factor*vec4(1,1,1,1.0);" + "gl_Position = uMVPMatrix * vec4(aPosition,1);" + "vColor = aColor;" + "vTextureCoord = aTextureCoord;" // 將紋理坐標(biāo)傳到片元著色器,得到更多的插值紋理坐標(biāo) + "}"; private String fragmentShaderCode = "precision mediump float;" + "uniform sampler2D uTexture;" // 這個uniform變量表示了紋理數(shù)據(jù),從java中傳過來的是所在的紋理單元編號 + "varying vec2 vTextureCoord;" + "varying vec4 vColor;" + "varying vec4 vDiffuse;" + "void main(){" + "gl_FragColor = (vColor*vDiffuse + vColor*vec4(0.6,0.6,0.6,1))*texture2D(uTexture, vTextureCoord);" // 在紋理的基礎(chǔ)上還考慮到光照,texture2D函數(shù)用于紋理采樣 + "}"; }
需要注意的還是傳入的頂點的時候數(shù)組里面包含了頂點、顏色和紋理坐標(biāo),因此要用ByteBuffer的position方法定位。
代碼下載
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。