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;
}
}