OpenGL ES案例一:平面三角形繪制

說(shuō)明:Android中1.x已經(jīng)過(guò)時(shí)了,且2.0并不兼容1.x。本文以2.0版本為準(zhǔn)!

1. 創(chuàng)建GLSurfaceView和Renderer

詳細(xì)解釋在代碼中用注釋標(biāo)明了,就不另作介紹了。
GLSurfaceView負(fù)責(zé)界面展示,GLSurfaceView.Renderer負(fù)責(zé)界面的渲染邏輯控制。

public class TriangleGLView extends GLSurfaceView {

    public TriangleGLView(Context context) {
        this(context,null);
    }

    public TriangleGLView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        //設(shè)置OpenGLES版本號(hào)2.0
        setEGLContextClientVersion(2);
        //設(shè)置渲染器
        setRenderer(new TriangleRenderer(this));
        //設(shè)置渲染模式
        //①RENDERMODE_CONTINUOUSLY:主動(dòng)繪制,一定的時(shí)間間隔自動(dòng)調(diào)用onDrawFrame方法,進(jìn)行繪制
        //②RENDERMODE_WHEN_DIRTY:被動(dòng)繪制,只有當(dāng)調(diào)用requestRender()方法是,才會(huì)進(jìn)行繪制。
        setRenderMode(RENDERMODE_WHEN_DIRTY);
    }

    private class TriangleRenderer implements Renderer {

        private GLSurfaceView mView;
        private Triangle mTriangle;

        public TriangleRenderer(GLSurfaceView view){
            this.mView = view;
        }

        @Override
        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
            //設(shè)置背景色
            GLES20.glClearColor(05f,0.5f,0.5f,0.5f);
            //開(kāi)啟OpenGLES的某些功能。GL_DEPTH_TEST:深度測(cè)試,當(dāng)繪制3D圖形的時(shí)候需要開(kāi)啟,我們現(xiàn)在繪制的是平面圖形不要開(kāi)啟。
//            GLES20.glEnable(GLES20.GL_DEPTH_TEST);
            mTriangle = new Triangle(mView);
        }

        @Override
        public void onSurfaceChanged(GL10 gl, int width, int height) {
            //設(shè)置OpenGLES視口。
            GLES20.glViewport(0,0,width,height);

            float r = (float)width/height;
            //設(shè)置平截頭體,并給投影矩陣賦值。(frustumM方法賦值的投影矩陣為透視投影,見(jiàn)圖透視投影)
            Matrix.frustumM(Triangle.sProjMatirx,0,
                    -r,r,-1,1,1,10);
            //設(shè)置平截頭體,并給投影矩陣賦值。(orthoM方法賦值的投影矩陣為正交投影,見(jiàn)圖:正交投影)
            //Matrix.orthoM(Triangle.sProjMatirx,0,
            //         -r,r,-1,1,1,10);
            //設(shè)置攝像機(jī)位置(觀察點(diǎn)位置),
            Matrix.setLookAtM(Triangle.sVMatrix,0,
                    0,0,3,
                    0,0,0,
                    0,1,0);
        }

        @Override
        public void onDrawFrame(GL10 gl) {
            //清除顏色緩存,注釋部分為深度緩存。
            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT/*|GLES20.GL_DEPTH_BUFFER_BIT*/);
            mTriangle.draw();
        }
    }
}
透視投影.png
正交投影.png
2. 創(chuàng)建頂點(diǎn)著色器(triangle_ver.glsl)
uniform mat4 u_ProjMatirx;//投影矩陣,通過(guò)程序傳進(jìn)來(lái)。
uniform mat4 u_VMatirx;//攝像機(jī)(觀察點(diǎn))矩陣,通過(guò)程序傳進(jìn)來(lái)。
uniform mat4 u_MMatirx;//變換矩陣,通過(guò)程序傳進(jìn)來(lái)。

attribute vec4 a_position;//頂點(diǎn)數(shù)據(jù),通過(guò)程序傳進(jìn)來(lái)。
attribute vec4 a_color;//頂點(diǎn)顏色數(shù)據(jù),通過(guò)程序傳進(jìn)來(lái)。

varying vec4 v_color;//傳遞給片元著色器的頂點(diǎn)顏色數(shù)據(jù)。

void main() {
    //這里解釋下總變換矩陣的作用:a_position是物體坐標(biāo),
    //而展示到屏幕上需要把物體坐標(biāo)轉(zhuǎn)換為世界坐標(biāo)(下圖會(huì)解釋變換過(guò)程)。
    //注意矩陣順序不能錯(cuò),矩陣叉乘的特性。
    gl_Position = u_ProjMatirx * u_VMatirx * u_MMatirx * a_position;
    //將頂點(diǎn)顏色數(shù)據(jù)通過(guò)共享變量,傳遞給片元著色器。
    v_color = a_color;
}

矩陣變換過(guò)程.png

上圖摘自OpenGL坐標(biāo)系統(tǒng)

創(chuàng)建片元著色器
//片元著色器中需要制定精度
precision mediump float;

varying vec4 v_color;

void main() {
    //將頂點(diǎn)顏色數(shù)據(jù)賦給片元著色器內(nèi)部,進(jìn)行柵格化。
    gl_FragColor = v_color;
}

3.創(chuàng)建三角形類(lèi)(Triangle)

內(nèi)容比較多,簡(jiǎn)單說(shuō)一下步驟:

  1. 初始化頂點(diǎn)數(shù)據(jù)
  2. 初始化著色器(包含-獲取著色器屬性(可選))
  3. 使用OpenGL ES 2.0繪制三角形(包含-變換矩陣初始化,著色器屬性賦值)
package com.linuxpara.gles20example.shader;

import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.util.Log;

import com.linuxpara.gles20example.util.ShaderUtils;

import java.nio.FloatBuffer;

/**
 * Date: 2017/12/11
 * *************************************************************
 * Auther: 陳占洋
 * *************************************************************
 * Email: zhanyang.chen@gmail.com
 * *************************************************************
 * Description:
 */

public class Triangle {

    private static final String TAG = "Triangle";

    //投影矩陣
    public static float[] sProjMatirx = new float[16];
    //攝像機(jī)矩陣
    public static float[] sVMatrix = new float[16];
    //變化矩陣
    public static float[] sMMatrix = new float[16];

    private FloatBuffer mVerBuffer;
    private int mVerSize;
    private FloatBuffer mColorBuffer;

    private int mProgram;
    private int a_position;
    private int a_color;
    private int u_mMatirx;
    private int u_projMatrix;
    private int u_vMatirx;

    public Triangle(GLSurfaceView view) {
        initVerData();
        initShader(view);
    }

    /**
     * 初始化頂點(diǎn)數(shù)據(jù)
     */
    private void initVerData() {
        float[] ver = {
                //三角形一共三個(gè)頂點(diǎn)
                -0.5f, -1f, -3f,//第一個(gè)頂點(diǎn)的xyz軸坐標(biāo)。
                0.5f, -1f, -3f,//第二個(gè)頂點(diǎn)的xyz軸坐標(biāo)。
                0f, 1f, -3f,//第三個(gè)頂點(diǎn)的xyz軸坐標(biāo)。
        };
        //頂點(diǎn)個(gè)數(shù)
        mVerSize = ver.length / 3;
        //頂點(diǎn)緩存
        mVerBuffer = ShaderUtils.getFloatBuffer(ver);

        float[] color = {
                //頂點(diǎn)的顏色
                1, 0, 0, 1,//第一個(gè)頂點(diǎn)的顏色RGBA
                0, 1, 0, 1,//第二個(gè)頂點(diǎn)的顏色RGBA
                0, 0, 1, 1,//第三個(gè)頂點(diǎn)的顏色RGBA
        };
        //頂點(diǎn)顏色緩存
        mColorBuffer = ShaderUtils.getFloatBuffer(color);
    }

    /**
     * 初始化著色器
     * @param view
     */
    private void initShader(GLSurfaceView view) {
        String verSource = ShaderUtils.getSourceFromAsset("triangle_ver.glsl", view.getResources());
        String fragSource = ShaderUtils.getSourceFromAsset("triangle_frag.glsl", view.getResources());

        mProgram = createProgram(verSource, fragSource);
        if (mProgram == 0){
            Log.i(TAG, "initShader: 創(chuàng)建著色器程序失?。。?!");
        }
        //獲取shader中的投影矩陣變量。
        u_projMatrix = GLES20.glGetUniformLocation(mProgram, "u_ProjMatirx");
        //獲取shader中的攝像機(jī)矩陣變量
        u_vMatirx = GLES20.glGetUniformLocation(mProgram, "u_VMatirx");
        //獲取shader中的變換矩陣變量
        u_mMatirx = GLES20.glGetUniformLocation(mProgram, "u_MMatirx");
        //獲取shader中的頂點(diǎn)數(shù)據(jù)變量
        a_position = GLES20.glGetAttribLocation(mProgram, "a_position");
        //獲取shader中的頂點(diǎn)顏色變量
        a_color = GLES20.glGetAttribLocation(mProgram, "a_color");
    }

    /**
     * 創(chuàng)建著色器程序。
     * @param verSource
     * @param fragSource
     * @return
     */
    protected int createProgram(String verSource,String fragSource){

        int verShader = loadShader(GLES20.GL_VERTEX_SHADER, verSource);
        if (verShader == 0){
            return 0;
        }

        int fragShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragSource);
        if (fragShader == 0){
            return 0;
        }

        int program = GLES20.glCreateProgram();
        if (program != 0){
            //依賴
            GLES20.glAttachShader(program,verShader);
            checkGLError("glAttachShader");
            GLES20.glAttachShader(program,fragShader);
            checkGLError("glAttachShader");
            //鏈接
            GLES20.glLinkProgram(program);

            int[] linked  = new int[1];
            GLES20.glGetProgramiv(program,GLES20.GL_LINK_STATUS,linked,0);
            if (linked[0] == 0){
                Log.e("ES20_ERROR", "Could not link program: ");
                Log.e("ES20_ERROR", GLES20.glGetProgramInfoLog(program));
                GLES20.glDeleteProgram(program);
                program = 0;
            }
        }

        return program;
    }

    /**
     * 加載著色器
     * @param shaderType
     * @param shaderSource
     * @return
     */
    private int loadShader(int shaderType,String shaderSource){
        //創(chuàng)建shader
        int shader = GLES20.glCreateShader(shaderType);
        if (shader != 0){
            //加載shader源碼
            GLES20.glShaderSource(shader,shaderSource);
            //編譯源碼
            GLES20.glCompileShader(shader);
            int[] complied = new int[1];
            //獲取shader信息
            GLES20.glGetShaderiv(shader,GLES20.GL_COMPILE_STATUS,complied,0);
            if (complied[0] == 0){
                Log.e("ES20_ERROR", "Could not compile shader " + shaderType + ":");
                Log.e("ES20_ERROR", GLES20.glGetShaderInfoLog(shader));
                //刪除shader
                GLES20.glDeleteShader(shader);
                shader = 0;
            }
        }
        return shader;

    }

    public void checkGLError(String op) {

        int error = 0;
        if ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR){
            Log.e("ES20_ERROR", op + ": glError " + error);
            throw new RuntimeException(op + ": glError " + error);
        }

    }

    public void draw() {
        //指定使用著色器程序。
        GLES20.glUseProgram(mProgram);
        //初始化變換矩陣
        Matrix.setRotateM(sMMatrix,0,
                0,
                0,0,-1f);
        //給shader中u_ProjMatrix變量賦值
        GLES20.glUniformMatrix4fv(u_projMatrix,1,
                false,sProjMatirx,0);
        //給shader中u_VMatirx變量賦值
        GLES20.glUniformMatrix4fv(u_vMatirx,1,
                false,sVMatrix,0);
        //給shader中u_MMatirx變量賦值
        GLES20.glUniformMatrix4fv(u_mMatirx,1,
                false,sMMatrix,0);
        //給shader中a_position變量賦值
        GLES20.glVertexAttribPointer(a_position,3,GLES20.GL_FLOAT,
                false,0,mVerBuffer);
        //給shader中a_color變量賦值
        GLES20.glVertexAttribPointer(a_color,4,GLES20.GL_FLOAT,
                false,0,mColorBuffer);
        //開(kāi)啟頂點(diǎn)數(shù)據(jù)
        GLES20.glEnableVertexAttribArray(a_position);
        GLES20.glEnableVertexAttribArray(a_color);
        //繪制頂點(diǎn)數(shù)據(jù)
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES,0,mVerSize);
        //關(guān)閉頂點(diǎn)數(shù)據(jù)
        GLES20.glDisableVertexAttribArray(a_position);
        GLES20.glDisableVertexAttribArray(a_color);

    }
}
用到的ShaderUtils工具類(lèi)

代碼比較簡(jiǎn)單,不做過(guò)多說(shuō)明

package com.linuxpara.gles20example.util;

import android.content.res.Resources;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;

/**
 * Date: 2017/12/11
 * *************************************************************
 * Auther: 陳占洋
 * *************************************************************
 * Email: zhanyang.chen@gmail.com
 * *************************************************************
 * Description:
 */

public class ShaderUtils {

    /**
     * 獲取浮點(diǎn)類(lèi)型緩存
     *
     * @param buffer
     * @return
     */
    public static FloatBuffer getFloatBuffer(float[] buffer) {

        FloatBuffer floatBuffer = ByteBuffer.allocateDirect(buffer.length * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer()
                .put(buffer);
        floatBuffer.position(0);

        return floatBuffer;
    }

    /**
     * 獲取int類(lèi)型緩存
     *
     * @param buffer
     * @return
     */
    public static IntBuffer getIntBuffer(int[] buffer) {

        IntBuffer intBuffer = ByteBuffer.allocateDirect(buffer.length * 4)
                .order(ByteOrder.nativeOrder())
                .asIntBuffer()
                .put(buffer);
        intBuffer.position(0);

        return intBuffer;
    }

    /**
     * 獲取字節(jié)類(lèi)型緩存
     *
     * @param buffer
     * @return
     */
    public static ByteBuffer getByteBuffer(byte[] buffer) {

        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(buffer.length)
                .order(ByteOrder.nativeOrder())
                .put(buffer);
        byteBuffer.position(0);

        return byteBuffer;
    }

    /**
     * 從資產(chǎn)目錄中獲取shader代碼
     *
     * @param fileName
     * @return
     */
    public static String getSourceFromAsset(String fileName, Resources resources) {

        InputStream in = null;
        ByteArrayOutputStream baos = null;
        String result = "";

        try {
            in = resources.getAssets().open(fileName);

            baos = new ByteArrayOutputStream();

            byte[] buf = new byte[1024];
            int len = 0;
            while ((len = in.read(buf)) != -1) {
                baos.write(buf, 0, len);
            }
            String source = baos.toString("UTF-8");
            result = source.replaceAll("\\r\\n", "\n");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (baos != null) {
                try {
                    baos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return result;
        }
    }

    /**
     * 打印數(shù)組類(lèi)型的矩陣
     * @param <T>
     * @param matrix
     * @param column
     * @return
     */
    public static <T> String printFloatMatrixArray(float[] matrix, int column) {
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < matrix.length; i++) {
            if (i%column == 0){
                result.append("\n");
            }else {
                result.append(",");
            }
            result.append(matrix[i]);
        }
        return result.toString();
    }
}

補(bǔ)充:以下圖片摘自極客學(xué)院。
GLES20.glDrawArrays(GLES20.GL_TRIANGLES,0,mVerSize);中繪制類(lèi)型解釋。
GL_POINTS:繪制獨(dú)立的點(diǎn)。

gl-points.png

GL_LINES:頂點(diǎn)兩兩連接,為多條線段構(gòu)成。
gl_lines.png

GL_LINE_STRIP:繪制一系列線段。
gl_line_strip.png

GL_LINE_LOOP:類(lèi)同上,但是首尾相連,構(gòu)成一個(gè)封閉曲線。
gl_line_loop.png

GL_TRIANGLES:每隔三個(gè)頂點(diǎn)構(gòu)成一個(gè)三角形,為多個(gè)三角形組成。
gl_triangles.png

GL_TRIANGLE_STRIP:每相鄰三個(gè)頂點(diǎn)組成一個(gè)三角形,為一系列相接三角形構(gòu)成。
gl_triangle_strip.png

GL_TRIANGLE_FAN:以一個(gè)點(diǎn)為三角形公共頂點(diǎn),組成一系列相鄰的三角形。
gl_triangle_fan.png

最后編輯于
?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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