每个二维纹理都由许多小的纹理元素组成,类似与片元和像素,使用纹理最简单的方式就是直接从一个图像加载数据。在OpenGL中规定纹理图像的左下角由stst坐标(0.0,0.0)指定,右上角由stst坐标(1.0,1.0)指定,不过超过1.0的坐标也是允许的,在该区间之外的纹理在读取时的时候由纹理拉伸模式决定。
在Android中使用的OpenGL ES的纹理坐标系跟官方的纹理坐标系统不一样,在Android中使用官方的纹理坐标系统,得到的结果是相反的,而是左上角是stst坐标(0.0,0.0)点,右下角是stst坐标(1.0,1.0)点。
使用纹理就是在纹理图中进行采样,因此需要将选定的纹理坐标穿进顶点着色器,经过插值在片元着色器中从纹理图中的指定位置采样即可,纹理图的数据通过往片元插值器传递纹理单元指定的。
纹理对象的 ID 必须是 glGenTextures 产生的,一旦生成纹理ID,就必须绑定纹理对象才能继续进行后续的操作。后续的操作将影响绑定的纹理对象。一旦纹理被绑定到一个特定的纹理目标,再删除之前就一直保持着绑定状态。
对这两个函数的理解:显卡中有N个纹理单元(GL_TEXTURE0,GL_TEXTURE1,GL_TEXTURE2…),每个纹理单元中保存着很多纹理目标(targetTexture1D,targetTexture2D,targetTexture3D,targetTextureCube…),OpenGL ES 2.0貌似只支持了targetTexture2D和targetTextureCube。
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[] { // 顶点 颜色 纹理坐标 //前面 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]; // 激活纹理单元,默认激活的就是0号纹理单元 //GLES20.glActiveTexture(GLES20.GL_TEXTURE0); // 将纹理对象ID绑定到当前活动的纹理单元0上的GL_TEXTURE_2D目标 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId); // 后面对纹理的设置都是对绑定了的纹理所生效的 //缩小采样使用最近点采样 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轴采用截取拉伸方式,这些设置指的是对坐标范围超过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); // 图片已经加载到了显存,可以回收 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;" // 将纹理坐标传到片元着色器,得到更多的插值纹理坐标 + "}"; private String fragmentShaderCode = "precision mediump float;" + "uniform sampler2D uTexture;" // 这个uniform变量表示了纹理数据,从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);" // 在纹理的基础上还考虑到光照,texture2D函数用于纹理采样 + "}";}