今天要为大家讲的是,怎样使用OpenGL绘制迷人的彩色六边形及实现该过程中使用的一些控件(ToggleButton).
下面我们先来看个效果图:
当我们单击该按钮时,触发了按钮事件,在该事件中,我们设置了投影矩阵,这样图像看起来就会变小写,效果如下:
当我们点击图像,移动时,会得到如下效果:
上面效果怎么实现的呢?下面,来分享下,这个开发过程;
1、创建一个Android项目,命名为GLTast01;
2、修该main.xml布局文件,文件具体代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main_liner"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ToggleButton
android:textOff="正交投影"
android:textOn="透视投影"
android:checked="false"
android:id="@+id/ToggleButton01"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
</ToggleButton>
</LinearLayout>
在这里有必要跟大家讲下,ToggleButton组件,该组件有两个属性,分别为:textOff和textOn。这两个分别是什么用的呢?textOn是设置当你选中该组件时显示的文本。而textOff则是当该组件没有被选中时显示的文本。这里不要弄混了。还有就是checked属性。该属性是设置是否处于选中状态。
3、创建Hexagon.java文件,该文件主要封装了,六边形的一些变量,比如顶点个数,颜色值等。都在这里面设置,具体代码如下:
public class Hexagon {
private IntBuffer mVertexBuffer;//顶点坐标数据缓冲
private IntBuffer mColorBuffer;//顶点着色数据缓冲
private ByteBuffer mIndexBuffer;//顶点构建索引数据缓冲
int vCount=0;//图形顶点数量
int iCount=0;//索引顶点数量
public Hexagon(int zOffset){
//顶点坐标数据的初始化
vCount=7;
final int UNIT_SIZE=10000;
int vertices[]=new int[]{
0*UNIT_SIZE,0*UNIT_SIZE,zOffset*UNIT_SIZE,
2*UNIT_SIZE,3*UNIT_SIZE,zOffset*UNIT_SIZE,
4*UNIT_SIZE,0*UNIT_SIZE,zOffset*UNIT_SIZE,
2*UNIT_SIZE,-3*UNIT_SIZE,zOffset*UNIT_SIZE,
-2*UNIT_SIZE,-3*UNIT_SIZE,zOffset*UNIT_SIZE,
-4*UNIT_SIZE,0*UNIT_SIZE,zOffset*UNIT_SIZE,
-2*UNIT_SIZE,3*UNIT_SIZE,zOffset*UNIT_SIZE
};
//创建顶点坐标数据缓冲
ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
vbb.order(ByteOrder.nativeOrder());//设置字节顺序
mVertexBuffer = vbb.asIntBuffer();//转换为int型缓冲
mVertexBuffer.put(vertices);//向缓冲区中放入顶点坐标数据
mVertexBuffer.position(0);//设置缓冲区起始位置
//顶点着色数据的初始化
final int one = 65535;
int colors[]=new int[]//顶点颜色值数组,每个顶点4个色彩值RGBA
{
0,0,one,0,
0,one,0,0,
0,one,one,0,
one,0,0,0,
one,0,one,0,
one,one,0,0,
one,one,one,0
};
//创建顶点着色数据缓冲
ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length*4);
cbb.order(ByteOrder.nativeOrder());//设置字节顺序
mColorBuffer = cbb.asIntBuffer();//转换为int型缓冲
mColorBuffer.put(colors);//向缓冲区中放入顶点着色数据
mColorBuffer.position(0);//设置缓冲区起始位置
//三角形构造索引数据初始化
iCount=18;
byte indices[]=new byte[]{
0,2,1,
0,3,2,
0,4,3,
0,5,4,
0,6,5,
0,1,6
};
//创建三角形构造索引数据缓冲
mIndexBuffer = ByteBuffer.allocateDirect(indices.length);
mIndexBuffer.put(indices);//向缓冲区中放入三角形构造索引数据
mIndexBuffer.position(0);//设置缓冲区起始位置
}
public void drawSelf(GL10 gl){
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);//启用顶点坐标数组
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);//启用顶点颜色数组
gl.glVertexPointer(//为画笔指定顶点坐标数据
3, //每个顶点的坐标数量为3 xyz
GL10.GL_FIXED, //顶点坐标值的类型为 GL_FIXED
0, //连续顶点坐标数据之间的间隔
mVertexBuffer //顶点坐标数据
);
gl.glColorPointer(//为画笔指定顶点着色数据
4, //设置颜色的组成成分,必须为4—RGBA
GL10.GL_FIXED, //顶点颜色值的类型为 GL_FIXED
0, //连续顶点着色数据之间的间隔
mColorBuffer //顶点着色数据
);
gl.glDrawElements(//索引法绘制图形
GL10.GL_TRIANGLES, //以三角形方式填充
iCount, //一共icount/3个三角形,iCount个顶点
GL10.GL_UNSIGNED_BYTE, //索引值的尺寸
mIndexBuffer //索引值数据
);
}
}
4、创建我们得MySurfaceView视图,该类继承了GLSurfaceView,在该类中包含了一个内部类SceneRenderer,用于绘制屏幕,下面来看下该类的代码如下:
public class MySurfaceView extends GLSurfaceView{ private final float TOUCH_SCALE_FACTOR = 180.0f/320; //角度缩放比例 private SceneRenderer mRenderer; //场景渲染器 public boolean isPerspective=false; //投影标志位 private float mPreviousY; //上次的触控位置Y坐标 public float xAngle=0; //整体绕x轴旋转的角度 public MySurfaceView(Context context) { super(context); mRenderer = new SceneRenderer(); //创建场景渲染器 setRenderer(mRenderer); //设置渲染器 setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);//主动渲染 } //触摸事件回调方法 @Override public boolean onTouchEvent(MotionEvent e) { float y = e.getY(); switch (e.getAction()) { //获取动作 case MotionEvent.ACTION_MOVE: //判断是否是滑动 float dy = y - mPreviousY; //计算触控笔Y位移 xAngle+= dy * TOUCH_SCALE_FACTOR; //设置沿x轴旋转角度 requestRender(); //重绘画面 } mPreviousY = y; //作为上一次触点的Y坐标 return true; } private class SceneRenderer implements GLSurfaceView.Renderer { Hexagon[] ha=new Hexagon[]{ //六边形数组 new Hexagon(0), new Hexagon(-2), new Hexagon(-4), new Hexagon(-6), new Hexagon(-8), new Hexagon(-10), new Hexagon(-12), }; public SceneRenderer(){}//渲染器构造类 @Override public void onDrawFrame(GL10 gl) { gl.glMatrixMode(GL10.GL_PROJECTION);//设置当前矩阵为投影矩阵 gl.glLoadIdentity(); //设置当前矩阵为单位矩阵 float ratio = (float) 320/480;//计算透视投影的比例 if(isPerspective){ gl.glFrustumf(-ratio, ratio, -1, 1, 1f, 10);//调用此方法计算产生透视投影矩阵 } else{ gl.glOrthof(-ratio, ratio, -1, 1, 1, 10);//调用此方法计算产生正交投影矩阵 } gl.glEnable(GL10.GL_CULL_FACE);//设置为打开背面剪裁 gl.glShadeModel(GL10.GL_SMOOTH); //设置着色模型为平滑着色 gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);//清除缓存 gl.glMatrixMode(GL10.GL_MODELVIEW); //设置当前矩阵为模式矩阵 gl.glLoadIdentity(); //设置当前矩阵为单位矩阵 gl.glTranslatef(0, 0f, -1.4f); //沿z轴向远处推 gl.glRotatef(xAngle, 1, 0, 0); //绕x轴旋转制定角度 for(Hexagon th:ha){ th.drawSelf(gl); //循环绘制六边形数组中的每个六边形 } } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { gl.glViewport(0, 0, width, height); //设置视窗大小及位置 } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { gl.glDisable(GL10.GL_DITHER); //关闭抗抖动 gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT,GL10.GL_FASTEST);//设置Hint模式 gl.glClearColor(0,0,0,0); //设置屏幕背景色黑色 gl.glEnable(GL10.GL_DEPTH_TEST); //启用深度测试 } } }
5、下面就剩下,控制文件MyActivity.java文件了。该文件主要是把MySurfaceView视图添加到main.xml中,并处理一些单击事件,代码如下:
public class MyActivity extends Activity { private MySurfaceView mSurfaceView; //声明MySurfaceView对象 /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mSurfaceView = new MySurfaceView(this); //创建 mSurfaceView.requestFocus();//获取焦点 //MySurfaceView对象 mSurfaceView.setFocusableInTouchMode(true);//设置为可触控 LinearLayout ll=(LinearLayout)findViewById(R.id.main_liner); //获得布局引用 ll.addView(mSurfaceView);//在布局中添加MySurfaceView对象 //控制是否打开背面剪裁的ToggleButton ToggleButton tb=(ToggleButton)this.findViewById(R.id.ToggleButton01);//获得按钮引用 tb.setOnCheckedChangeListener(new MyListener()); //为按钮设置监听器 } class MyListener implements OnCheckedChangeListener{ @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { // TODO Auto-generated method stub mSurfaceView.isPerspective=!mSurfaceView.isPerspective;//在正交投影与透视投影之间切换 mSurfaceView.requestRender();//重新绘制 } } }
6、到此整个项目就已开发完毕。单击项目运行,就会得到以上效果;