(1)首先需要介绍的是负责按照切分规则生成球面上顶点的坐标,并渲染球体的Ball类,其代码框架如下。
代码位置:见随书光盘中源代码/第6章/Sample6_1/com/bn/Sample6_1目录下的Ball.java。
- 1 package com.bn.Sample6_1; //声明包
- 2 import java.nio.ByteBuffer; //引入相关类
- 3 ……//此处省略了部分类的引入代码,读者可自行查看随书光盘的源代码
- 4 import android.opengl.GLES20; //引入相关类
- 5 public class Ball { //球
- 6 int mProgram; //自定义渲染管线着色器程序id
- 7 int muMVPMatrixHandle; //总变换矩阵引用
- 8 int maPositionHandle; //顶点位置属性引用
- 9 int muRHandle; //球的半径参数引用
- 10 String mVertexShader; //顶点着色器代码脚本
- 11 String mFragmentShader; //片元着色器代码脚本
- 12 FloatBuffer mVertexBuffer; //顶点坐标数据缓冲
- 13 int vCount = 0; //顶点数量
- 14 float yAngle = 0;float xAngle = 0;float zAngle = 0; //绕X、Y、Z轴旋转的角度
- 15 float r = 0.8f; //球的半径
- 16 public Ball(MySurfaceView mv) {
- 17 initVertexData(); //初始化顶点坐标
- 18 initShader(mv); //初始化着色器
- 19 }
- 20 public void initVertexData() {
- 21 ……//此处省略了初始化顶点坐标方法的代码,将在后面步骤中介绍
- 22 }
- 23 public void initShader(MySurfaceView mv) { //初始化着色器的方法
- 24 ……//此处省略了部分代码,读者可自行查看随书光盘的源代码
- 25 //获取着色器程序中球半径参数的引用
- 26 muRHandle = GLES20.glGetUniformLocation(mProgram, "uR");
- 27 }
- 28 public void drawSelf() {
- 29 ……//此处省略了部分代码,读者可自行查看随书光盘的源代码
- 30 GLES20.glUniform1f(muRHandle, r * UNIT_SIZE); //将半径属性传入渲染管线
- 31 GLES20.glVertexAttribPointer( //将顶点属性传入渲染管线
- 32 maPositionHandle, 3, GLES20.GL_FLOAT, false, 3 * 4, mVertexBuffer);
- 33 GLES20.glEnableVertexAttribArray(maPositionHandle);//允许渲染管线顶点位置数据
- 34 GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vCount);//绘制球
- 35 }}
第6-15行声明了一些成员变量,其中大部分都与前面的很多案例类似。最大的区别是增添了着色器中球半径参数的引用muRHandle以及球的半径r。
第23-27行为初始化着色器的initShader方法,其中大部分代码与前面很多的案例类似,主要区别为增加了获取着色器程序中球半径参数引用的代码。
第28-35行为绘制球体的drawSelf方法,与前面案例中此方法的主要区别是增加了将当前球的半径传入渲染管线的代码。
(2)接下来介绍上一步骤中省略的负责初始化顶点的initVertexData方法,其具体代码如下。
代码位置:见随书光盘中源代码/第6章/Sample6_1/com/bn/Sample6_1目录下的Ball.java。
- 1 public void initVertexData() {
- 2 ArrayList<Float> alVertix = new ArrayList<Float>();//存放顶点坐标值的ArrayList
- 3 final int angleSpan = 10; //将球进行单位切分的角度
- 4 for (int vAngle = -90; vAngle < 90; vAnglevAngle = vAngle + angleSpan) { //纬度方向angleSpan度一份
- 5 for (int hAngle = 0; hAngle <= 360; hAnglehAngle = hAngle + angleSpan){ //经度方向angleSpan度一份
- 6 //计算出以当前经度、纬度位置的顶点为左上侧点的四边形4个顶点的坐标
- 7 float x0 = (float) (r * UNIT_SIZE //第1个顶点的坐标
- 8 * Math.cos(Math.toRadians(vAngle)) * Math.cos(Math
- 9 .toRadians(hAngle)));
- 10 float y0 = (float) (r * UNIT_SIZE
- 11 * Math.cos(Math.toRadians(vAngle)) * Math.sin(Math .
- toRadians(hAngle)));
- 12 float z0 = (float) (r * UNIT_SIZE * Math.sin(Math .toRadians(vAngle)));
- 13 float x1 = (float) (r * UNIT_SIZE //第2个顶点的坐标
- 14 * Math.cos(Math.toRadians(vAngle)) * Math.cos(Math
- 15 .toRadians(hAngle + angleSpan)));
- 16 float y1 = (float) (r * UNIT_SIZE
- 17 * Math.cos(Math.toRadians(vAngle)) * Math.sin(Math
- 18 .toRadians(hAngle + angleSpan)));
- 19 float z1 = (float) (r * UNIT_SIZE * Math.sin(Math .toRadians(vAngle)));
- 20 float x2 = (float) (r * UNIT_SIZE //第3个顶点的坐标
- 21 * Math.cos(Math.toRadians(vAngle + angleSpan)) * Math
- 22 .cos(Math.toRadians(hAngle + angleSpan)));
- 23 float y2 = (float) (r * UNIT_SIZE
- 24 * Math.cos(Math.toRadians(vAngle + angleSpan)) * Math
- 25 .sin(Math.toRadians(hAngle + angleSpan)));
- 26 float z2 = (float) (r * UNIT_SIZE * Math.sin(Math .toRadians(vAngle + angleSpan)));
- 27 float x3 = (float) (r * UNIT_SIZE //第4个顶点的坐标
- 28 * Math.cos(Math.toRadians(vAngle + angleSpan)) * Math
- 29 .cos(Math.toRadians(hAngle)));
- 30 float y3 = (float) (r * UNIT_SIZE
- 31 * Math.cos(Math.toRadians(vAngle + angleSpan)) * Math
- 32 .sin(Math.toRadians(hAngle)));
- 33 float z3 = (float) (r * UNIT_SIZE * Math.sin(Math .toRadians(vAngle + angleSpan)));
- 34 //将4个顶点的坐标按照卷绕成两个三角形的需要依次存入列表
- 35 alVertix.add(x1); alVertix.add(y1); alVertix.add(z1);
- 36 alVertix.add(x3); alVertix.add(y3); alVertix.add(z3);
- 37 alVertix.add(x0); alVertix.add(y0); alVertix.add(z0);
- 38 alVertix.add(x1); alVertix.add(y1); alVertix.add(z1);
- 39 alVertix.add(x2); alVertix.add(y2); alVertix.add(z2);
- 40 alVertix.add(x3); alVertix.add(y3); alVertix.add(z3);
- 41 }}
- 42 vCount = alVertix.size() / 3; //顶点的数量为坐标值数量的1/3,因为一个顶点有3个坐标
- 43 float vertices[] = new float[vCount * 3];
- 44 for (int i = 0; i < alVertix.size(); i++) { //将alVertix列表中的坐标值转存到一个float型数组中
- 45 vertices[i] = alVertix.get(i);
- 46 }
- 47 ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4); //创建顶点坐标数据缓冲
- 48 vbb.order(ByteOrder.nativeOrder()); //设置字节顺序
- 49 mVertexBuffer = vbb.asFloatBuffer(); //转换为int型缓冲
- 50 mVertexBuffer.put(vertices); //向缓冲区中放入顶点坐标数据
- 51 mVertexBuffer.position(0); //设置缓冲区起始位置
- 52 }