Android OpenGL ES開發(fā)(五)正方形和圓

前言

前面提到過,在OpenGL ES 世界里面是沒有正方形和圓形的,只有點(diǎn)、線、三角形。三角形是OpenGL ES提供的最復(fù)雜的土元單位。序偶一我們要繪制填充正方形和圓形就需要利用三角形來充實(shí)。

正方形

正方形的構(gòu)建比較簡單,可以用兩個(gè)三角形組成。當(dāng)然,你也可以用很多三角形去合成一個(gè)正方形,只要你樂意。如下圖所示,我們可以按照123組成的三角形和134組成的三角形,兩個(gè)拼成一個(gè)正方形。


1.jpg

可以設(shè)置正方形的坐標(biāo)數(shù)組為:

    private float triangleCoors[] = {
            -0.5f, 0.5f, 0.0f, // top left   0
            -0.5f, -0.5f, 0.0f, // bottom left  1
            0.5f, -0.5f, 0.0f, // bottom right  2
            0.5f, 0.5f, 0.0f  // top right  3
    };

顏色

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

根據(jù)上圖1繪制三角形坐標(biāo)的順序

    private short index[] = {
            0,1,2,0,2,3
    };
頂點(diǎn)著色器
    private final String vertextShaderCode =
            "attribute vec4 vPosition;" +
            "uniform mat4 vMatrix;" +
            "void main() {" +
            "   gl_Position = vMatrix * vPosition;" +
            "}";

vPosition:接收頂點(diǎn)坐標(biāo)
vMatrix:變換矩陣,作用上篇文章已經(jīng)說過

片元著色器
    private final String fragmentShaderCode =
            "precision mediump float;" +
            "uniform vec4 vColor;" +
            "void main() {" +
            "   gl_FragColor = vColor;" +
            "}";

vColor:接收顏色

onSurfaceCreated 方法

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config)
    {
        //將背景設(shè)置為灰色
        GLES20.glClearColor(0.0f,0.0f,0.0f,1.0f);
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        //申請底層空間    頂點(diǎn)坐標(biāo)
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(triangleCoors.length * 4);
        byteBuffer.order(ByteOrder.nativeOrder());
        //將坐標(biāo)數(shù)據(jù)轉(zhuǎn)換為FloatBuffer,用以傳入給OpenGL ES程序
        vertexBuffer = byteBuffer.asFloatBuffer();
        //將三角形坐標(biāo)傳入FloatBuffer
        vertexBuffer.put(triangleCoors);
        vertexBuffer.position(0);

        //申請底層空間   坐標(biāo)順序
        ByteBuffer indexBuffer = ByteBuffer.allocateDirect(index.length * 2);
        indexBuffer.order(ByteOrder.nativeOrder());
        indexShortBuffer = indexBuffer.asShortBuffer();
        indexShortBuffer.put(index);
        indexShortBuffer.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);

    }

onSurfaceChanged方法

    @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,7);
        //設(shè)置相機(jī)位置
        Matrix.setLookAtM(mViewMatrix,0,0,0,7.0f,0,0,0,0,1.0f,0);
        //計(jì)算變換矩陣
        Matrix.multiplyMM(mMVPMatrix,0,mProjectMatrix,0,mViewMatrix,0);
    }

onDrawFrame 方法

    @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);
        //獲取頂點(diǎn)著色器的vColor成員句柄
        int aColor = GLES20.glGetUniformLocation(program, "vColor");
        //設(shè)置繪制三角形的顏色
        GLES20.glUniform4fv(aColor, 1, color, 0);
        //繪制三角形
        GLES20.glDrawElements(GLES20.GL_TRIANGLES,index.length,GLES20.GL_UNSIGNED_SHORT,indexShortBuffer);
        //禁止頂點(diǎn)數(shù)組的句柄
        GLES20.glDisableVertexAttribArray(vPosition);
    }
3.jpg

圓形

圓形的構(gòu)建,相對復(fù)雜一點(diǎn),我們可以把圓形看成一個(gè)正多邊形,邊越多,圓越平滑。如下圖所示,分別為正六邊形、正八邊形、正十六邊形和正一百邊形。


2.jpg

以六邊形為例,由012、023,034、045、056、061六個(gè)三角形,更多變形同樣如此。
利用簡單的數(shù)學(xué)知識(shí),即可得到,以多邊形中心建立直角坐標(biāo)系,得到n變形的頂點(diǎn)坐標(biāo)為L:

    private float[] createPositions()
    {
        ArrayList<Float> data = new ArrayList<>();
        data.add(1.0f);             //設(shè)置圓心坐標(biāo)
        data.add(1.0f);
        data.add(0.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;
    }

createPositions方法得到頂點(diǎn)坐標(biāo)數(shù)組,剩下的工作就和三角形的繪制基本相同,唯一不同的地方是需要修改:

GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 1);

為:

GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, shapePos.length/3);
    public static native void glDrawArrays(
        int mode,     //表示繪制方式
        int first,    //表示偏移量
        int count     //頂點(diǎn)個(gè)數(shù)
    );

繪制方式有:

GL_POINTS         //將傳入的頂點(diǎn)坐標(biāo)作為單獨(dú)的點(diǎn)繪制
GL_LINES          //將傳入的頂點(diǎn)坐標(biāo)作為單獨(dú)線條繪制,ABCDEF六個(gè)頂點(diǎn),繪制AB、CD、EF三條線
GL_LINE_LOOP      // 將傳入的頂點(diǎn)作為閉合折線繪制 ,ABCD四個(gè)頂點(diǎn),繪制AB、BC、CD、DA四條線
GL_LINE_STRIP     //將傳入的頂點(diǎn)作為折線繪制,ABCD四個(gè)頂點(diǎn),繪制AB、BC、CD三條線
GL_TRIANGLES      //將傳入的頂點(diǎn)作為單獨(dú)的三角形繪制,ABCDEF六個(gè)頂點(diǎn),繪制ABC、DEF兩個(gè)三角形
GL_TRIANGLE_STRIP //將傳入的頂點(diǎn)作為三角條帶繪制,ABCDEF六個(gè)頂點(diǎn),繪制ABC、BCD、CDE、DEF四個(gè)三角形
GL_TRIANGLE_FAN   //將傳入的頂作為扇面繪制,ABCDEF六個(gè)頂點(diǎn),繪制ABC、ACD、ADE、AEF四個(gè)三角形

顏色

    private float color[] = {
            1.0f , 1.0f , 1.0f , 1.0f
    };
頂點(diǎn)著色器
    private final String vertextShaderCode =
            "attribute vec4 vPosition;" +
            "uniform mat4 vMatrix;" +
            "void main() {" +
            "   gl_Position = vMatrix * vPosition;" +
            "}";

vPosition:接收頂點(diǎn)坐標(biāo)
vMatrix:變換矩陣,作用上篇文章已經(jīng)說過

片元著色器
    private final String fragmentShaderCode =
            "precision mediump float;" +
            "uniform vec4 vColor;" +
            "void main() {" +
            "   gl_FragColor = vColor;" +
            "}";

vColor:接收顏色

onSurfaceCreated 方法

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config)
    {
        //將背景設(shè)置為灰色
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        //圓頂點(diǎn)坐標(biāo)
        shapePos = createPositions();
        //申請底層空間
        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);

        //申請底層空間
        ByteBuffer colorBuffer = ByteBuffer.allocateDirect(color.length * 4);
        colorBuffer.order(ByteOrder.nativeOrder());
        colorFloatBuffer = colorBuffer.asFloatBuffer();
        colorFloatBuffer.put(color);
        colorFloatBuffer.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);

    }

onSurfaceChanged 方法

    @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, 7);
        //設(shè)置相機(jī)位置
        Matrix.setLookAtM(mViewMatrix, 0, 0, 0, 7.0f, 0, 0, 0, 0, 1.0f, 0);
        //計(jì)算變換矩陣
        Matrix.multiplyMM(mMVPMatrix, 0, mProjectMatrix, 0, mViewMatrix, 0);
    }

onDrawFrame 方法

    @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);
        //獲取頂點(diǎn)著色器的vColor成員句柄
        int aColor = GLES20.glGetUniformLocation(program, "vColor");
        //設(shè)置繪制三角形的顏色
        GLES20.glUniform4fv(aColor, 1, color, 0);
        //繪制三角形
//        GLES20.glDrawArrays(GLES20.GL_TRIANGLES,0,4);
//        GLES20.glDrawElements(GLES20.GL_TRIANGLES,index.length,GLES20.GL_UNSIGNED_SHORT,indexShortBuffer);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 1, shapePos.length / 3);
        //禁止頂點(diǎn)數(shù)組的句柄
        GLES20.glDisableVertexAttribArray(vPosition);
    }
4.jpg

繪制總結(jié)

GL_TRIANGLE_STRIP
由上面我們可以知道,GL_TRIANGLE_STRIP的方式繪制連續(xù)的三角形,比直接用GL_TRIANGLES的方式少好幾個(gè)頂點(diǎn),效率會(huì)高很多。另外,GL_TRIANGLE_STRIP并不只能繪制連續(xù)的三角形構(gòu)造的物體,我們只需要將不需要重復(fù)繪制的點(diǎn)重復(fù)兩次即可。比如,傳入ABCDEEFFGH坐標(biāo),就會(huì)得到ABC、BCD、CDE以及FGH四個(gè)三角形。

GL_TRIANGLE_FAN
扇面繪制是以第一個(gè)為零點(diǎn)進(jìn)行繪制,通常我們繪制圓形,圓錐的錐面都會(huì)使用到,值得注意的是,最后一個(gè)點(diǎn)的左邊應(yīng)當(dāng)與第二個(gè)重合,在計(jì)算的時(shí)候,起始角度為0度,重點(diǎn)角度包含360度。

頂點(diǎn)法和索引法
GLES20.glDrawArrays,也就是頂點(diǎn)法,是根據(jù)傳入的頂點(diǎn)順序進(jìn)行繪制的。
GLES20.glDrawElements,稱之為索引法,是根據(jù)索引序列,在頂點(diǎn)序列中找到對應(yīng)的頂點(diǎn),并根據(jù)繪制的方式,組成相應(yīng)的圖元進(jìn)行繪制。

頂點(diǎn)法擁有的繪制方式,所引發(fā)也都有。相對于頂點(diǎn)發(fā)在復(fù)雜圖形的繪制中無法避免大量頂點(diǎn)重復(fù)的情況,所引發(fā)可以相對頂點(diǎn)發(fā)減少很多重復(fù)頂點(diǎn)占用的空間。

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

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

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