效果

实现
使用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中的代码并不完善,有所缺漏还请大家多多交流!