Android OpenGL ES2.0 绘制多纹理的三棱锥

效果


实现

使用openGL ES 2.0,分别画出三棱锥的4个面(包括底面),分别给4个面涂上纹理。

着色器

顶点着色器:
    private final String mVertexShaderWithTexture=
            "attribute vec3 aPosition;\n"
            +"uniform mat4 uMvp;\n"
            +"attribute vec2 aTextureCoord;\n"
            +"varying vec2 vTextureCoord;\n"
            +"void main(){\n"
            +"  vTextureCoord=aTextureCoord;"
            +"  vec4 vertex=vec4(aPosition[0],aPosition[1],aPosition[2],1.0);"
            +"  gl_Position = uMvp*vertex;\n"
            +"}";
aPosition用于存放顶点坐标,uMvp为准换矩阵,aTextureCoord用于存放纹理坐标;
片元着色器:
    private final String mFragmentShaderWithTexture=
            "precision mediump float;\n"
            +"uniform sampler2D uSample;\n"
            +"varying vec2 vTextureCoord;\n"
            +"void main(){\n"
            +"  gl_FragColor =texture2D(uSample,vTextureCoord);\n"
            +"}";

uSample为纹理采集器,vTextureCoord是顶点着色器传过来的经过插值后的顶点坐标。
由内置方法texture2D,通过纹理采集器和对应纹理坐标得到纹理值给gl_FragColor赋值。

创建渲染程序

通过加载着色器源码,编译着色器获取顶点和片元着色器;
创建渲染程序,为其添加着色器,链接程序,从而得到需要使用的渲染程序。
基础的东西,只罗列了步骤。

绘前准备

获取着色器中对应变量的引用:
            mAPositionLoc=GLES20.glGetAttribLocation(mShader,"aPosition");
            mUMvpLoc=GLES20.glGetUniformLocation(mShader,"uMvp");
            mUSamplerLoc=GLES20.glGetUniformLocation(mShader,"uSample");
            mATextureCoordLoc=GLES20.glGetAttribLocation(mShader,"aTextureCoord");
准备数据:
            mVertexFB=transFloat(mVertex);
            parseVertexInd();
            mTextureCoordFB=transFloat(mTextureCoords);
其中,transFloat()将float的数组转换为FloatBuffer;
因为要单独绘制出每个面,parseVertexInd()将顶点索引数组划分为4个索引数组。
    private FloatBuffer transFloat(float[] floatArray){
        ByteBuffer bb = ByteBuffer.allocateDirect(floatArray.length * 4);
        bb.order(ByteOrder.nativeOrder());
        FloatBuffer fb = bb.asFloatBuffer();
        fb.put(floatArray);
        fb.position(0);
        return fb;
    }
    private void parseVertexInd(){
        m1stInd=Arrays.copyOfRange(mVertexInd,0,3);
        m1stSB=transShort(m1stInd);
        m2ndInd=Arrays.copyOfRange(mVertexInd,3,6);
        m2ndSB=transShort(m2ndInd);
        m3rdInd=Arrays.copyOfRange(mVertexInd,6,9);
        m3rdSB=transShort(m3rdInd);
        m4thInd=Arrays.copyOfRange(mVertexInd,9,12);
        m4thSB=transShort(m4thInd);
    }

开始绘制

        GLES20.glUseProgram(mShader);
        GLES20.glEnable(GLES20.GL_CULL_FACE);
        GLES20.glCullFace(GLES20.GL_BACK);
        GLES20.glFrontFace(GLES20.GL_CCW);
使用渲染程序,剔除背面,且设置逆时针方向绘制的面为正面。

设置转换矩阵

            Matrix.setIdentityM(mRotateM,0);
            Matrix.multiplyMM(mRotateM,0,mRotateY,0,mRotateX,0);
            Matrix.multiplyMM(mRotateM,0,mRotateZ,0,mRotateM,0);
            Matrix.setIdentityM(mViewM,0);
            Matrix.setLookAtM(mViewM,0,mEyeX,mEyeY,mEyeZ,mDstX,mDstY,mDstZ,mUpX,mUpY,mUpZ);
            Matrix.setIdentityM(mProjectionM,0);
            Matrix.frustumM(mProjectionM,0,-mFrustumRatio/mDisplayRatio,mFrustumRatio/mDisplayRatio,-mFrustumRatio,
                    mFrustumRatio,mFrustumNear,mFrustumFar);
            Matrix.multiplyMM(mMvpMatrix,0,mViewM,0,mRotateM,0);
            Matrix.multiplyMM(mMvpMatrix,0,mProjectionM,0,mMvpMatrix,0);
mRotateX、mRotateY和mRotateZ是分别绕X、Y和Z轴旋转的矩阵(调用了Matrix.rotateM()方法得到的结果);
Matrix.setLookAtM()设置视线;
Matrix.frustumM()设置透视矩阵。
最终将三种旋转的结果存放在mMvpMatrix中。

创建Textures

            GLES20.glGenTextures(4,mTextureId,0);
            for (int i=0;i<4;i++){
                generateTexture(i);
            }
其中generateTexure()方法为如下:
    private void generateTexture(int index){
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,mTextureId[index]);
        GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT,1);
        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);
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_REPEAT);
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_REPEAT);
        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D,0,mTextures[index],0);
    }
要绘制4个面,且4个面有不同的纹理,则需要4个Texture。

激活纹理单元和纹理坐标

        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glUniform1i(mUSamplerLoc,0);
        GLES20.glVertexAttribPointer(mATextureCoordLoc,2,GLES20.GL_FLOAT,false,0,mTextureCoordFB);
        GLES20.glEnableVertexAttribArray(mATextureCoordLoc);
激活了默认纹理单元0,并将片元着色器的sampler2D对象绑定至该纹理单元。绑定纹理坐标的引用和值,并激活它。

逐面绘制

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,mTextureId[0]);
        GLES20.glDrawElements(GLES20.GL_TRIANGLES,3,GLES20.GL_UNSIGNED_SHORT,m1stSB);

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,mTextureId[1]);
        GLES20.glDrawElements(GLES20.GL_TRIANGLES,3,GLES20.GL_UNSIGNED_SHORT,m2ndSB);

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,mTextureId[2]);
        GLES20.glDrawElements(GLES20.GL_TRIANGLES,3,GLES20.GL_UNSIGNED_SHORT,m3rdSB);

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,mTextureId[3]);
        GLES20.glDrawElements(GLES20.GL_TRIANGLES,3,GLES20.GL_UNSIGNED_SHORT,m4thSB);
绘制每个面前,首先将特定纹理绑定至纹理单元0,让sampler2D对象能采集到正确的数据;
接着调用drawElements方法,drawElements方法将使用之前绑定的顶点数组。注意,最后1个参数是索引数组,可以看到每次绘制时索引数组都是不同的。
这些索引数组是在parseVertexInd()方法中得到的。

完整demo

完整的demo在如下的地址中查找:
https://github.com/lyzirving/TrianglePyramidInAndroid.git
在完整的demo中,我将上述方法封装到了Triangle类中,可以选择通过颜色或是纹理来涂画三棱锥的表面。
上述代码和demo中的代码并不完善,有所缺漏还请大家多多交流!



















你可能感兴趣的:(Android,OpenGL,ES)