Android OpenGL ES开发(六)圆锥、圆柱、球

之前文章中,我们绘制了三角形、正方形、圆形、立方星,今天我们将回执圆锥、圆柱、体。能够绘制这些基本的常规几何形体后,其他常见的几何形体的绘制对我们来说基本没问题了。

圆锥

由之前的文章,我们应该知道了,OpenGL ES中物体的绘制重点就是在于把这个物体表面分解成三角形,分解成功后,绘制自然不成问题了。圆锥我们很容易就想到把它拆解成一个圆形和一个锥面,锥面的顶点跟圆形的圆点,除了锥面的中心点的坐标有了“高度”,其他的完全相同。圆形在之前文章已经绘制过,那么锥面其实对我们来说也是小事。

锥面顶点坐标

其实锥面顶点坐标也就是圆形中,给圆心相对圆边增加高度,使之形成锥面。

        ArrayList data = new ArrayList<>();
        data.add(0.0f);             
        data.add(0.0f);
        data.add(2.0f);         //给圆心相对圆边增加高度,使之形成锥面
        float angDegSpan = 360f / n;
        for (float i = 0; i < 360 + angDegSpan; i += angDegSpan)
        {
            data.add((float) (radius * Math.sin(i * Math.PI / 180f)));
            data.add((float) (radius * Math.cos(i * Math.PI / 180f)));
            data.add(0.0f);
        }
        float[] f = new float[data.size()];
        for (int i = 0; i < f.length; i++)
        {
            f[i] = data.get(i);
        }

我们按照绘制圆形的方式,绘制出锥面,然后再在这个锥面的地步绘制一个圆形,这样我们就可以得到一个圆锥了:

1.jpg

从上图中可以看出,我们绘制的并不是同样的颜色,如果使用同样的颜色,很难看出圆锥的立体效果。这种颜色怎么实现的呢?我们来看它的顶点着色器:

    private final String vertextShaderCode =
            "attribute vec4 vPosition;" +
            "uniform mat4 vMatrix;" +
            "varying vec4 vColor;" +
            "void main() {" +
            "   gl_Position = vMatrix * vPosition;" +
            "   if(vPosition.z!=0.0){"+
            "       vColor=vec4(0.0,0.0,0.0,1.0);"+
            "   }else{"+
            "       vColor=vec4(0.9,0.9,0.9,1.0);"+
            "   }"+
            "}";

在顶点着色器中并没有传入颜色,而是在程序中直接判断进行赋值,当然顶点颜色和定边颜色也可以由外面传入。

具体代码:

public class MySixthRender implements GLSurfaceView.Renderer
{

    private final String vertextShaderCode =
            "attribute vec4 vPosition;" +
                    "uniform mat4 vMatrix;" +
                    "varying vec4 vColor;" +
                    "void main() {" +
                    "   gl_Position = vMatrix * vPosition;" +
                    "   if(vPosition.z!=0.0){"+
                    "       vColor=vec4(0.0,0.0,0.0,1.0);"+
                    "   }else{"+
                    "       vColor=vec4(0.9,0.9,0.9,1.0);"+
                    "   }"+
                    "}";
    private final String fragmentShaderCode =
            "precision mediump float;" +
                    "varying vec4 vColor;" +
                    "void main() {" +
                    "   gl_FragColor = vColor;" +
                    "}";

    private float color[] = {
            1.0f, 1.0f, 1.0f, 1.0f
    };

    private int program;
    private FloatBuffer vertexBuffer1;
    private FloatBuffer vertexBuffer2;
    //投影矩阵
    private final float[] mProjectMatrix = new float[16];
    //相机位置矩阵
    private final float[] mViewMatrix = new float[16];
    //计算变换矩阵
    private final float[] mMVPMatrix = new float[16];
    private float radius = 1.0f; //半径
    private int n = 3;  //切割份数
    private float[] shapePos;


    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config)
    {
        GLES20.glEnable(GLES20.GL_DEPTH_TEST);
        //将背景设置为灰色
        GLES20.glClearColor(0.5f,0.2f,1.0f,1.0f);
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT| GLES20.GL_DEPTH_BUFFER_BIT);

        //锥面所有坐标
        shapePos = createPositions();
        //申请底层空间
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(shapePos.length * 4);
        byteBuffer.order(ByteOrder.nativeOrder());
        //将坐标数据转换为FloatBuffer,用以传入给OpenGL ES程序
        vertexBuffer1 = byteBuffer.asFloatBuffer();
        //将三角形坐标传入FloatBuffer
        vertexBuffer1.put(shapePos);
        vertexBuffer1.position(0);


        //圆形所有坐标
        ByteBuffer byteBuffer2 = ByteBuffer.allocateDirect(shapePos.length * 4);
        byteBuffer2.order(ByteOrder.nativeOrder());
        //将坐标数据转换为FloatBuffer,用以传入给OpenGL ES程序
        vertexBuffer2 = byteBuffer2.asFloatBuffer();
        //将三角形坐标传入FloatBuffer
        shapePos[2] = 0.0f;  //改变锥面顶点高度,变为圆心坐标
        vertexBuffer2.put(shapePos);
        vertexBuffer2.position(0);


        //创建顶点着色器程序
        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertextShaderCode);
        //创建片元着色器程序
        int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);

        if (vertexShader == 0 || fragmentShader == 0)
        {
            return;
        }
        //创建一个空的OpenGL ES程序
        program = GLES20.glCreateProgram();
        //将顶点着色器加入程序
        GLES20.glAttachShader(program, vertexShader);
        //将片元着色器加入程序
        GLES20.glAttachShader(program, fragmentShader);
        //连接到着色器程序中
        GLES20.glLinkProgram(program);
        //使用程序
        GLES20.glUseProgram(program);

    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height)
    {
        GLES20.glViewport(0, 0, width, height);

        float ratio = (float) width / height;

        //设置透视矩阵
        Matrix.frustumM(mProjectMatrix, 0, -ratio, ratio, -1, 1, 3, 20);
        //设置相机位置
        Matrix.setLookAtM(mViewMatrix , 0, 1.0f, -10.0f, -4.0f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
        //计算变换矩阵
        Matrix.multiplyMM(mMVPMatrix, 0, mProjectMatrix, 0, mViewMatrix, 0);
    }

    @Override
    public void onDrawFrame(GL10 gl)
    {
        if (program == 0)
            return;

        //获取变换矩阵vMatrix成员句柄
        int vMatrix = GLES20.glGetUniformLocation(program, "vMatrix");
        //设置vMatrix的值
        GLES20.glUniformMatrix4fv(vMatrix, 1, false, mMVPMatrix, 0);
        //获取顶点着色器的vPosition成员句柄
        int vPosition = GLES20.glGetAttribLocation(program, "vPosition");
        //启用vPosition句柄
        GLES20.glEnableVertexAttribArray(vPosition);
        //传的圆锥所有顶点坐标数据
        GLES20.glVertexAttribPointer(vPosition, 3, GLES20.GL_FLOAT, false, 3 * 4, vertexBuffer1);
        //绘制
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, shapePos.length / 3);
        //传的圆形所有顶点坐标数据
        GLES20.glVertexAttribPointer(vPosition, 3, GLES20.GL_FLOAT, false, 3 * 4, vertexBuffer2);
        //绘制圆形
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, shapePos.length / 3);

        //禁止顶点数组的句柄
        GLES20.glDisableVertexAttribArray(vPosition);

    }

    public int loadShader(int type, String shaderCode)
    {
        //创建空的着色器
        int shader = GLES20.glCreateShader(type);
        //将着色器程序加到着色器中
        GLES20.glShaderSource(shader, shaderCode);
        //编译色器程序
        GLES20.glCompileShader(shader);

        return shader;

    }

    private float[] createPositions()
    {
        ArrayList data = new ArrayList<>();
        data.add(0.0f);
        data.add(0.0f);
        data.add(2.0f);         //给圆心相对圆边增加高度,使之形成锥面
        float angDegSpan = 360f / n;
        for (float i = 0; i < 360 + angDegSpan; i += angDegSpan)
        {
            data.add((float) (radius * Math.sin(i * Math.PI / 180f)));
            data.add((float) (radius * Math.cos(i * Math.PI / 180f)));
            data.add(0.0f);
        }
        float[] f = new float[data.size()];
        for (int i = 0; i < f.length; i++)
        {
            f[i] = data.get(i);
        }
        return f;
    }
}

圆柱

圆柱与圆锥类似,我们可以把圆柱拆分成上下圆面,加上一个圆筒。圆筒我们之前也没有画过,它怎么拆解成三角形呢?我们可以如同拆解东思路来理解圆柱,想想正三棱柱,正八棱柱,正一百棱柱。。。棱越多,就越圆滑,与圆柱越接近,然后再把每个棱面(矩形)拆分成两个三角形就ok。圆筒面的所有顶点为:

        ArrayList cylinder = new ArrayList<>();

        float angDegSpan = 360f / n;
        for (float i = 0; i < 360 + angDegSpan; i += angDegSpan)
        {
            float x = (float) (radius * Math.sin(i * Math.PI / 180f));
            float y = (float) (radius * Math.cos(i * Math.PI / 180f));
            //圆筒坐标
            cylinder.add(x);
            cylinder.add(y);
            cylinder.add(2.0f);

            cylinder.add(x);
            cylinder.add(y);
            cylinder.add(0.0f);

        }
        cylinderShapePos = new float[cylinder.size()];
        for (int i = 0; i < cylinderShapePos.length; i++)
        {
            cylinderShapePos[i] = cylinder.get(i);
        }

顶部圆所有顶点坐标为:

        ArrayList top = new ArrayList<>();

        top.add(0.0f);
        top.add(0.0f);
        top.add(2.0f);

        float angDegSpan = 360f / n;
        for (float i = 0; i < 360 + angDegSpan; i += angDegSpan)
        {
            float x = (float) (radius * Math.sin(i * Math.PI / 180f));
            float y = (float) (radius * Math.cos(i * Math.PI / 180f));
            //顶部圆形坐标
            top.add(x);
            top.add(y);
            top.add(2.0f);

        }
        topShapePos = new float[top.size()];
        for (int i = 0; i < topShapePos.length; i++)
        {
            topShapePos[i] = top.get(i);
        }

底部圆所有顶点坐标为:

        ArrayList bottom = new ArrayList<>();

        bottom.add(0.0f);
        bottom.add(0.0f);
        bottom.add(0.0f);
        float angDegSpan = 360f / n;
        for (float i = 0; i < 360 + angDegSpan; i += angDegSpan)
        {
            float x = (float) (radius * Math.sin(i * Math.PI / 180f));
            float y = (float) (radius * Math.cos(i * Math.PI / 180f));
             //底下圆形坐标
            bottom.add(x);
            bottom.add(y);
            bottom.add(0.0f);
        }
        bottomShapePos = new float[bottom.size()];
        for (int i = 0; i < bottomShapePos.length; i++)
        {
            bottomShapePos[i] = bottom.get(i);
        }

这样就可以绘制圆柱了,绘制圆筒面时要注意绘制方式是GL_TRIANGLE_STRIP。这样我们就可以得到一个圆柱了:


2.jpg

具体代码:

public class MySeventhRender implements GLSurfaceView.Renderer
{

    private final String vertextShaderCode =
            "attribute vec4 vPosition;" +
            "uniform mat4 vMatrix;" +
            "varying vec4 vColor;" +
            "void main() {" +
            "   gl_Position = vMatrix * vPosition;" +
            "   if(vPosition.z!=0.0){"+
            "       vColor=vec4(0.0,0.0,0.0,1.0);"+
            "   }else{"+
            "       vColor=vec4(0.7,0.7,0.7,1.0);"+
            "   }"+
            "}";
    private final String fragmentShaderCode =
            "precision mediump float;" +
            "varying vec4 vColor;" +
            "void main() {" +
            "   gl_FragColor = vColor;" +
            "}";

    private int program;
    private FloatBuffer cylinderVertexBuffer;
    private FloatBuffer bottomVertexBuffer;
    //投影矩阵
    private final float[] mProjectMatrix = new float[16];
    //相机位置矩阵
    private final float[] mViewMatrix = new float[16];
    //计算变换矩阵
    private final float[] mMVPMatrix = new float[16];
    private float radius = 1.0f; //半径
    private int n = 360;  //切割份数
    private float[] cylinderShapePos;
    private float[] bottomShapePos;
    private float[] topShapePos;
    private FloatBuffer topVertexBuffer;


    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config)
    {
        GLES20.glEnable(GLES20.GL_DEPTH_TEST);
        //将背景设置为灰色
        GLES20.glClearColor(0.5f,0.2f,1.0f,1.0f);
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT| GLES20.GL_DEPTH_BUFFER_BIT);

        //顶点坐标
        createPositions();
        //申请底层空间
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(cylinderShapePos.length * 4);
        byteBuffer.order(ByteOrder.nativeOrder());
        //将坐标数据转换为FloatBuffer,用以传入给OpenGL ES程序
        cylinderVertexBuffer = byteBuffer.asFloatBuffer();
        //将三角形坐标传入FloatBuffer
        cylinderVertexBuffer.put(cylinderShapePos);
        cylinderVertexBuffer.position(0);


        ByteBuffer bottomByteBuffer = ByteBuffer.allocateDirect(bottomShapePos.length * 4);
        bottomByteBuffer.order(ByteOrder.nativeOrder());
        //将坐标数据转换为FloatBuffer,用以传入给OpenGL ES程序
        bottomVertexBuffer = bottomByteBuffer.asFloatBuffer();
        //将三角形坐标传入FloatBuffer
        bottomVertexBuffer.put(bottomShapePos);
        bottomVertexBuffer.position(0);

        ByteBuffer topByteBuffer = ByteBuffer.allocateDirect(topShapePos.length * 4);
        topByteBuffer.order(ByteOrder.nativeOrder());
        //将坐标数据转换为FloatBuffer,用以传入给OpenGL ES程序
        topVertexBuffer = topByteBuffer.asFloatBuffer();
        //将三角形坐标传入FloatBuffer
        topVertexBuffer.put(topShapePos);
        topVertexBuffer.position(0);

        //创建顶点着色器程序
        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertextShaderCode);
        //创建片元着色器程序
        int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);

        if (vertexShader == 0 || fragmentShader == 0)
        {
            return;
        }
        //创建一个空的OpenGL ES程序
        program = GLES20.glCreateProgram();
        //将顶点着色器加入程序
        GLES20.glAttachShader(program, vertexShader);
        //将片元着色器加入程序
        GLES20.glAttachShader(program, fragmentShader);
        //连接到着色器程序中
        GLES20.glLinkProgram(program);
        //使用程序
        GLES20.glUseProgram(program);

    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height)
    {
        GLES20.glViewport(0, 0, width, height);

        float ratio = (float) width / height;

        //设置透视矩阵
        Matrix.frustumM(mProjectMatrix, 0, -ratio, ratio, -1, 1, 3, 20);
        //设置相机位置
        Matrix.setLookAtM(mViewMatrix , 0, 1.0f, -10.0f, -4.0f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
        //计算变换矩阵
        Matrix.multiplyMM(mMVPMatrix, 0, mProjectMatrix, 0, mViewMatrix, 0);
    }

    @Override
    public void onDrawFrame(GL10 gl)
    {
        if (program == 0)
            return;

        //获取变换矩阵vMatrix成员句柄
        int vMatrix = GLES20.glGetUniformLocation(program, "vMatrix");
        //设置vMatrix的值
        GLES20.glUniformMatrix4fv(vMatrix, 1, false, mMVPMatrix, 0);
        //获取顶点着色器的vPosition成员句柄
        int vPosition = GLES20.glGetAttribLocation(program, "vPosition");
        //启用vPosition句柄
        GLES20.glEnableVertexAttribArray(vPosition);
        //传的圆筒面的所有坐标数据
        GLES20.glVertexAttribPointer(vPosition, 3, GLES20.GL_FLOAT, false, 3 * 4, cylinderVertexBuffer);
        //绘制圆筒面
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, cylinderShapePos.length / 3);
        //底部圆形的所有坐标数据
        GLES20.glVertexAttribPointer(vPosition, 3, GLES20.GL_FLOAT, false, 3 * 4, bottomVertexBuffer);
        //绘制底部圆形
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, bottomShapePos.length / 3);
        //顶部圆形的所有坐标数据
        GLES20.glVertexAttribPointer(vPosition, 3, GLES20.GL_FLOAT, false, 3 * 4, topVertexBuffer);
        //绘制顶部圆形
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, topShapePos.length / 3);

        //禁止顶点数组的句柄
        GLES20.glDisableVertexAttribArray(vPosition);

    }

    public int loadShader(int type, String shaderCode)
    {
        //创建空的着色器
        int shader = GLES20.glCreateShader(type);
        //将着色器程序加到着色器中
        GLES20.glShaderSource(shader, shaderCode);
        //编译色器程序
        GLES20.glCompileShader(shader);

        return shader;

    }

    private void createPositions()
    {
        ArrayList cylinder = new ArrayList<>();
        ArrayList bottom = new ArrayList<>();
        ArrayList top = new ArrayList<>();

        bottom.add(0.0f);
        bottom.add(0.0f);
        bottom.add(0.0f);

        top.add(0.0f);
        top.add(0.0f);
        top.add(2.0f);

        float angDegSpan = 360f / n;
        for (float i = 0; i < 360 + angDegSpan; i += angDegSpan)
        {
            float x = (float) (radius * Math.sin(i * Math.PI / 180f));
            float y = (float) (radius * Math.cos(i * Math.PI / 180f));
            //圆筒坐标
            cylinder.add(x);
            cylinder.add(y);
            cylinder.add(2.0f);

            cylinder.add(x);
            cylinder.add(y);
            cylinder.add(0.0f);

            //底下圆形坐标
            bottom.add(x);
            bottom.add(y);
            bottom.add(0.0f);
            //顶部圆形坐标
            top.add(x);
            top.add(y);
            top.add(2.0f);

        }
        cylinderShapePos = new float[cylinder.size()];
        for (int i = 0; i < cylinderShapePos.length; i++)
        {
            cylinderShapePos[i] = cylinder.get(i);
        }

        bottomShapePos = new float[bottom.size()];
        for (int i = 0; i < bottomShapePos.length; i++)
        {
            bottomShapePos[i] = bottom.get(i);
        }

        topShapePos = new float[top.size()];
        for (int i = 0; i < topShapePos.length; i++)
        {
            topShapePos[i] = top.get(i);
        }
    }
}

相对于圆锥圆柱来说,球体的拆解就复杂许多了,比较常见的拆解方法是将按照经纬度拆解和按照正多面体拆解,下图分别为正多面体和经纬度拆解.

正多面体拆解

3.jpg

经纬度拆解(每一个小块看作一个矩形,再拆成三角形):

4.jpg

由上图可以明显看出,多面体虽然看起来好看点,但是还是按照经纬度方式来拆解计算容易点,毕竟规律那么明显。

球上点的坐标

无论是按照经纬度拆还是多面体拆,都需要知道球上面点的坐标,这算是基本的几何知识了。以球的中心为坐标中心,球的半径为R的话,那么球上点的坐标(x0,y0,z0)为:

x0 = R * cos(a) * sin(b);
y0 = R * sin(a);
z0 = R * cos(a) * cos(b);

其中,a为圆心到点的线段与xz平面的夹角,b为圆心到点的线段在xz平面的投影与z轴的夹角,如图:


5.png

得到顶点后,剩下的工作就和之前绘制其他图形一样了。
但是如果继续使用圆锥的着色器,我们会得到这样的一个球:


6.jpg

看起来都不太像个球了,要不是有条白线,这是不是个球就不好说了。我们需要修啊下顶点着色器,让它有立体感。把顶点着色器修改为:

    private final String vertextShaderCode =
            "attribute vec4 vPosition;" +
            "uniform mat4 vMatrix;" +
            "varying vec4 vColor;" +
            "float color;" +
            "void main() {" +
            "   gl_Position = vMatrix * vPosition;" +
            "   if(vPosition.z>0.0){" +
            "       color=vPosition.z;" +
            "   }else{" +
            "       color=-vPosition.z;" +
            "   }" +
            "   vColor=vec4(color,color,color,1.0);" +
            "}";

运行一下,我们得到的结果如下图:


7.jpg

具体代码:

public class MyEighthRender implements GLSurfaceView.Renderer
{

    private final String vertextShaderCode =
            "attribute vec4 vPosition;" +
            "uniform mat4 vMatrix;" +
            "varying vec4 vColor;" +
            "float color;" +
            "void main() {" +
            "   gl_Position = vMatrix * vPosition;" +
            "   if(vPosition.z>0.0){" +
            "       color=vPosition.z;" +
            "   }else{" +
            "       color=-vPosition.z;" +
            "   }" +
            "   vColor=vec4(color,color,color,1.0);" +
            "}";
    private final String fragmentShaderCode =
            "precision mediump float;" +
            "varying vec4 vColor;" +
            "void main() {" +
            "   gl_FragColor = vColor;" +
            "}";

    private int program;
    private FloatBuffer vertexBuffer;
    //投影矩阵
    private final float[] mProjectMatrix = new float[16];
    //相机位置矩阵
    private final float[] mViewMatrix = new float[16];
    //计算变换矩阵
    private final float[] mMVPMatrix = new float[16];
    private float[] shapePos;
    private float step = 1f;

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config)
    {
        GLES20.glEnable(GLES20.GL_DEPTH_TEST);
        //将背景设置为灰色
        GLES20.glClearColor(0.5f, 0.2f, 1.0f, 1.0f);
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);

        //顶点坐标
        createPositions();
        //申请底层空间
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(shapePos.length * 4);
        byteBuffer.order(ByteOrder.nativeOrder());
        //将坐标数据转换为FloatBuffer,用以传入给OpenGL ES程序
        vertexBuffer = byteBuffer.asFloatBuffer();
        //将三角形坐标传入FloatBuffer
        vertexBuffer.put(shapePos);
        vertexBuffer.position(0);


        //创建顶点着色器程序
        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertextShaderCode);
        //创建片元着色器程序
        int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);

        if (vertexShader == 0 || fragmentShader == 0)
        {
            return;
        }
        //创建一个空的OpenGL ES程序
        program = GLES20.glCreateProgram();
        //将顶点着色器加入程序
        GLES20.glAttachShader(program, vertexShader);
        //将片元着色器加入程序
        GLES20.glAttachShader(program, fragmentShader);
        //连接到着色器程序中
        GLES20.glLinkProgram(program);
        //使用程序
        GLES20.glUseProgram(program);

    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height)
    {
        GLES20.glViewport(0, 0, width, height);

        float ratio = (float) width / height;

        //设置透视矩阵
        Matrix.frustumM(mProjectMatrix, 0, -ratio, ratio, -1, 1, 3, 20);
        //设置相机位置
        Matrix.setLookAtM(mViewMatrix, 0, 1.0f, -10.0f, -4.0f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
        //计算变换矩阵
        Matrix.multiplyMM(mMVPMatrix, 0, mProjectMatrix, 0, mViewMatrix, 0);
    }

    @Override
    public void onDrawFrame(GL10 gl)
    {
        if (program == 0)
            return;

        //获取变换矩阵vMatrix成员句柄
        int vMatrix = GLES20.glGetUniformLocation(program, "vMatrix");
        //设置vMatrix的值
        GLES20.glUniformMatrix4fv(vMatrix, 1, false, mMVPMatrix, 0);
        //获取顶点着色器的vPosition成员句柄
        int vPosition = GLES20.glGetAttribLocation(program, "vPosition");
        //启用vPosition句柄
        GLES20.glEnableVertexAttribArray(vPosition);
        //传的圆筒面的所有坐标数据
        GLES20.glVertexAttribPointer(vPosition, 3, GLES20.GL_FLOAT, false, 3 * 4, vertexBuffer);
        //绘制圆筒面
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, shapePos.length / 3);


        //禁止顶点数组的句柄
        GLES20.glDisableVertexAttribArray(vPosition);

    }

    public int loadShader(int type, String shaderCode)
    {
        //创建空的着色器
        int shader = GLES20.glCreateShader(type);
        //将着色器程序加到着色器中
        GLES20.glShaderSource(shader, shaderCode);
        //编译色器程序
        GLES20.glCompileShader(shader);

        return shader;

    }

    private void createPositions()
    {
        ArrayList data = new ArrayList<>();
        float r1, r2;
        float h1, h2;
        float sin, cos;
        for (float i = -90; i < 90 + step; i += step)
        {
            r1 = (float) Math.cos(i * Math.PI / 180.0);
            r2 = (float) Math.cos((i + step) * Math.PI / 180.0);
            h1 = (float) Math.sin(i * Math.PI / 180.0);
            h2 = (float) Math.sin((i + step) * Math.PI / 180.0);
            // 固定纬度, 360 度旋转遍历一条纬线
            float step2 = step * 2;
            for (float j = 0.0f; j < 360.0f + step; j += step2)
            {
                cos = (float) Math.cos(j * Math.PI / 180.0);
                sin = (float) Math.sin(j * Math.PI / 180.0);

                data.add(r2 * cos);
                data.add(h2);
                data.add(r2 * sin);
                data.add(r1 * cos);
                data.add(h1);
                data.add(r1 * sin);
            }
        }
        shapePos = new float[data.size()];
        for (int i = 0; i < shapePos.length; i++)
        {
            shapePos[i] = data.get(i);
        }

    }
}

你可能感兴趣的:(Android OpenGL ES开发(六)圆锥、圆柱、球)