openGL中的图元一共有三种:点、线、三角形。
针对线,openGL为我们提供了三种画线的方式:
①画线段(lines):所谓线段就是不相连的一些些线集。
②画线带(line_strip):首尾相连但不闭合的线。
③画线环(line_loop):首尾相连且闭合的线。
首先来实现画线段,这里我们来画一个发散的射线形状,效果图如下:
render的实现类重写的三个方法,前两个一模一样,关键看一下onDrawFrame()方法:
同样的,准备工作是,清除颜色缓冲区,设置绘图颜色,设置矩阵模式,加载单位矩阵,放置眼球位置,如有需要的话还可以设置旋转角度。
那么做好了准备工作就可以正式画射线了,首先要给线段一个长度r,再定义圆面上点的坐标,用list存放各个点坐标,发现点太多不能手动指定,所以用for循环出圆面上各个点的坐标并放进list,在将这个点放进list之前要记得把中心点(0,0,0)先放进list,这样就由两点(中心点及循环出来的圆上的点)确定了一条线,最后,再设置顶点指针和画数组,画数组时候要注意,type是GL10.GL_LINES,first还是0,count为总数除以三。
接下来实现画线带,这次用画线带的方式画一段螺旋线,效果图如下:
实现方式与线段基本类似,只不过不需要原点,再将glDrawArrays()方法中的type参数设置为GL_LINE_STRIP就OK了。
最后附代码
画线段:
public class MyLineRenderer extends AbstractRenderer { 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, 0f, 0f, 5f, 0f, 0f, 0f, 0f, 1f, 0f); //旋转角度,以便更直观查看 // gl.glRotatef(-90,1,0,0);// 绕x轴旋转 (openGL规定,顺时针旋转为负值) // gl.glRotatef(yRotate,1,0,0);// 绕y轴旋转 /** * 计算点的坐标 * @param r 半径 * @param coordsList 坐标集合 * @param x,y,z 每个点的坐标 * @param alpha 角度 * */ float r = 0.5f;//半径 float x = 0f,y = 0f,z = 0f;//点的坐标 List<Float> coordsList = new ArrayList<Float>(); //循环绘制点 for( float alpha = 0f; alpha < Math.PI * 6;alpha = (float) (alpha+Math.PI / 16 )){ x = (float) (Math.cos(alpha) * r); y = (float) (Math.sin(alpha) * r); //添加原点 coordsList.add(0f); coordsList.add(0f); coordsList.add(0f); //添加当前点 coordsList.add(x); coordsList.add(y); coordsList.add(z); } gl.glVertexPointer(3, GL10.GL_FLOAT, 0, BufferUtils.list2ByteBuffer(coordsList)); gl.glDrawArrays( GL10.GL_LINES,0,coordsList.size() / 3 ); } }画线带:
public class MyLineStripRenderer extends AbstractRenderer{ 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, 0f, 0f, 5f, 0f, 0f, 0f, 0f, 1f, 0f); //旋转角度,以便更直观查看 gl.glRotatef(-90, 1, 0, 0);// 绕x轴旋转 (openGL规定,顺时针旋转为负值) gl.glRotatef(yRotate,1,0,0);// 绕y轴旋转 /** * 计算点的坐标 * @param r 半径 * @param coordsList 坐标集合 * @param x,y,z 每个点的坐标 * @param alpha 角度 * */ List<Float> coordsList = new ArrayList<Float>(); //用list存放各个点 float r = 0.5f;//半径 float x = 0f,y = 0f,z = 1.5f;//点的坐标 float zStep = 0.005f;//z轴的步长 float lsize = 1.0f; float lStep = 0.5f; //用for循环出各个点的坐标 for( float alpha = 0f; alpha < Math.PI * 12;alpha = (float) (alpha+Math.PI / 32 )){ x = (float) (Math.cos(alpha) * r); y = (float) (Math.sin(alpha) * r); z = z - zStep; gl.glLineWidth( lsize = lStep+lsize ); coordsList.add(x); coordsList.add(y); coordsList.add(z); } gl.glVertexPointer(3,GL10.GL_FLOAT,0, BufferUtils.list2ByteBuffer(coordsList));//指定顶点指针 /** * 画数组 * 因为coordsList中用3个点存一个坐标, * 所以点的数量要除以3 */ gl.glDrawArrays( GL10.GL_LINE_STRIP,0,coordsList.size() / 3 ); } }需用到的工具类
AbstactRenderer:
public abstract class AbstractRenderer implements GLSurfaceView.Renderer{ private float ratio;//视口比例 public float xRotate = 0f;//绕X轴旋转的角度 public float yRotate = 0f;//绕Y轴旋转的角度 @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { /** * 步骤: * 第一步设置清屏色,第二步启用顶点缓冲数组。 */ //设置清屏色 gl.glClearColor(0f, 0f, 0f, 1f); //启用顶点缓冲数组 gl.glEnableClientState( GL10.GL_VERTEX_ARRAY ); } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { /**步骤: * 第一步设置视口,再设置平截头体,而要设置平截头体就要先指定矩阵类型为投影矩阵 * 设置过矩阵类型后,就马上要加载单位矩阵。 * * 设置平截头体: * 为了视口所展示的画面不失真,一般设置平截头体的比例与视口比例相同。 * 所以就要先计算视口比例,再应用到平截头体中。 */ //设置视口 gl.glViewport(0,0,width,height); //计算视口比例 ratio = (float)width / (float)height; //设置矩阵模式 gl.glMatrixMode(GL10.GL_PROJECTION); //加载单位矩阵 gl.glLoadIdentity(); //设置平截头体 gl.glFrustumf(ratio,-ratio,-1f,1f,3f,7f);//ratio为with/height,所以高度为,宽度为ratio } @Override public abstract void onDrawFrame(GL10 gl); }BufferUtils:
public class BufferUtils { /** * * * 将浮点数组转换成字节缓冲区 * @param arr * @return */ public static ByteBuffer array2ByteBuffer( float [] arr ){ ByteBuffer ibb = ByteBuffer.allocateDirect(arr.length * 4);//一个浮点数等于四个字节 ibb.order(ByteOrder.nativeOrder());//设置排列顺序,本地顺序 FloatBuffer fbb = ibb.asFloatBuffer();//将字节缓冲转换成浮点缓冲 //用增强for循环把每个坐标put进去 fbb.put(arr); ibb.position(0);//把缓冲区指针位置定位到0位置 return ibb; } /** * * * 将list集合转换成字节缓冲区 * @param list * @return */ public static ByteBuffer list2ByteBuffer( List<Float> list){ ByteBuffer ibb = ByteBuffer.allocateDirect(list.size() * 4);//一个浮点数等于四个字节 ibb.order(ByteOrder.nativeOrder());//设置排列顺序,本地顺序 FloatBuffer fbb = ibb.asFloatBuffer();//将字节缓冲转换成浮点缓冲 //用增强for循环把每个坐标put进去 for( Float f : list ){ fbb.put(f); } ibb.position(0);//把缓冲区指针位置定位到0位置 return ibb; } }