Android(安卓)OpenGL ES2.0 绘制多纹理的三棱锥
16lz
2021-01-24
效果
实现
使用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自动背光调节机制分析
- android中往字符串数组动态添加元素
- Android(安卓)OpenGLES2.0(二)——绘制一个三角形
- Android(安卓)基于 Comparator 对象列表数组排序
- 初步了解OpenGL开启摄像头
- Android(安卓)使用ViewPager实现左右循环滑动图片
- Android(安卓)获取外部存储设备列表
- Android(安卓)OpenGL(二) 学习《Android(安卓)3D 游戏开发技术宝
- PHP将数据库查询内容转换为JSON格式且显示中文