Android OpenGL ES開發(fā)(六)圓錐、圓柱、球

之前文章中,我們繪制了三角形、正方形、圓形、立方星,今天我們將回執(zhí)圓錐、圓柱、體。能夠繪制這些基本的常規(guī)幾何形體后,其他常見的幾何形體的繪制對(duì)我們來說基本沒問題了。

圓錐

由之前的文章,我們應(yīng)該知道了,OpenGL ES中物體的繪制重點(diǎn)就是在于把這個(gè)物體表面分解成三角形,分解成功后,繪制自然不成問題了。圓錐我們很容易就想到把它拆解成一個(gè)圓形和一個(gè)錐面,錐面的頂點(diǎn)跟圓形的圓點(diǎn),除了錐面的中心點(diǎn)的坐標(biāo)有了“高度”,其他的完全相同。圓形在之前文章已經(jīng)繪制過,那么錐面其實(shí)對(duì)我們來說也是小事。

錐面頂點(diǎn)坐標(biāo)

其實(shí)錐面頂點(diǎn)坐標(biāo)也就是圓形中,給圓心相對(duì)圓邊增加高度,使之形成錐面。

        ArrayList<Float> data = new ArrayList<>();
        data.add(0.0f);             
        data.add(0.0f);
        data.add(2.0f);         //給圓心相對(duì)圓邊增加高度,使之形成錐面
        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);
        }

我們按照繪制圓形的方式,繪制出錐面,然后再在這個(gè)錐面的地步繪制一個(gè)圓形,這樣我們就可以得到一個(gè)圓錐了:

1.jpg

從上圖中可以看出,我們繪制的并不是同樣的顏色,如果使用同樣的顏色,很難看出圓錐的立體效果。這種顏色怎么實(shí)現(xiàn)的呢?我們來看它的頂點(diǎn)著色器:

    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);"+
            "   }"+
            "}";

在頂點(diǎn)著色器中并沒有傳入顏色,而是在程序中直接判斷進(jìn)行賦值,當(dāng)然頂點(diǎn)顏色和定邊顏色也可以由外面?zhèn)魅搿?/p>

具體代碼:

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];
    //相機(jī)位置矩陣
    private final float[] mViewMatrix = new float[16];
    //計(jì)算變換矩陣
    private final float[] mMVPMatrix = new float[16];
    private float radius = 1.0f; //半徑
    private int n = 3;  //切割份數(shù)
    private float[] shapePos;


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

        //錐面所有坐標(biāo)
        shapePos = createPositions();
        //申請(qǐng)底層空間
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(shapePos.length * 4);
        byteBuffer.order(ByteOrder.nativeOrder());
        //將坐標(biāo)數(shù)據(jù)轉(zhuǎn)換為FloatBuffer,用以傳入給OpenGL ES程序
        vertexBuffer1 = byteBuffer.asFloatBuffer();
        //將三角形坐標(biāo)傳入FloatBuffer
        vertexBuffer1.put(shapePos);
        vertexBuffer1.position(0);


        //圓形所有坐標(biāo)
        ByteBuffer byteBuffer2 = ByteBuffer.allocateDirect(shapePos.length * 4);
        byteBuffer2.order(ByteOrder.nativeOrder());
        //將坐標(biāo)數(shù)據(jù)轉(zhuǎn)換為FloatBuffer,用以傳入給OpenGL ES程序
        vertexBuffer2 = byteBuffer2.asFloatBuffer();
        //將三角形坐標(biāo)傳入FloatBuffer
        shapePos[2] = 0.0f;  //改變錐面頂點(diǎn)高度,變?yōu)閳A心坐標(biāo)
        vertexBuffer2.put(shapePos);
        vertexBuffer2.position(0);


        //創(chuàng)建頂點(diǎn)著色器程序
        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertextShaderCode);
        //創(chuàng)建片元著色器程序
        int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);

        if (vertexShader == 0 || fragmentShader == 0)
        {
            return;
        }
        //創(chuàng)建一個(gè)空的OpenGL ES程序
        program = GLES20.glCreateProgram();
        //將頂點(diǎn)著色器加入程序
        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;

        //設(shè)置透視矩陣
        Matrix.frustumM(mProjectMatrix, 0, -ratio, ratio, -1, 1, 3, 20);
        //設(shè)置相機(jī)位置
        Matrix.setLookAtM(mViewMatrix , 0, 1.0f, -10.0f, -4.0f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
        //計(jì)算變換矩陣
        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");
        //設(shè)置vMatrix的值
        GLES20.glUniformMatrix4fv(vMatrix, 1, false, mMVPMatrix, 0);
        //獲取頂點(diǎn)著色器的vPosition成員句柄
        int vPosition = GLES20.glGetAttribLocation(program, "vPosition");
        //啟用vPosition句柄
        GLES20.glEnableVertexAttribArray(vPosition);
        //傳的圓錐所有頂點(diǎn)坐標(biāo)數(shù)據(jù)
        GLES20.glVertexAttribPointer(vPosition, 3, GLES20.GL_FLOAT, false, 3 * 4, vertexBuffer1);
        //繪制
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, shapePos.length / 3);
        //傳的圓形所有頂點(diǎn)坐標(biāo)數(shù)據(jù)
        GLES20.glVertexAttribPointer(vPosition, 3, GLES20.GL_FLOAT, false, 3 * 4, vertexBuffer2);
        //繪制圓形
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, shapePos.length / 3);

        //禁止頂點(diǎn)數(shù)組的句柄
        GLES20.glDisableVertexAttribArray(vPosition);

    }

    public int loadShader(int type, String shaderCode)
    {
        //創(chuàng)建空的著色器
        int shader = GLES20.glCreateShader(type);
        //將著色器程序加到著色器中
        GLES20.glShaderSource(shader, shaderCode);
        //編譯色器程序
        GLES20.glCompileShader(shader);

        return shader;

    }

    private float[] createPositions()
    {
        ArrayList<Float> data = new ArrayList<>();
        data.add(0.0f);
        data.add(0.0f);
        data.add(2.0f);         //給圓心相對(duì)圓邊增加高度,使之形成錐面
        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;
    }
}

圓柱

圓柱與圓錐類似,我們可以把圓柱拆分成上下圓面,加上一個(gè)圓筒。圓筒我們之前也沒有畫過,它怎么拆解成三角形呢?我們可以如同拆解東思路來理解圓柱,想想正三棱柱,正八棱柱,正一百棱柱。。。棱越多,就越圓滑,與圓柱越接近,然后再把每個(gè)棱面(矩形)拆分成兩個(gè)三角形就ok。圓筒面的所有頂點(diǎn)為:

        ArrayList<Float> 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));
            //圓筒坐標(biāo)
            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);
        }

頂部圓所有頂點(diǎn)坐標(biāo)為:

        ArrayList<Float> 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));
            //頂部圓形坐標(biāo)
            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);
        }

底部圓所有頂點(diǎn)坐標(biāo)為:

        ArrayList<Float> 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));
             //底下圓形坐標(biāo)
            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);
        }

這樣就可以繪制圓柱了,繪制圓筒面時(shí)要注意繪制方式是GL_TRIANGLE_STRIP。這樣我們就可以得到一個(gè)圓柱了:


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];
    //相機(jī)位置矩陣
    private final float[] mViewMatrix = new float[16];
    //計(jì)算變換矩陣
    private final float[] mMVPMatrix = new float[16];
    private float radius = 1.0f; //半徑
    private int n = 360;  //切割份數(shù)
    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);
        //將背景設(shè)置為灰色
        GLES20.glClearColor(0.5f,0.2f,1.0f,1.0f);
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT| GLES20.GL_DEPTH_BUFFER_BIT);

        //頂點(diǎn)坐標(biāo)
        createPositions();
        //申請(qǐng)底層空間
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(cylinderShapePos.length * 4);
        byteBuffer.order(ByteOrder.nativeOrder());
        //將坐標(biāo)數(shù)據(jù)轉(zhuǎn)換為FloatBuffer,用以傳入給OpenGL ES程序
        cylinderVertexBuffer = byteBuffer.asFloatBuffer();
        //將三角形坐標(biāo)傳入FloatBuffer
        cylinderVertexBuffer.put(cylinderShapePos);
        cylinderVertexBuffer.position(0);


        ByteBuffer bottomByteBuffer = ByteBuffer.allocateDirect(bottomShapePos.length * 4);
        bottomByteBuffer.order(ByteOrder.nativeOrder());
        //將坐標(biāo)數(shù)據(jù)轉(zhuǎn)換為FloatBuffer,用以傳入給OpenGL ES程序
        bottomVertexBuffer = bottomByteBuffer.asFloatBuffer();
        //將三角形坐標(biāo)傳入FloatBuffer
        bottomVertexBuffer.put(bottomShapePos);
        bottomVertexBuffer.position(0);

        ByteBuffer topByteBuffer = ByteBuffer.allocateDirect(topShapePos.length * 4);
        topByteBuffer.order(ByteOrder.nativeOrder());
        //將坐標(biāo)數(shù)據(jù)轉(zhuǎn)換為FloatBuffer,用以傳入給OpenGL ES程序
        topVertexBuffer = topByteBuffer.asFloatBuffer();
        //將三角形坐標(biāo)傳入FloatBuffer
        topVertexBuffer.put(topShapePos);
        topVertexBuffer.position(0);

        //創(chuàng)建頂點(diǎn)著色器程序
        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertextShaderCode);
        //創(chuàng)建片元著色器程序
        int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);

        if (vertexShader == 0 || fragmentShader == 0)
        {
            return;
        }
        //創(chuàng)建一個(gè)空的OpenGL ES程序
        program = GLES20.glCreateProgram();
        //將頂點(diǎn)著色器加入程序
        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;

        //設(shè)置透視矩陣
        Matrix.frustumM(mProjectMatrix, 0, -ratio, ratio, -1, 1, 3, 20);
        //設(shè)置相機(jī)位置
        Matrix.setLookAtM(mViewMatrix , 0, 1.0f, -10.0f, -4.0f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
        //計(jì)算變換矩陣
        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");
        //設(shè)置vMatrix的值
        GLES20.glUniformMatrix4fv(vMatrix, 1, false, mMVPMatrix, 0);
        //獲取頂點(diǎn)著色器的vPosition成員句柄
        int vPosition = GLES20.glGetAttribLocation(program, "vPosition");
        //啟用vPosition句柄
        GLES20.glEnableVertexAttribArray(vPosition);
        //傳的圓筒面的所有坐標(biāo)數(shù)據(jù)
        GLES20.glVertexAttribPointer(vPosition, 3, GLES20.GL_FLOAT, false, 3 * 4, cylinderVertexBuffer);
        //繪制圓筒面
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, cylinderShapePos.length / 3);
        //底部圓形的所有坐標(biāo)數(shù)據(jù)
        GLES20.glVertexAttribPointer(vPosition, 3, GLES20.GL_FLOAT, false, 3 * 4, bottomVertexBuffer);
        //繪制底部圓形
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, bottomShapePos.length / 3);
        //頂部圓形的所有坐標(biāo)數(shù)據(jù)
        GLES20.glVertexAttribPointer(vPosition, 3, GLES20.GL_FLOAT, false, 3 * 4, topVertexBuffer);
        //繪制頂部圓形
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, topShapePos.length / 3);

        //禁止頂點(diǎn)數(shù)組的句柄
        GLES20.glDisableVertexAttribArray(vPosition);

    }

    public int loadShader(int type, String shaderCode)
    {
        //創(chuàng)建空的著色器
        int shader = GLES20.glCreateShader(type);
        //將著色器程序加到著色器中
        GLES20.glShaderSource(shader, shaderCode);
        //編譯色器程序
        GLES20.glCompileShader(shader);

        return shader;

    }

    private void createPositions()
    {
        ArrayList<Float> cylinder = new ArrayList<>();
        ArrayList<Float> bottom = new ArrayList<>();
        ArrayList<Float> 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));
            //圓筒坐標(biāo)
            cylinder.add(x);
            cylinder.add(y);
            cylinder.add(2.0f);

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

            //底下圓形坐標(biāo)
            bottom.add(x);
            bottom.add(y);
            bottom.add(0.0f);
            //頂部圓形坐標(biāo)
            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);
        }
    }
}

相對(duì)于圓錐圓柱來說,球體的拆解就復(fù)雜許多了,比較常見的拆解方法是將按照經(jīng)緯度拆解和按照正多面體拆解,下圖分別為正多面體和經(jīng)緯度拆解.

正多面體拆解

3.jpg

經(jīng)緯度拆解(每一個(gè)小塊看作一個(gè)矩形,再拆成三角形):

4.jpg

由上圖可以明顯看出,多面體雖然看起來好看點(diǎn),但是還是按照經(jīng)緯度方式來拆解計(jì)算容易點(diǎn),畢竟規(guī)律那么明顯。

球上點(diǎn)的坐標(biāo)

無論是按照經(jīng)緯度拆還是多面體拆,都需要知道球上面點(diǎn)的坐標(biāo),這算是基本的幾何知識(shí)了。以球的中心為坐標(biāo)中心,球的半徑為R的話,那么球上點(diǎn)的坐標(biāo)(x0,y0,z0)為:

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

其中,a為圓心到點(diǎn)的線段與xz平面的夾角,b為圓心到點(diǎn)的線段在xz平面的投影與z軸的夾角,如圖:


5.png

得到頂點(diǎn)后,剩下的工作就和之前繪制其他圖形一樣了。
但是如果繼續(xù)使用圓錐的著色器,我們會(huì)得到這樣的一個(gè)球:


6.jpg

看起來都不太像個(gè)球了,要不是有條白線,這是不是個(gè)球就不好說了。我們需要修啊下頂點(diǎn)著色器,讓它有立體感。把頂點(diǎn)著色器修改為:

    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);" +
            "}";

運(yùn)行一下,我們得到的結(jié)果如下圖:


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];
    //相機(jī)位置矩陣
    private final float[] mViewMatrix = new float[16];
    //計(jì)算變換矩陣
    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);
        //將背景設(shè)置為灰色
        GLES20.glClearColor(0.5f, 0.2f, 1.0f, 1.0f);
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);

        //頂點(diǎn)坐標(biāo)
        createPositions();
        //申請(qǐng)底層空間
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(shapePos.length * 4);
        byteBuffer.order(ByteOrder.nativeOrder());
        //將坐標(biāo)數(shù)據(jù)轉(zhuǎn)換為FloatBuffer,用以傳入給OpenGL ES程序
        vertexBuffer = byteBuffer.asFloatBuffer();
        //將三角形坐標(biāo)傳入FloatBuffer
        vertexBuffer.put(shapePos);
        vertexBuffer.position(0);


        //創(chuàng)建頂點(diǎn)著色器程序
        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertextShaderCode);
        //創(chuàng)建片元著色器程序
        int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);

        if (vertexShader == 0 || fragmentShader == 0)
        {
            return;
        }
        //創(chuàng)建一個(gè)空的OpenGL ES程序
        program = GLES20.glCreateProgram();
        //將頂點(diǎn)著色器加入程序
        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;

        //設(shè)置透視矩陣
        Matrix.frustumM(mProjectMatrix, 0, -ratio, ratio, -1, 1, 3, 20);
        //設(shè)置相機(jī)位置
        Matrix.setLookAtM(mViewMatrix, 0, 1.0f, -10.0f, -4.0f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
        //計(jì)算變換矩陣
        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");
        //設(shè)置vMatrix的值
        GLES20.glUniformMatrix4fv(vMatrix, 1, false, mMVPMatrix, 0);
        //獲取頂點(diǎn)著色器的vPosition成員句柄
        int vPosition = GLES20.glGetAttribLocation(program, "vPosition");
        //啟用vPosition句柄
        GLES20.glEnableVertexAttribArray(vPosition);
        //傳的圓筒面的所有坐標(biāo)數(shù)據(jù)
        GLES20.glVertexAttribPointer(vPosition, 3, GLES20.GL_FLOAT, false, 3 * 4, vertexBuffer);
        //繪制圓筒面
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, shapePos.length / 3);


        //禁止頂點(diǎn)數(shù)組的句柄
        GLES20.glDisableVertexAttribArray(vPosition);

    }

    public int loadShader(int type, String shaderCode)
    {
        //創(chuàng)建空的著色器
        int shader = GLES20.glCreateShader(type);
        //將著色器程序加到著色器中
        GLES20.glShaderSource(shader, shaderCode);
        //編譯色器程序
        GLES20.glCompileShader(shader);

        return shader;

    }

    private void createPositions()
    {
        ArrayList<Float> 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 度旋轉(zhuǎn)遍歷一條緯線
            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);
        }

    }
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容