1.平面图形的绘制


思路:

  1. 首先自己想象一个形状
  2. 3D坐标系统是(左上角-1,1,0  右下角1,-1,0),可以根据手机屏幕宽高调整坐标,防止图形旋转时的拉伸。
  3. 然后根据安卓手机的坐标系统确定每个顶点的坐标。
  4. 所有的坐标都是(x,y,z)形式并且存于一个float[] 。
  5. 由于OpenGLES是用单个三角形组成矩形等其它图形。且每3个点以逆时针方向形成的是一个正面。
  6. 所以需要一个byte[] 来存储这些顶点,并且按照顺序排列。这样就知道有哪些点参与了绘制,并知道这些点是按照何种顺序进行绘制三角形的,然后三角形组成其它图形。
  7. 有了顶点数组float[] 和 面数组byte[] ,但是不能直接使用,需要转化才能在代码中使用
  8. float[] 转化为 FloatBuffer
  9. byte[] 转化为 ByteBuffer
  10. 暂时到这里就结束了,可以直接在代码中使用并绘制图形了。之后还会多一个纹理绘制,之后再讲。

 

2.API介绍


一般实现Renderer接口,重写三个方法,onSurfaceCreated()   onSurfaceChanged()   onDrawFrame()

onSurfaceCreated()中一般做一些初始化设置

@Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        //关闭抗抖动
        gl.glDisable(GL10.GL_DITHER);
        //修正系统透视
        gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT , GL10.GL_FASTEST);
        //设置阴影平滑模式
        gl.glShadeModel(GL10.GL_SMOOTH);
        //启用深度测试 记录Z轴深度
        gl.glEnable(GL10.GL_DEPTH_TEST);
        //设置深度测试的类型
        gl.glDepthFunc(GL10.GL_LEQUAL);
    }

onSurfaceChanged() 常用来初始化3D场景

@Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        //设置3D窗口的大小及位置
        gl.glViewport(0 , 0 , width , height );
        //将矩阵模式设置为投影矩阵
        gl.glMatrixMode(GL10.GL_PROJECTION);
        //初始化单位矩阵
        gl.glLoadIdentity();
        //计算透视窗宽高比
        float ratio = (float)width/height;
        //设置透视视窗的空间大小 默认为 -1,1,-1,1,-1,1
        //gl.glFrustumf(-ratio , ratio , -1 , 1  ,1 , 10);
    }

onDrawFrame() 进行绘制

 @Override
    public void onDrawFrame(GL10 gl) {
        //清除屏幕缓存和深度缓存
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
        //启用顶点坐标数据
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        //设置当前矩形堆栈为模型堆栈
        gl.glMatrixMode(GL10.GL_MODELVIEW);

        //重置当前的模型视图矩阵
        gl.glLoadIdentity();
       //如果需要用颜色填充平面,还需要禁用顶点颜色数据
        gl.glColor4f(0.2f , 1.0f , 0.2f , 0.0f);
        gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
        //绕y轴旋转
        gl.glRotatef(roate , 0f , 1f , 0f);
        //设置顶点位置数据vertextBuffer
        gl.glVertexPointer(3 , GL10.GL_FLOAT , 0 , vertextBuffer);
//绘制面数据faceBuffer
        gl.glDrawElements(GL10.GL_TRIANGLES, faceBuffer.remaining() , GL10.GL_UNSIGNED_BYTE , faceBuffer);


        //停止绘制
        gl.glFinish();
        //停用坐标数据
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);

        roate+=1;
        if (roate == 360){
            roate = 0;
        }
    }

接下来是一个矩形的绘制:

OpenGL ES (2): 绘制平面图形

我们先用0,1,2三个顶点绘制第一个三角形,然后用3,4,5三个顶点绘制第二个三角形。都是逆时针顺序,显示的就是正面。然后两个三角形组成矩形。

有些点在空间上是重复的,在设置顶点数组的时候,可以只设置四个点,然后在面数组中你重复利用4个点就行了,但是为了之后的纹理绘制不出错,因为重复利用顶点经过试验导致发生了错误,所以2个三角形,6个点,每个点都有值,不重复利用顶点,而不是简单的给4个点。

这样你的顶点数组内容应该是:

float[]={

x0 , y0 , z0 , 

x1 , y1 , z1 ,

x2 , y2 , z2 ,

x3 , y3 , z3 ,

x4 , y4 , z4 ,

x5 , y5 , z5

};

面数组的内容应该是:byte[] = {0,1,2,3,4,5}

 

3.代码:


public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        GLSurfaceView view = new GLSurfaceView(this);
        view.setRenderer(new OtherShader(this));

        setContentView(view);

    }
}
public class OtherShader implements GLSurfaceView.Renderer{
    FloatBuffer vertextBuffer;
    ByteBuffer faceBuffer;
    private float roate;
    Context context;

    public OtherShader(Context context) {
        this.context = context;
//转化
        vertextBuffer = floatArray2Buffer(vertex);
        faceBuffer = ByteBuffer.wrap(face);
    }
//顶点
    float[] vertex = new float[]{
            -0.5f , 0.5f , 0f ,
            -0.5f , -0.5f , 0f ,
            0.5f , -0.5f , 0f ,
            -0.5f , 0.5f , 0f ,
            0.5f , -0.5f , 0f ,
            0.5f , 0.5f , 0f
    };
//面
//两个三角形组成一个矩形
    byte[] face = new byte[]{
            0,1,2, //第0,1,2个顶点组成一个三角形
            3,4,5
    };

    //将顶点颜色数组转换为IntBuffer,是OpenGl ES所需要的。也可以不设置顶点颜色数组int[]
    private IntBuffer intArray2Buffer(int[] rect1color) {
        IntBuffer intBuffer;
        //不用该方法得到IntBuffer,因为Android平台限制,Buffer必须为native Buffer,所以要通过allocateDirect()创建
        //并且该Buffer必须是排序的,所以要order()方法进行排序
        //intBuffer = IntBuffer.wrap(rect1color);
        //因为一个int=4字节
        ByteBuffer bb = ByteBuffer.allocateDirect(rect1color.length * 4);
        bb.order(ByteOrder.nativeOrder());
        intBuffer = bb.asIntBuffer();
        intBuffer.put(rect1color);
        intBuffer.position(0); //移到第一个点的数据
        return intBuffer;
    }

    //将顶点位置数组转换为FloatBuffer,是OpenGl ES所需要的
    private FloatBuffer floatArray2Buffer(float[] rect1) {
        FloatBuffer floatBuffer;
        //不用该方法得到FloatBuffer,因为Android平台限制,Buffer必须为native Buffer,所以要通过allocateDirect()创建
        //并且该Buffer必须是排序的,所以要order()方法进行排序
        //floatBuffer = FloatBuffer.wrap(rect1);
        //因为一个int=4字节
        ByteBuffer bb = ByteBuffer.allocateDirect(rect1.length * 4);
        bb.order(ByteOrder.nativeOrder());
        floatBuffer = bb.asFloatBuffer();
        floatBuffer.put(rect1);
        floatBuffer.position(0); //移到第一个点的数据
        return floatBuffer;
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        //关闭抗抖动
        gl.glDisable(GL10.GL_DITHER);
        //修正系统透视
        gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT , GL10.GL_FASTEST);
        //设置阴影平滑模式
        gl.glShadeModel(GL10.GL_SMOOTH);
        //启用深度测试 记录Z轴深度
        gl.glEnable(GL10.GL_DEPTH_TEST);
        //设置深度测试的类型
        gl.glDepthFunc(GL10.GL_LEQUAL);

     
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        //设置3D窗口的大小及位置
        gl.glViewport(0 , 0 , width , height );
        //将矩阵模式设置为投影矩阵
        gl.glMatrixMode(GL10.GL_PROJECTION);
        //初始化单位矩阵
        gl.glLoadIdentity();
        //计算透视窗宽高比
        float ratio = (float)width/height;
        //设置透视视窗的空间大小 默认为 -1,1,-1,1,-1,1
        //gl.glFrustumf(-ratio , ratio , -1 , 1  ,1 , 10); //防止图形旋转拉伸,可在这里调整
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        //清除屏幕缓存和深度缓存
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
        //启用顶点坐标数据
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        //设置当前矩形堆栈为模型堆栈
        gl.glMatrixMode(GL10.GL_MODELVIEW);

        
        //重置当前的模型视图矩阵
        gl.glLoadIdentity();
       //如果需要用颜色填充平面,还需要禁用顶点颜色数据
        gl.glColor4f(0.2f , 1.0f , 0.2f , 0.0f);
        gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
        //绕y轴旋转
        gl.glRotatef(roate , 0f , 1f , 0f);
        //设置顶点位置数据
        gl.glVertexPointer(3 , GL10.GL_FLOAT , 0 , vertextBuffer);
//绘制面
        gl.glDrawElements(GL10.GL_TRIANGLES, faceBuffer.remaining() , GL10.GL_UNSIGNED_BYTE , faceBuffer);


        //停止绘制
        gl.glFinish();
        //停用坐标数据
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);

        roate+=1;
        if (roate == 360){
            roate = 0;
        }
    }


}

OpenGL ES (2): 绘制平面图形

 

之后会讲给这个平面进行纹理贴图.。

 

相关文章: