1、首先给出的是顶点坐标、纹理坐标和法向量生成的相关代码:
import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import android.opengl.GLES20; //圆面 public class Circle { int mProgram;//自定义渲染管线着色器程序id int muMVPMatrixHandle;//总变换矩阵引用 int maPositionHandle; //顶点位置属性引用 int maTexCoorHandle; //顶点纹理坐标属性引用 int muMMatrixHandle; int maCameraHandle; //摄像机位置属性引用 int maNormalHandle; //顶点法向量属性引用 int maLightLocationHandle;//光源位置属性引用 String mVertexShader;//顶点着色器代码脚本 String mFragmentShader;//片元着色器代码脚本 FloatBuffer mVertexBuffer;//顶点坐标数据缓冲 FloatBuffer mTexCoorBuffer;//顶点纹理坐标数据缓冲 FloatBuffer mNormalBuffer;//顶点法向量数据缓冲 int vCount=0; float xAngle=0;//绕x轴旋转的角度 float yAngle=0;//绕y轴旋转的角度 float zAngle=0;//绕z轴旋转的角度 public Circle(MySurfaceView mv,float scale,float r,int n) { //调用初始化顶点数据的initVertexData方法 initVertexData(scale,r,n); //调用初始化着色器的intShader方法 initShader(mv); } //自定义的初始化顶点数据的方法 public void initVertexData( float scale, //大小 float r, //半径 int n) //切分的份数 { r=r*scale; float angdegSpan=360.0f/n; //顶角的度数 vCount=3*n;//顶点个数,共有n个三角形,每个三角形都有三个顶点 float[] vertices=new float[vCount*3];//坐标数据 float[] textures=new float[vCount*2];//顶点纹理S、T坐标值数组 //坐标数据初始化 int count=0; int stCount=0; for(float angdeg=0;Math.ceil(angdeg)<360;angdeg+=angdegSpan) { double angrad=Math.toRadians(angdeg);//当前弧度 double angradNext=Math.toRadians(angdeg+angdegSpan);//下一弧度 //中心点 vertices[count++]=0;//顶点坐标 vertices[count++]=0; vertices[count++]=0; textures[stCount++]=0.5f;//st坐标 textures[stCount++]=0.5f; //当前点 vertices[count++]=(float) (-r*Math.sin(angrad));//顶点坐标 vertices[count++]=(float) (r*Math.cos(angrad)); vertices[count++]=0; textures[stCount++]=(float) (0.5f-0.5f*Math.sin(angrad));//st坐标 textures[stCount++]=(float) (0.5f-0.5f*Math.cos(angrad)); //下一点 vertices[count++]=(float) (-r*Math.sin(angradNext));//顶点坐标 vertices[count++]=(float) (r*Math.cos(angradNext)); vertices[count++]=0; textures[stCount++]=(float) (0.5f-0.5f*Math.sin(angradNext));//st坐标 textures[stCount++]=(float) (0.5f-0.5f*Math.cos(angradNext)); } ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);//创建顶点坐标数据缓冲 vbb.order(ByteOrder.nativeOrder());//设置字节顺序为本地操作系统顺序 mVertexBuffer = vbb.asFloatBuffer();//转换为float型缓冲 mVertexBuffer.put(vertices);//向缓冲区中放入顶点坐标数据 mVertexBuffer.position(0);//设置缓冲区起始位置 //法向量数据初始化 float[] normals=new float[vertices.length]; for(int i=0;i<normals.length;i+=3){ normals[i]=0; normals[i+1]=0; normals[i+2]=1; } ByteBuffer nbb = ByteBuffer.allocateDirect(normals.length*4);//创建顶点法向量数据缓冲 nbb.order(ByteOrder.nativeOrder());//设置字节顺序为本地操作系统顺序 mNormalBuffer = nbb.asFloatBuffer();//转换为float型缓冲 mNormalBuffer.put(normals);//向缓冲区中放入顶点法向量数据 mNormalBuffer.position(0);//设置缓冲区起始位置 //纹理坐标数据初始化 ByteBuffer cbb = ByteBuffer.allocateDirect(textures.length*4);//创建顶点纹理数据缓冲 cbb.order(ByteOrder.nativeOrder());//设置字节顺序为本地操作系统顺序 mTexCoorBuffer = cbb.asFloatBuffer();//转换为float型缓冲 mTexCoorBuffer.put(textures);//向缓冲区中放入顶点纹理数据 mTexCoorBuffer.position(0);//设置缓冲区起始位置 } //自定义初始化着色器initShader方法 public void initShader(MySurfaceView mv){ //加载顶点着色器的脚本内容 mVertexShader=ShaderUtil.loadFromAssetsFile("vertex_tex_light.sh", mv.getResources()); //加载片元着色器的脚本内容 mFragmentShader=ShaderUtil.loadFromAssetsFile("frag_tex_light.sh", mv.getResources()); //基于顶点着色器与片元着色器创建程序 mProgram = createProgram(mVertexShader, mFragmentShader); //获取程序中顶点位置属性引用id maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition"); //获取程序中顶点纹理坐标属性引用id maTexCoorHandle= GLES20.glGetAttribLocation(mProgram, "aTexCoor"); //获取程序中总变换矩阵引用id muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); //获取程序中顶点法向量属性引用id maNormalHandle= GLES20.glGetAttribLocation(mProgram, "aNormal"); //获取程序中摄像机位置引用id maCameraHandle=GLES20.glGetUniformLocation(mProgram, "uCamera"); //获取程序中光源位置引用id maLightLocationHandle=GLES20.glGetUniformLocation(mProgram, "uLightLocation"); //获取位置、旋转变换矩阵引用id muMMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMMatrix"); } public void drawSelf(int texId) { //制定使用某套shader程序 GLES20.glUseProgram(mProgram); //将最终变换矩阵传入shader程序 GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, MatrixState.getFinalMatrix(), 0); //将位置、旋转变换矩阵传入shader程序 GLES20.glUniformMatrix4fv(muMMatrixHandle, 1, false, MatrixState.getMMatrix(), 0); //将摄像机位置传入shader程序 GLES20.glUniform3fv(maCameraHandle, 1, MatrixState.cameraFB); //将光源位置传入shader程序 GLES20.glUniform3fv(maLightLocationHandle, 1, MatrixState.lightPositionFB); //传送顶点位置数据 GLES20.glVertexAttribPointer ( maPositionHandle, 3, GLES20.GL_FLOAT, false, 3*4, mVertexBuffer ); //传送顶点纹理坐标数据 GLES20.glVertexAttribPointer ( maTexCoorHandle, 2, GLES20.GL_FLOAT, false, 2*4, mTexCoorBuffer ); //传送顶点法向量数据 GLES20.glVertexAttribPointer ( maNormalHandle, 4, GLES20.GL_FLOAT, false, 3*4, mNormalBuffer ); //启用顶点位置数据 GLES20.glEnableVertexAttribArray(maPositionHandle); //启用顶点纹理数据 GLES20.glEnableVertexAttribArray(maTexCoorHandle); //启用顶点法向量数据 GLES20.glEnableVertexAttribArray(maNormalHandle); //绑定纹理 GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texId); //绘制纹理矩形 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, vCount); } }2、接着给出的是柱面顶点坐标、纹理坐标和法向量生成的相关代码:
import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import android.opengl.GLES20; //圆柱侧面 public class CylinderSide { int mProgram;//自定义渲染管线着色器程序id int muMVPMatrixHandle;//总变换矩阵引用 int maPositionHandle; //顶点位置属性引用 int maTexCoorHandle; //顶点纹理坐标属性引用 int muMMatrixHandle;//位置、旋转、缩放变换矩阵 int maCameraHandle; //摄像机位置属性引用 int maNormalHandle; //顶点法向量属性引用 int maLightLocationHandle;//光源位置属性引用 String mVertexShader;//顶点着色器代码脚本 String mFragmentShader;//片元着色器代码脚本 FloatBuffer mVertexBuffer;//顶点坐标数据缓冲 FloatBuffer mTexCoorBuffer;//顶点纹理坐标数据缓冲 FloatBuffer mNormalBuffer;//顶点法向量数据缓冲 int vCount=0; float xAngle=0;//绕x轴旋转的角度 float yAngle=0;//绕y轴旋转的角度 float zAngle=0;//绕z轴旋转的角度 public CylinderSide(MySurfaceView mv,float scale,float r,float h,int n) { //调用初始化顶点数据的initVertexData方法 initVertexData(scale,r,h,n); //调用初始化着色器的intShader方法 initShader(mv); } //自定义初始化顶点坐标数据的方法 public void initVertexData( float scale, //大小 float r, //半径 float h, //高度 int n //切分的份数 ) { r=scale*r; h=scale*h; float angdegSpan=360.0f/n; vCount=3*n*4;//顶点个数,共有3*n*4个三角形,每个三角形都有三个顶点 //坐标数据初始化 float[] vertices=new float[vCount*3]; float[] textures=new float[vCount*2];//顶点纹理S、T坐标值数组 //坐标数据初始化 int count=0; int stCount=0; for(float angdeg=0;Math.ceil(angdeg)<360;angdeg+=angdegSpan)//侧面 { double angrad=Math.toRadians(angdeg);//当前弧度 double angradNext=Math.toRadians(angdeg+angdegSpan);//下一弧度 //底圆当前点---0 vertices[count++]=(float) (-r*Math.sin(angrad)); vertices[count++]=0; vertices[count++]=(float) (-r*Math.cos(angrad)); textures[stCount++]=(float) (angrad/(2*Math.PI));//st坐标 textures[stCount++]=1; //顶圆下一点---3 vertices[count++]=(float) (-r*Math.sin(angradNext)); vertices[count++]=h; vertices[count++]=(float) (-r*Math.cos(angradNext)); textures[stCount++]=(float) (angradNext/(2*Math.PI));//st坐标 textures[stCount++]=0; //顶圆当前点---2 vertices[count++]=(float) (-r*Math.sin(angrad)); vertices[count++]=h; vertices[count++]=(float) (-r*Math.cos(angrad)); textures[stCount++]=(float) (angrad/(2*Math.PI));//st坐标 textures[stCount++]=0; //底圆当前点---0 vertices[count++]=(float) (-r*Math.sin(angrad)); vertices[count++]=0; vertices[count++]=(float) (-r*Math.cos(angrad)); textures[stCount++]=(float) (angrad/(2*Math.PI));//st坐标 textures[stCount++]=1; //底圆下一点---1 vertices[count++]=(float) (-r*Math.sin(angradNext)); vertices[count++]=0; vertices[count++]=(float) (-r*Math.cos(angradNext)); textures[stCount++]=(float) (angradNext/(2*Math.PI));//st坐标 textures[stCount++]=1; //顶圆下一点---3 vertices[count++]=(float) (-r*Math.sin(angradNext)); vertices[count++]=h; vertices[count++]=(float) (-r*Math.cos(angradNext)); textures[stCount++]=(float) (angradNext/(2*Math.PI));//st坐标 textures[stCount++]=0; } //法向量数据初始化 float[] normals=new float[vertices.length]; for(int i=0;i<vertices.length;i++){ if(i%3==1){ normals[i]=0; }else{ normals[i]=vertices[i]; } } ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);//创建顶点坐标数据缓冲 vbb.order(ByteOrder.nativeOrder());//设置字节顺序为本地操作系统顺序 mVertexBuffer = vbb.asFloatBuffer();//转换为float型缓冲 mVertexBuffer.put(vertices);//向缓冲区中放入顶点坐标数据 mVertexBuffer.position(0);//设置缓冲区起始位置 ByteBuffer nbb = ByteBuffer.allocateDirect(vertices.length*4);//创建顶点法向量数据缓冲 nbb.order(ByteOrder.nativeOrder());//设置字节顺序为本地操作系统顺序 mNormalBuffer = nbb.asFloatBuffer();//转换为float型缓冲 mNormalBuffer.put(normals);//向缓冲区中放入顶点法向量数据 mNormalBuffer.position(0);//设置缓冲区起始位置 //st坐标数据初始化 ByteBuffer cbb = ByteBuffer.allocateDirect(textures.length*4);//创建顶点纹理数据缓冲 cbb.order(ByteOrder.nativeOrder());//设置字节顺序为本地操作系统顺序 mTexCoorBuffer = cbb.asFloatBuffer();//转换为float型缓冲 mTexCoorBuffer.put(textures);//向缓冲区中放入顶点纹理数据 mTexCoorBuffer.position(0);//设置缓冲区起始位置 } //自定义初始化着色器的initShader方法 public void initShader(MySurfaceView mv) { //加载顶点着色器的脚本内容 mVertexShader=ShaderUtil.loadFromAssetsFile("vertex_tex_light.sh", mv.getResources()); //加载片元着色器的脚本内容 mFragmentShader=ShaderUtil.loadFromAssetsFile("frag_tex_light.sh", mv.getResources()); //基于顶点着色器与片元着色器创建程序 mProgram = createProgram(mVertexShader, mFragmentShader); //获取程序中顶点位置属性引用id maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition"); //获取程序中顶点纹理坐标属性引用id maTexCoorHandle= GLES20.glGetAttribLocation(mProgram, "aTexCoor"); //获取程序中总变换矩阵引用id muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); //获取程序中顶点法向量属性引用id maNormalHandle= GLES20.glGetAttribLocation(mProgram, "aNormal"); //获取程序中摄像机位置引用id maCameraHandle=GLES20.glGetUniformLocation(mProgram, "uCamera"); //获取程序中光源位置引用id maLightLocationHandle=GLES20.glGetUniformLocation(mProgram, "uLightLocation"); //获取位置、旋转变换矩阵引用id muMMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMMatrix"); } public void drawSelf(int texId) { //制定使用某套shader程序 GLES20.glUseProgram(mProgram); //将最终变换矩阵传入shader程序 GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, MatrixState.getFinalMatrix(), 0); //将位置、旋转变换矩阵传入shader程序 GLES20.glUniformMatrix4fv(muMMatrixHandle, 1, false, MatrixState.getMMatrix(), 0); //将摄像机位置传入shader程序 GLES20.glUniform3fv(maCameraHandle, 1, MatrixState.cameraFB); //将光源位置传入shader程序 GLES20.glUniform3fv(maLightLocationHandle, 1, MatrixState.lightPositionFB); //传送顶点位置数据 GLES20.glVertexAttribPointer ( maPositionHandle, 3, GLES20.GL_FLOAT, false, 3*4, mVertexBuffer ); //传送顶点纹理坐标数据 GLES20.glVertexAttribPointer ( maTexCoorHandle, 2, GLES20.GL_FLOAT, false, 2*4, mTexCoorBuffer ); //传送顶点法向量数据 GLES20.glVertexAttribPointer ( maNormalHandle, 4, GLES20.GL_FLOAT, false, 3*4, mNormalBuffer ); //启用顶点位置数据 GLES20.glEnableVertexAttribArray(maPositionHandle); //启用顶点纹理数据 GLES20.glEnableVertexAttribArray(maTexCoorHandle); //启用顶点法向量数据 GLES20.glEnableVertexAttribArray(maNormalHandle); //绑定纹理 GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texId); //绘制纹理矩形 GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vCount); } }
3、最后给出圆面和柱面组装成柱体的代码:
public class Cylinder { Circle bottomCircle;//底圆 Circle topCircle;//顶圆 CylinderSide cylinderSide;//侧面 float xAngle=0;//绕x轴旋转的角度 float yAngle=0;//绕y轴旋转的角度 float zAngle=0;//绕z轴旋转的角度 float h; float scale; int topTexId; //顶面纹理 int BottomTexId; //底面纹理 int sideTexId; //侧面纹理 public Cylinder(MySurfaceView mySurfaceView,float scale,float r, float h,int n, int topTexId, int BottomTexId, int sideTexId) { this.h=h; this.scale=scale; this.topTexId=topTexId; this.BottomTexId=BottomTexId; this.sideTexId=sideTexId; topCircle=new Circle(mySurfaceView,scale,r,n); //创建顶面圆对象 bottomCircle=new Circle(mySurfaceView,scale,r,n); //创建底面圆对象 cylinderSide=new CylinderSide(mySurfaceView,scale,r,h,n); //创建侧面无顶圆柱对象 } public void drawSelf() { MatrixState.rotate(xAngle, 1, 0, 0); MatrixState.rotate(yAngle, 0, 1, 0); MatrixState.rotate(zAngle, 0, 0, 1); //顶面 MatrixState.pushMatrix(); MatrixState.translate(0, h/2*scale, 0); MatrixState.rotate(-90, 1, 0, 0); topCircle.drawSelf(topTexId); MatrixState.popMatrix(); //底面 MatrixState.pushMatrix(); MatrixState.translate(0, -h/2*scale, 0); MatrixState.rotate(90, 1, 0, 0); MatrixState.rotate(180, 0, 0, 1); bottomCircle.drawSelf(BottomTexId); MatrixState.popMatrix(); //侧面 MatrixState.pushMatrix(); MatrixState.translate(0, -h/2*scale, 0); cylinderSide.drawSelf(sideTexId); MatrixState.popMatrix(); } }