今天要为大家讲的是,怎样使用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、到此整个项目就已开发完毕。单击项目运行,就会得到以上效果;