美文网首页Android开发Android开发
OpenGL ES 学习笔记(2)-复杂图形绘制

OpenGL ES 学习笔记(2)-复杂图形绘制

作者: changer0 | 来源:发表于2018-10-15 08:26 被阅读5次

    结合球体和圆环,学习复杂图形绘制

    1. 球体绘制

    1.1. 思路

    在上一篇笔记中说过,OpenGL可以画点、线和三角形,为了能让球体有立体显示的效果我们采用线带(line_strip)的方式就行绘制.
    要想使用OpenGL画复杂图形,最重要的就是分解图形.
    首先看下图中球体:


    球体

    分析上面的球体, 我们可以首先对球体横向切片,这样就会得到若干个圆台(顶部特殊).


    圆台

    拆分圆台:


    拆分圆台
    当我们拆开圆台之后,在上下圆上打点并连线,形成带状:
    打点连线

    综上,画一个球,实际就是把球拆分成一个个带状拼接起来形成.

    1.2. 求坐标

    通过1.1中的球体可以看出,我们无非就是求得a、b点坐标,然后让OpenGL引擎画出来.
    已知半径R,横向切片次数stack,因为需要横向切开180°,故横向切角步长为stackStep = (π/stack).
    设第i次切片,r0为圆台的半径
    通过上述条件求出,图中 alpha = (-π/2) + (i * stackStep);
    所以, y0 = R * sin(alpha); r0 = R*cos(alpha)
    接下来看圆台的俯视图:


    圆台俯视图

    设设第j次打点, 打点次数为slice,打点范围为360°,故打点的步长为sliceStep = (2π/since);
    故图中的beta = j * sliceStep;
    可轻松求得: x0和y0的坐标:
    x0 = r0 * cos(beta);
    z0 = r0 * sin(beta);
    同样的办法求得b(x1,y1,z1)的坐标.

    1.3. 核心代码

    通过上面分析,很容易编写代码:

    //计算球体坐标
    float R = 0.5f;//球的半径
    int stack = 8;//水平层数
    float stackStep = ((float) (Math.PI / stack));//单位角度值 180度
    int slice = 12;//竖直
    float sliceStep = (float) ((Math.PI*2) / slice);//水平圆递增角度 360度
    
    //上下两个圆的坐标 半径
    float r0, r1, x0, x1, y0, y1, z0, z1;
    //两个切片的夹角
    float alpha0 = 0;
    float alpha1 = 0;
    //切片圆的角度
    float beta = 0;
    
    //顶点坐标
    List<Float> coords = new ArrayList<>();
    //外层循环 水平切片!
    for (int i = 0; i <= stack; i++) {
        alpha0 = (float) (-Math.PI/2 + (i * stackStep));
        alpha1 = (float) (-Math.PI/2 + ((i+1) * stackStep));
        y0 = (float) (R * Math.sin(alpha0));
        r0 = (float) (R * Math.cos(alpha0));
    
        y1 = (float) (R * Math.sin(alpha1));
        r1 = (float) (R * Math.cos(alpha1));
    
        //循环每一层的圆
        for (int j = 0; j <= slice * 2; j++) {
            beta = j * sliceStep;
            x0 = (float) (r0 * Math.cos(beta));
            z0 = -(float) (r0 * Math.sin(beta));
    
            x1 = (float) (r1 * Math.cos(beta));
            z1 = -(float) (r1 * Math.sin(beta));
            coords.add(x0);
            coords.add(y0);
            coords.add(z0);
            coords.add(x1);
            coords.add(y1);
            coords.add(z1);
        }
    }
    FloatBuffer fbb = BufferUtil.list2FloatBuffer(coords);
    gl.glVertexPointer(3, GL10.GL_FLOAT, 0, fbb);
    gl.glDrawArrays(GL10.GL_LINE_STRIP, 0, coords.size()/3);
    

    2. 圆环绘制

    2.1. 思路

    如果球体的绘制屡清楚了,圆环就更不在话下了,同样还是把圆环拆分.
    如下图所示, 圆环就是一个一个圆套在一起形成圆环,我们的目标就是求得a点坐标.
    为了描述清楚我把圆环中的圆单独也抽在坐标系中.


    圆环 抽离坐标系

    2.2. 求坐标

    已知条件,内环圆半径 r, 环中圆半径r'.
    通过2.1图中所示:
    x0 = (r + r' + r' * cos(beta)) * cos(alpha);
    y0 = (r + r' + r' * cos(beta)) * sin(alpha);
    z0 = r' * sin(beta).
    同样的道理再求得下一个点坐标.

    2.3 核心代码

    
    //开始画
    float Rinner = 0.2f; //内环半径
    float Rring = 0.3f; //环半径
    
    int count = 20; //环 圆与圆之间的循环次数
    float alphaStep = (float) ((2 * Math.PI) / count);
    float alpha;
    
    int countRing = 20;//环上圆循环次数
    float betaStep = (float) ((2 * Math.PI) / countRing);
    float beta;
    
    float x0, y0, z0, x1, y1, z1;
    
    List<Float> coordsList = new ArrayList<>();
    
    //外层 圆与圆之间
    for (int i = 0; i < count; i++) {
        alpha = alphaStep * i;
    
        //环上圆的点 (需要闭合)
        for (int j = 0; j <= countRing; j++) {
            beta = betaStep * j;
    
            x0 = (float) ((Rinner + Rring * (1 + Math.cos(beta))) * Math.cos(alpha));
            y0 = (float) ((Rinner + Rring * (1 + Math.cos(beta))) * Math.sin(alpha));
            z0 = -(float) (Rring * Math.sin(beta));
    
            x1 = (float) ((Rinner + Rring * (1 + Math.cos(beta))) * Math.cos(alpha + alphaStep));
            y1 = (float) ((Rinner + Rring * (1 + Math.cos(beta))) * Math.sin(alpha + alphaStep));
            z1 = -(float) (Rring * Math.sin(beta));
    
            coordsList.add(x0);
            coordsList.add(y0);
            coordsList.add(z0);
            coordsList.add(x1);
            coordsList.add(y1);
            coordsList.add(z1);
        }
    }
    
    gl.glVertexPointer(3, GL10.GL_FLOAT, 0, BufferUtil.list2ByteBuffer(coordsList));
    gl.glDrawArrays(GL10.GL_LINE_STRIP, 0, coordsList.size()/3);
    

    2.4 预览图

    效果预览

    3. 项目地址

    GitHub: https://github.com/changer0/OpenGLDemo

    相关文章

      网友评论

      本文标题:OpenGL ES 学习笔记(2)-复杂图形绘制

      本文链接:https://www.haomeiwen.com/subject/ocmaaftx.html