顶点变换的管线:
源顶点数据----------------->经过变换的视图坐标------------->剪裁坐标--------------->经过规范化的设备坐标------------->窗口坐标
我们要绘制一个球体,那么就要先把球体切开,先水平切成几部分,把其中的一部分拿出来是一条带状,将这部分切开将会是一个平行四边形带,如下图。
需要注意的是:因为openGL中剔除表面时通常剔除背面,而绘制三角形过程中,采用画三角形带的方法,如果按照0-->1-->2-->0 1-->2-->3-->1 的方式绘制的话就会导致所绘制的三角形带一个逆时针,一个顺时针,而剔除时,就会把其中一个剔除掉,所以openGL在绘制三角形带时,实际的做法是:0-->1-->2-->0 2-->1-->3-->2,这样就可以保证所有的三角形全部为逆时针方向绘制。
那么这个平行四边形可以用画三角形带的方法画出来,那么如果分的份数够多(微积分的思想),且把每一个部分都以平行四边形的方式画出来,那么球体就可以实现,如下图:
首先是坐标的计算:
假如以球心圆点为坐标原点,那么水平切成的几个部分的Y坐标可以确定,且不同。而在内循环绘制每个圆面的时候的X、Z坐标是可以确定且不同的,所以,给定圆的半径R,就能用双循环的方式绘制出球体的各个点坐标。
float R = 0.7f;//球的半径 int statck = 20;//statck:切片----把球体横向切成几部分 float statckStep = (float) (Math.PI / statck);//单位角度值 int slice = 50;//纵向切几部分 float sliceStep = (float) (Math.PI / slice);//水平圆递增的角度 float r0,r1,x0,x1,y0,y1,z0,z1; //r0、r1为圆心引向两个临近切片部分表面的两条线 (x0,y0,z0)和(x1,y1,z1)为临近两个切面的点。 float alpha0 = 0,alpha1 = 0; //前后两个角度 float beta = 0; //切片平面上的角度 List坐标确定了之后剩下就简单了,一样的,先要设置清屏色,设置绘图颜色,然后指定模型视图矩阵,加载单位矩阵,放置眼球位置,设置旋转角度。最后再指定顶点指针,绘制三角形带。coordsList = new ArrayList (); //外层循环 for( int i = 0;i < statck;i++ ){ alpha0 = (float) (- Math.PI / 2 + (i*statckStep)); alpha1 = (float) (- Math.PI / 2 + ((i+1)*statckStep)); y0 = (float) (R * Math.sin(alpha0)); r0 = (float) (R * Math.cos(alpha0)); y1 = (float) (R * Math.sin(alpha1)); r1 = (float) (R * Math.cos(alpha1)); //循环每一层圆 for( int j = 0;j <= (slice * 2);j ++ ){ beta = j * sliceStep; x0 = (float) (r0 * Math.cos(beta)); z0 = -(float) (r0 * Math.sin(beta)); x1 = (float) (r1 * Math.cos(beta)); z1 = -(float) (r1 * Math.sin(beta)); coordsList.add(x0); coordsList.add(y0); coordsList.add(z0); coordsList.add(x1); coordsList.add(y1); coordsList.add(z1); }
运行效果图:
注:为方便观察,这个图是用画线带的方式绘制的,想要画球体该用画三角形带的方法。
最后附代码:
public class MySphereRenderer extends AbstractRenderer{ @Override public void onDrawFrame(GL10 gl) { gl.glClear(GL10.GL_COLOR_BUFFER_BIT);//设置清屏色 gl.glColor4f(1f, 1f, 1f, 1f);//设置绘图颜色 gl.glMatrixMode(GL10.GL_MODELVIEW);//模型视图矩阵 gl.glLoadIdentity();//加载单位矩阵 GLU.gluLookAt(gl, 0, 0, 5, 0, 0, 0, 0, 1, 0);//放置眼球位置 gl.glRotatef(xRotate, 1, 0, 0);//x轴旋转角度 gl.glRotatef(yRotate,0,1,0);//y轴旋转角度 /***********************计算球体坐标****************************/ float R = 0.7f;//球的半径 int statck = 20;//statck:切片----把球体横向切成几部分 float statckStep = (float) (Math.PI / statck);//单位角度值 int slice = 50;//纵向切几部分 float sliceStep = (float) (Math.PI / slice);//水平圆递增的角度 float r0,r1,x0,x1,y0,y1,z0,z1; //r0、r1为圆心引向两个临近切片部分表面的两条线 (x0,y0,z0)和(x1,y1,z1)为临近两个切面的点。 float alpha0 = 0,alpha1 = 0; //前后两个角度 float beta = 0; //切片平面上的角度 ListcoordsList = new ArrayList (); //外层循环 for( int i = 0;i < statck;i++ ){ alpha0 = (float) (- Math.PI / 2 + (i*statckStep)); alpha1 = (float) (- Math.PI / 2 + ((i+1)*statckStep)); y0 = (float) (R * Math.sin(alpha0)); r0 = (float) (R * Math.cos(alpha0)); y1 = (float) (R * Math.sin(alpha1)); r1 = (float) (R * Math.cos(alpha1)); //循环每一层圆 for( int j = 0;j <= (slice * 2);j ++ ){ beta = j * sliceStep; x0 = (float) (r0 * Math.cos(beta)); z0 = -(float) (r0 * Math.sin(beta)); x1 = (float) (r1 * Math.cos(beta)); z1 = -(float) (r1 * Math.sin(beta)); coordsList.add(x0); coordsList.add(y0); coordsList.add(z0); coordsList.add(x1); coordsList.add(y1); coordsList.add(z1); } //指定顶点指针 gl.glVertexPointer(3,GL10.GL_FLOAT,0, BufferUtils.list2FloatBuffer(coordsList)); //若要画球体,应以画三角形带的方式绘制,为方便观察,此处用画线带的方式 gl.glDrawArrays(GL10.GL_LINE_STRIP,0,coordsList.size() / 3); } } }