Android openGl開發(fā)詳解(一)——簡(jiǎn)單圖形的基本繪制

簡(jiǎn)書不支持toc,暫時(shí)沒找到其他辦法蓝厌,先這樣截個(gè)圖。
帶目錄CSDN文章鏈接請(qǐng)點(diǎn)擊:Android openGl開發(fā)詳解(一)——繪制簡(jiǎn)單圖形

學(xué)習(xí)五部曲,弄清楚5個(gè)W一個(gè)H(when(什么時(shí)候使用)绣硝、where(在哪個(gè)地方使用?)撑刺、who(對(duì)誰(shuí)使用)鹉胖、what(是個(gè)什么東西)、why(為什么要這么用?).一個(gè)H即:how(到底該怎么用甫菠?))挠铲,基本的概念篇主要圍繞這幾個(gè)方面進(jìn)行分析

1. What? openGl是什么?openGl ES又是什么寂诱?

相信很多人從事開發(fā)的都或多或少聽到過(guò)有關(guān)OpenGl這個(gè)東西拂苹,但是平時(shí)用的少,只知道有這么個(gè)東西痰洒,而且學(xué)起來(lái)不簡(jiǎn)單瓢棒,所以大多數(shù)人都不能講出個(gè)個(gè)所以然來(lái)。

官方對(duì)OpenGl的描述為:

OpenGL(Open Graphics Library開發(fā)圖形接口)是一個(gè)跨平臺(tái)的圖形API丘喻,用于指定3D圖形處理硬件中的標(biāo)準(zhǔn)軟件接口脯宿。

OpenGl的前身是SGI公司為其圖形工作站開發(fā)的IRIS GL,后來(lái)因?yàn)镮RIS GL的移植性不好,所以在其基礎(chǔ)上泉粉,開發(fā)出了OpenGl连霉。OpenGl一般用于在圖形工作站,PC端使用嗡靡,由于性能各方面原因窘面,在移動(dòng)端使用OpenGl基本帶不動(dòng)。為此叽躯,Khronos公司就為OpenGl提供了一個(gè)子集财边,OpenGl ES(OpenGl for Embedded System)

什么是OpenGl ES呢?

OpenGl ES是免費(fèi)的跨平臺(tái)的功能完善的2D/3D圖形庫(kù)接口的API,是OpenGL的一個(gè)子集点骑。

移動(dòng)端使用到的基本上都是OpenGl ES酣难,當(dāng)然Android開發(fā)下還專門為OpenGl提供了android.opengl包,并且提供了GlSurfaceView,GLU,GlUtils等工具類黑滴。

2. How? Android中的openGL 如何使用憨募?

在了解OpenGl的使用之前,我們需要了解兩個(gè)基本類別的Android框架:GlSurfaceView和GlSurfaceView.Renderer

3. GlSurfaceView是什么袁辈? GLSurfaceView的作用是什么菜谣? GLSurfaceView如何使用?

GlSurfaceView從名字就可以看出晚缩,它是一個(gè)SurfaceView尾膊,看源碼可知,GlSurfaceView繼承自SurfaceView荞彼。并增加了Renderer.它的作用就是專門為OpenGl顯示渲染使用的冈敛。

GLSurfaceView的使用方法:
可以通過(guò)創(chuàng)建的實(shí)例使用這個(gè)類,并增加你的Renderer.

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        GLSurfaceView glSurfaceView = new GLSurfaceView(this);
        glSurfaceView.setRenderer(new GLSurfaceView.Renderer() {
            @Override
            public void onSurfaceCreated(GL10 gl, EGLConfig config) {
                
            }

            @Override
            public void onSurfaceChanged(GL10 gl, int width, int height) {

            }

            @Override
            public void onDrawFrame(GL10 gl) {

            }
        });
        setContentView(glSurfaceView);
    }

4. GlSurfaceView.Renderer是什么鸣皂?GLSurfaceView.Renderer的作用抓谴?GLSurfaceView.Renderer的用法暮蹂?

該接口定義了用于繪制在圖形所需的方法GLSurfaceView。你必須提供這個(gè)接口作為一個(gè)單獨(dú)的類的實(shí)現(xiàn)癌压,并將其連接到您的GLSurfaceView使用實(shí)例 GLSurfaceView.setRenderer()仰泻。如上面的代碼所示。作用就是提供各種渲染方法滩届,OpenGl的渲染操作均在此接口中實(shí)習(xí)集侯。下面說(shuō)下實(shí)現(xiàn)該接口的方法含義:

  • onSurfaceCreated():系統(tǒng)調(diào)用這個(gè)方法一次創(chuàng)建時(shí)GLSurfaceView。使用此方法來(lái)執(zhí)行只需要發(fā)生一次的操作丐吓,比如設(shè)置OpenGL的環(huán)境參數(shù)或初始化的OpenGL圖形對(duì)象浅悉。
  • onDrawFrame():系統(tǒng)調(diào)用上的每個(gè)重繪此方法GLSurfaceView趟据。使用此方法作為主要執(zhí)行點(diǎn)用于繪制(和重新繪制)的圖形對(duì)象券犁。
  • 系統(tǒng)調(diào)用此方法時(shí)的GLSurfaceView幾何形狀的變化,包括尺寸變化GLSurfaceView或設(shè)備屏幕的取向汹碱。例如粘衬,當(dāng)設(shè)備從縱向變?yōu)闄M向的系統(tǒng)調(diào)用這個(gè)方法。使用此方法可以在變化做出反應(yīng)GLSurfaceView容器咳促。

介紹完了GlSurfaceView和GlSurfaceView.renderer之后稚新,接下來(lái)說(shuō)下如何使用GlSurfaceView;

  1. 創(chuàng)建一個(gè)GlSurfaceView
  2. 為這個(gè)GlSurfaceView設(shè)置渲染
  3. 在GlSurfaceView.renderer中繪制處理顯示數(shù)據(jù)

5. OpenGl的簡(jiǎn)單使用實(shí)例(繪制一個(gè)三角形)

  1. 在使用OpenGl之前,需要在AndroidManifest.xml中設(shè)置OpenGl的版本:這里我們使用的是OpenGl ES 2.0,所以需要添加如下說(shuō)明:
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
  1. 使用GLSufaceView(上面有介紹)
  2. 具體在GlSurfaceView.Renderer中的繪制步驟:
    • 設(shè)置視圖展示窗口(viewport) :在onSurfaceChanged中調(diào)用GLES20.glViewport(0, 0, width, height);
    • 創(chuàng)建圖形類跪腹,確定好頂點(diǎn)位置和圖形顏色褂删,將頂點(diǎn)和顏色數(shù)據(jù)轉(zhuǎn)換為OpenGl使用的數(shù)據(jù)格式
    • 加載頂點(diǎn)找色器和片段著色器用來(lái)修改圖形的顏色,紋理冲茸,坐標(biāo)等屬性
    • 創(chuàng)建投影和相機(jī)視圖來(lái)顯示視圖的顯示狀態(tài)屯阀,并將投影和相機(jī)視圖的轉(zhuǎn)換傳遞給著色器。
    • 創(chuàng)建項(xiàng)目(Program),連接頂點(diǎn)著色器片段著色器轴术。
    • 將坐標(biāo)數(shù)據(jù)傳入到OpenGl ES程序中:

使用OpenGl修改背景顏色

創(chuàng)建一個(gè)GlSurfaceView难衰,并為其設(shè)置渲染OneGlRenderer;

public class OneGlSurfaceView extends GLSurfaceView {
    private final OneGlRenderer mRenderer;
    public OneGlSurfaceView(Context context) {
        super(context);
        // Create an OpenGL ES 2.0 context
        setEGLContextClientVersion(2);

        mRenderer = new OneGlRenderer();

        // Set the Renderer for drawing on the GLSurfaceView
        setRenderer(mRenderer);
    }
}

實(shí)現(xiàn)渲染接口

public class OneGlRenderer implements GLSurfaceView.Renderer {
    public void onSurfaceCreated(GL10 unused, EGLConfig config) {
        // Set the background frame color
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    }

    public void onDrawFrame(GL10 unused) {
        // Redraw background color
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
    }

    public void onSurfaceChanged(GL10 unused, int width, int height) {
        GLES20.glViewport(0, 0, width, height);
    }
}

展示渲染后的GlSurfaceView

public class OneOpenGlActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        OneGlSurfaceView glSurfaceView = new OneGlSurfaceView(this);
        setContentView(glSurfaceView);
    }
}

效果如下:就是簡(jiǎn)單給GlSurfaceView渲染一層黑色。

這里寫圖片描述

使用OpenGl繪制幾何圖形

一:圖形創(chuàng)建

創(chuàng)建一個(gè)幾何圖形(這里主要列舉三角形和正方形)逗栽,需要注意一點(diǎn)盖袭,我們?cè)O(shè)置圖形的頂點(diǎn)坐標(biāo)后,需要將頂點(diǎn)坐標(biāo)轉(zhuǎn)為ByteBuffer,這樣OpenGl才能進(jìn)行圖形處理彼宠。

三角形圖形創(chuàng)建:

public class Triangle {

    private FloatBuffer vertexBuffer;

    // number of coordinates per vertex in this array
    static final int COORDS_PER_VERTEX = 3;
    static float triangleCoords[] = {   // in counterclockwise order:
             0.0f,  0.5f, 0.0f, // top
            -0.5f, -0.5f, 0.0f, // bottom left
             0.5f, -0.5f, 0.0f  // bottom right
    };

    // Set color with red, green, blue and alpha (opacity) values
    float color[] = { 255, 0, 0, 1.0f };

    public Triangle() {
       // 初始化ByteBuffer鳄虱,長(zhǎng)度為arr數(shù)組的長(zhǎng)度*4,因?yàn)橐粋€(gè)float占4個(gè)字節(jié)
        ByteBuffer bb = ByteBuffer.allocateDirect(triangleCoords.length * 4);
        // 數(shù)組排列用nativeOrder
        bb.order(ByteOrder.nativeOrder());
        // 從ByteBuffer創(chuàng)建一個(gè)浮點(diǎn)緩沖區(qū)
        vertexBuffer = bb.asFloatBuffer();
        // 將坐標(biāo)添加到FloatBuffer
        vertexBuffer.put(triangleCoords);
        // 設(shè)置緩沖區(qū)來(lái)讀取第一個(gè)坐標(biāo)
        vertexBuffer.position(0);
    }
}

正方型圖:

public class Square {

    private FloatBuffer vertexBuffer;
    private ShortBuffer drawListBuffer;

    // number of coordinates per vertex in this array
    static final int COORDS_PER_VERTEX = 3;
    static float squareCoords[] = {
            -0.5f,  0.5f, 0.0f,   // top left
            -0.5f, -0.5f, 0.0f,   // bottom left
             0.5f, -0.5f, 0.0f,   // bottom right
             0.5f,  0.5f, 0.0f }; // top right

    private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices

    public Square() {
        // 初始化ByteBuffer凭峡,長(zhǎng)度為arr數(shù)組的長(zhǎng)度*4醇蝴,因?yàn)橐粋€(gè)float占4個(gè)字節(jié)
        ByteBuffer bb = ByteBuffer.allocateDirect(squareCoords.length * 4);
        bb.order(ByteOrder.nativeOrder());
        vertexBuffer = bb.asFloatBuffer();
        vertexBuffer.put(squareCoords);
        vertexBuffer.position(0);

        // 初始化ByteBuffer,長(zhǎng)度為arr數(shù)組的長(zhǎng)度*2想罕,因?yàn)橐粋€(gè)short占2個(gè)字節(jié)
        ByteBuffer dlb = ByteBuffer.allocateDirect(drawOrder.length * 2);
        dlb.order(ByteOrder.nativeOrder());
        drawListBuffer = dlb.asShortBuffer();
        drawListBuffer.put(drawOrder);
        drawListBuffer.position(0);
    }
}

創(chuàng)建圖形基本沒什么技巧可言悠栓,按部就班就行了霉涨,為什么數(shù)據(jù)需要轉(zhuǎn)換格式呢?主要是因?yàn)镴ava的緩沖區(qū)數(shù)據(jù)存儲(chǔ)結(jié)構(gòu)為大端字節(jié)序(BigEdian)惭适,而OpenGl的數(shù)據(jù)為小端字節(jié)序(LittleEdian),因?yàn)閿?shù)據(jù)存儲(chǔ)結(jié)構(gòu)的差異笙瑟,所以,在Android中使用OpenGl的時(shí)候必須要進(jìn)行下轉(zhuǎn)換癞志。當(dāng)然往枷,一般我們?cè)谑褂玫臅r(shí)候都會(huì)做個(gè)簡(jiǎn)單的工具類。這里提供幾個(gè)簡(jiǎn)單的封裝凄杯。(占幾個(gè)字節(jié)就初始化ByteBuffer長(zhǎng)度的時(shí)候*幾)

  1. 將int[]轉(zhuǎn)成IntBuffer
private IntBuffer intBufferUtil(int[] arr)
    {
        IntBuffer mBuffer;
        // 初始化ByteBuffer错洁,長(zhǎng)度為arr數(shù)組的長(zhǎng)度*4,因?yàn)橐粋€(gè)int占4個(gè)字節(jié)
        ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
        // 數(shù)組排列用nativeOrder
        qbb.order(ByteOrder.nativeOrder());
        mBuffer = qbb.asIntBuffer();
        mBuffer.put(arr);
        mBuffer.position(0);
        return mBuffer;
    }
  1. 將float[]數(shù)組轉(zhuǎn)為OpenGl 所需要的FloatBuffer
private FloatBuffer floatBufferUtil(float[] arr)
    {
        FloatBuffer mBuffer;
        // 初始化ByteBuffer戒突,長(zhǎng)度為arr數(shù)組的長(zhǎng)度*4屯碴,因?yàn)橐粋€(gè)int占4個(gè)字節(jié)
        ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4);
        // 數(shù)組排列用nativeOrder
        qbb.order(ByteOrder.nativeOrder());
        mBuffer = qbb.asFloatBuffer();
        mBuffer.put(arr);
        mBuffer.position(0);
        return mBuffer;
    }
  1. 當(dāng)然,依葫蘆畫瓢膊存,如何將short[]轉(zhuǎn)ShortBuffer這個(gè)就照著寫就ok了
 private ShortBuffer shortBufferUtil(short[] arr){
        ShortBuffer mBuffer;
        // 初始化ByteBuffer导而,長(zhǎng)度為arr數(shù)組的長(zhǎng)度*2,因?yàn)橐粋€(gè)short占2個(gè)字節(jié)
        ByteBuffer dlb = ByteBuffer.allocateDirect(
                // (# of coordinate values * 2 bytes per short)
                arr.length * 2);
        dlb.order(ByteOrder.nativeOrder());
        mBuffer = dlb.asShortBuffer();
        mBuffer.put(arr);
        mBuffer.position(0);
        return mBuffer;
    }

創(chuàng)建完形狀之后隔崎,我們就要進(jìn)行我們的第二步了今艺,將這些形狀渲染到GlSurfaceView中去。主要可分為下面幾步:

  1. 首先我們需要在GlSurfaceView.Renderer中初始化需要渲染的幾何圖形
private Triangle mTriangle;
    private Square   mSquare;
    public void onSurfaceCreated(GL10 unused, EGLConfig config) {
        // 設(shè)置背景顏色
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        // 初始化triangle
        mTriangle = new Triangle();
        // 初始化 square
        mSquare = new Square();
    }

二.:繪制圖形爵卒,因?yàn)樾枰峁┖芏嗉?xì)節(jié)的圖形渲染管線虚缎,所以繪制圖形前至少需要一個(gè)頂點(diǎn)著色器來(lái)繪制形狀和一個(gè)片段著色器的顏色,形狀钓株。這些著色器必須被編譯实牡,然后加入到一個(gè)OpenGL ES程序,然后將其用于繪制形狀享幽。簡(jiǎn)單介紹下這幾個(gè)概念:

  • 頂點(diǎn)著色器(Vertex Shader)頂點(diǎn)著色器是GPU上運(yùn)行的小程序铲掐,由名字可以知道,通過(guò)它來(lái)處理頂點(diǎn)值桩,他用于渲染圖形頂點(diǎn)的OpenGL ES圖形代碼摆霉。頂點(diǎn)著色器可用來(lái)修改圖形的位置,顏色奔坟,紋理坐標(biāo)携栋,不過(guò)不能用來(lái)創(chuàng)建新的頂點(diǎn)坐標(biāo)。
  • 片段著色器(Fragment Shader ) 用于呈現(xiàn)與顏色或紋理的形狀的面的OpenGL ES代碼咳秉。
  • 項(xiàng)目(Program) -包含要用于繪制一個(gè)或多個(gè)形狀著色器的OpenGL ES的對(duì)象婉支。

下面給Triangle類定義一個(gè)基本的著色器代碼:

public class Triangle {

    private final String vertexShaderCode =
        "attribute vec4 vPosition;" +
        "void main() {" +
        "  gl_Position = vPosition;" +
        "}";

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

    ...
}

當(dāng)然,上面我們創(chuàng)建了著色器的編譯代碼澜建,代碼編寫完成向挖,需要寫個(gè)方法來(lái)執(zhí)行這段代碼蝌以,這里我們?cè)阡秩酒髦袑懸粋€(gè)如下方法來(lái)執(zhí)行著色器代碼:

public static int loadShader(int type, String shaderCode){

        // 創(chuàng)造頂點(diǎn)著色器類型(GLES20.GL_VERTEX_SHADER)
        // 或者是片段著色器類型 (GLES20.GL_FRAGMENT_SHADER)
        int shader = GLES20.glCreateShader(type);
        // 添加上面編寫的著色器代碼并編譯它
        GLES20.glShaderSource(shader, shaderCode);
        GLES20.glCompileShader(shader);
        return shader;
    }

這里有一點(diǎn)需要注意,因?yàn)橹鞯拇a執(zhí)行是很昂貴滴何之,所以避免多次執(zhí)行跟畅,需要我們一般將執(zhí)行代碼的邏輯寫帶圖形類的構(gòu)造方法中。比如上面的Triangle溶推,我們就這么寫:

private final int mProgram;
public Triangle() {
        ... ...//數(shù)據(jù)轉(zhuǎn)換
        int vertexShader = OneGlRenderer.loadShader(GLES20.GL_VERTEX_SHADER,
                vertexShaderCode);
        int fragmentShader = OneGlRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER,
                fragmentShaderCode);

        // 創(chuàng)建空的OpenGL ES程序
        mProgram = GLES20.glCreateProgram();

        // 添加頂點(diǎn)著色器到程序中
        GLES20.glAttachShader(mProgram, vertexShader);

        // 添加片段著色器到程序中
        GLES20.glAttachShader(mProgram, fragmentShader);

        // 創(chuàng)建OpenGL ES程序可執(zhí)行文件
        GLES20.glLinkProgram(mProgram);
    }

最后徊件,所有繪制的所有基本配置都配置完成之后,我們來(lái)寫繪制圖形的方法蒜危,我們?cè)趫D形類(Triangle)中創(chuàng)建一個(gè)繪制的方法onDraw(),可以在onDraw()方法中設(shè)置繪制邏輯虱痕。

private int mPositionHandle;
    private int mColorHandle;

    private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX;
    private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex

    public void draw() {
        // 將程序添加到OpenGL ES環(huán)境
        GLES20.glUseProgram(mProgram);

        // 獲取頂點(diǎn)著色器的位置的句柄
        mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");

        // 啟用三角形頂點(diǎn)位置的句柄
        GLES20.glEnableVertexAttribArray(mPositionHandle);

        //準(zhǔn)備三角形坐標(biāo)數(shù)據(jù)
        GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
                GLES20.GL_FLOAT, false,
                vertexStride, vertexBuffer);

        // 獲取片段著色器的顏色的句柄
        mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");

        // 設(shè)置繪制三角形的顏色
        GLES20.glUniform4fv(mColorHandle, 1, color, 0);

        // 繪制三角形
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);

        // 禁用頂點(diǎn)數(shù)組
        GLES20.glDisableVertexAttribArray(mPositionHandle);
    }

完成上面所有步驟,只需要在GlSurfaceView.Renderer的onDrawFrame()方法中調(diào)用圖形類的繪制方法即可(上面的onDraw()):

 public void onDrawFrame(GL10 unused) {        mTriangle.draw();    }

最后的呈現(xiàn)效果如下圖所示:

這里寫圖片描述

運(yùn)用投影和相機(jī)視圖

通常情況下辐赞,OpenGl中展示的視圖和在Android上顯示的圖形會(huì)有偏差部翘。借用官方圖片:

這里寫圖片描述

當(dāng)然我們可以通過(guò)矩陣轉(zhuǎn)換來(lái)解決這種問(wèn)題,讓OpenGl上的視圖在任何android設(shè)備上顯示的比例都是一樣的占拍,這里說(shuō)下什么是投影和相機(jī)視圖:

投影的定義

使用OpenGl繪制的3D圖形略就,需要展示在移動(dòng)端2D設(shè)備上捎迫,這就是投影晃酒。Android OpenGl ES中有兩種投影方式:一種是正交投影,一種是透視投影:

正交投影投影物體的帶下不會(huì)隨觀察點(diǎn)的遠(yuǎn)近而發(fā)生變化窄绒,我們可以使用下面方法來(lái)執(zhí)行正交投影:

Matrix.orthoM (float[] m,           //接收正交投影的變換矩陣
                int mOffset,        //變換矩陣的起始位置(偏移量)
                float left,         //相對(duì)觀察點(diǎn)近面的左邊距
                float right,        //相對(duì)觀察點(diǎn)近面的右邊距
                float bottom,       //相對(duì)觀察點(diǎn)近面的下邊距
                float top,          //相對(duì)觀察點(diǎn)近面的上邊距
                float near,         //相對(duì)觀察點(diǎn)近面距離
                float far)          //相對(duì)觀察點(diǎn)遠(yuǎn)面距離

透視投影:隨觀察點(diǎn)的距離變化而變化贝次,觀察點(diǎn)越遠(yuǎn),視圖越小彰导,反之越大蛔翅,我們可以通過(guò)如下方法來(lái)設(shè)置透視投影:

Matrix.frustumM (float[] m,         //接收透視投影的變換矩陣
                int mOffset,        //變換矩陣的起始位置(偏移量)
                float left,         //相對(duì)觀察點(diǎn)近面的左邊距
                float right,        //相對(duì)觀察點(diǎn)近面的右邊距
                float bottom,       //相對(duì)觀察點(diǎn)近面的下邊距
                float top,          //相對(duì)觀察點(diǎn)近面的上邊距
                float near,         //相對(duì)觀察點(diǎn)近面距離
                float far)          //相對(duì)觀察點(diǎn)遠(yuǎn)面距離

相機(jī)視圖

什么是相機(jī)視圖?簡(jiǎn)單來(lái)說(shuō)生活中我們拍照位谋,你站的高度山析,拿相機(jī)的位置,姿勢(shì)不同掏父,拍出來(lái)的照片也就不一樣笋轨,相機(jī)視圖就是來(lái)修改相機(jī)位置,觀察方式以及相機(jī)的傾斜角度等屬性赊淑。我們可以通過(guò)下面方法來(lái)修改相機(jī)視圖屬性:

Matrix.setLookAtM (float[] rm,      //接收相機(jī)變換矩陣
                int rmOffset,       //變換矩陣的起始位置(偏移量)
                float eyeX,float eyeY, float eyeZ,   //相機(jī)位置
                float centerX,float centerY,float centerZ,  //觀察點(diǎn)位置
                float upX,float upY,float upZ)  //up向量在xyz上的分量

轉(zhuǎn)換矩陣(變換矩陣)

轉(zhuǎn)換矩陣用來(lái)做什么的呢爵政?是否記得上面我們繪制的圖形坐標(biāo)需要轉(zhuǎn)換為OpenGl中能處理的小端字節(jié)序(LittleEdian),沒錯(cuò)陶缺,轉(zhuǎn)換矩陣就是用來(lái)將數(shù)據(jù)轉(zhuǎn)為OpenGl ES可用的數(shù)據(jù)字節(jié)钾挟,我們將相機(jī)視圖和投影設(shè)置的數(shù)據(jù)相乘,便得到一個(gè)轉(zhuǎn)換矩陣饱岸,然后我們?cè)僦v此矩陣傳給頂點(diǎn)著色器掺出,具體使用方法及參數(shù)說(shuō)明如下:

Matrix.multiplyMM (float[] result, //接收相乘結(jié)果
                int resultOffset,  //接收矩陣的起始位置(偏移量)
                float[] lhs,       //左矩陣
                int lhsOffset,     //左矩陣的起始位置(偏移量)
                float[] rhs,       //右矩陣
                int rhsOffset)     //右矩陣的起始位置(偏移量)

下面簡(jiǎn)單講解下如何使用投影和相機(jī)視圖來(lái)實(shí)現(xiàn)矩陣變換并傳遞給頂點(diǎn)著色器徽千;

  1. 定義一個(gè)投影:
 // mMVPMatrix is an abbreviation for "Model View Projection Matrix"
    private final float[] mMVPMatrix = new float[16];
    private final float[] mProjectionMatrix = new float[16];
    private final float[] mViewMatrix = new float[16];
    public void onSurfaceChanged(GL10 unused, int width, int height) {
        GLES20.glViewport(0, 0, width, height);

        float ratio = (float) width / height;

        // 這個(gè)投影矩陣被應(yīng)用于對(duì)象坐標(biāo)在onDrawFrame()方法中
        Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
    }
  1. 定義一個(gè)相機(jī)視圖
@Override
public void onDrawFrame(GL10 unused) {
    ...
    // Set the camera position (View matrix)
    Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);

    // Calculate the projection and view transformation
    Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);

    // Draw shape
    mTriangle.draw(mMVPMatrix);
}
  1. 修改圖形類執(zhí)行代碼
public class Triangle {    private final String vertexShaderCode =        // This matrix member variable provides a hook to manipulate        // the coordinates of the objects that use this vertex shader        "uniform mat4 uMVPMatrix;" +        "attribute vec4 vPosition;" +        "void main() {" +        // the matrix must be included as a modifier of gl_Position        // Note that the uMVPMatrix factor *must be first* in order        // for the matrix multiplication product to be correct.        "  gl_Position = uMVPMatrix * vPosition;" +        "}";    // Use to access and set the view transformation    private int mMVPMatrixHandle;    ...}
  1. 投影和相機(jī)視圖代碼到圖形類的繪制方法中去onDraw()
public class Triangle {

    private final String vertexShaderCode =
        // This matrix member variable provides a hook to manipulate
        // the coordinates of the objects that use this vertex shader
        "uniform mat4 uMVPMatrix;" +
        "attribute vec4 vPosition;" +
        "void main() {" +
        // the matrix must be included as a modifier of gl_Position
        // Note that the uMVPMatrix factor *must be first* in order
        // for the matrix multiplication product to be correct.
        "  gl_Position = uMVPMatrix * vPosition;" +
        "}";

    // Use to access and set the view transformation
    private int mMVPMatrixHandle;

    ...
}
  1. 投影和相機(jī)視圖代碼到圖形類的繪制方法中去onDraw()
 public void draw(float[] mvpMatrix){
       ... ...
        // 得到形狀的變換矩陣的句柄
        mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");

        // 將投影和視圖轉(zhuǎn)換傳遞給著色器
        GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);

        // 畫三角形
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);

        // 禁用頂點(diǎn)數(shù)組
        GLES20.glDisableVertexAttribArray(mPositionHandle);
    }

做完這些,我們就能得到如下圖:

這里寫圖片描述

沒錯(cuò)汤锨,這才沒有變形的視圖罐栈。到這里,基本的通過(guò)OpenGl繪制簡(jiǎn)單圖形就over了泥畅,下面我們講解下如何添加一些交互動(dòng)作荠诬。

添加動(dòng)作

前面都是簡(jiǎn)單的動(dòng)作介紹,使用OpenGl在屏幕上繪制對(duì)象是使用openGl的基本功位仁。下面我來(lái)說(shuō)下如何添加旋轉(zhuǎn)形狀柑贞。使用OpenGl的描繪對(duì)象是相對(duì)簡(jiǎn)單的,首先需要在渲染器中創(chuàng)建一組旋轉(zhuǎn)矩陣聂抢,然后使用之前提到過(guò)的投影和相機(jī)視圖變換矩陣結(jié)合起來(lái)使用:

private float[] mRotationMatrix = new float[16];
public void onDrawFrame(GL10 gl) {
    float[] scratch = new float[16];

    ...

    // 創(chuàng)建一個(gè)旋轉(zhuǎn)矩陣
    long time = SystemClock.uptimeMillis() % 4000L;
    float angle = 0.090f * ((int) time);
    Matrix.setRotateM(mRotationMatrix, 0, angle, 0, 0, -1.0f);

    // 將旋轉(zhuǎn)矩陣與投影和相機(jī)視圖組合在一起
    // Note that the mMVPMatrix factor *must be first* in order
    // for the matrix multiplication product to be correct.
    Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);

    // Draw triangle
    mTriangle.draw(scratch);
}

運(yùn)行效果圖如下:

這里寫圖片描述

153); font-size: 0.7em;">這里寫圖片描述</figcaption>

修改頂點(diǎn)顏色

一個(gè)顏色是不是太單調(diào)了钧嘶?如何讓做成多彩的呢?接下來(lái)我們來(lái)做一個(gè)多彩三角形琳疏,如何來(lái)做一個(gè)多彩三角形有决?我們通過(guò)頂點(diǎn)著色器來(lái)做】张危基于上面的代碼书幕,我們只需要做一點(diǎn)點(diǎn)改動(dòng),下面是基本步驟:

  1. 修改著色器代碼
  2. 將顏色值修改為float數(shù)組并轉(zhuǎn)為floatBuffer
  3. 將獲取的floatBuffer傳遞給頂點(diǎn)著色器揽趾。

修改著色器代碼:

 private final String vertexShaderCode =
            "attribute vec4 vPosition;" +
                    "uniform mat4 uMVPMatrix;"+
                    "varying  vec4 vColor;"+
                    "attribute vec4 aColor;"+
                    "void main() {" +
                    "  gl_Position = uMVPMatrix*vPosition;" +
                    "  vColor=aColor;"+
                    "}";

    private final String fragmentShaderCode =
            "precision mediump float;" +
                    "varying vec4 vColor;" +
                    "void main() {" +
                    "  gl_FragColor = vColor;" +
                    "}";

shader的變量類型(uniform台汇,attribute和varying)的區(qū)別

關(guān)于shader的變量類型(uniform,attribute和varying)的區(qū)別及使用篱瞎,下面做下說(shuō)明:

  1. uniform:uniform變量在vertex和fragment兩者之間聲明方式完全一樣苟呐,則它可以在vertex和fragment共享使用。(相當(dāng)于一個(gè)被vertex和fragment shader共享的全局變量)uniform變量一般用來(lái)表示:變換矩陣俐筋,材質(zhì)牵素,光照參數(shù)和顏色等信息。在代碼中通過(guò)GLES20.glGetUniformLocation(int program, String name)來(lái)獲取屬性值澄者。并通過(guò) GLES20.glUniformMatrix4fv(int location, int count, boolean transpose, float[] value, int offset);方法將數(shù)據(jù)傳遞給著色器笆呆。
  2. attribute:這個(gè)變量只能在頂點(diǎn)著色器中使用(vertex Shader),用來(lái)表示頂點(diǎn)的數(shù)據(jù),比如頂點(diǎn)坐標(biāo)闷哆,頂點(diǎn)顏色腰奋,法線,紋理坐標(biāo)等抱怔。在繪制的時(shí)候通過(guò)GLES20.glGetAttribLocation(int program, String name)來(lái)獲取變量值劣坊,通過(guò) GLES20.glEnableVertexAttribArray(int index)來(lái)啟動(dòng)句柄,最后通過(guò) GLES20.glVertexAttribPointer(int indx,int size,int type,boolean normalized,int stride,java.nio.Buffer ptr)來(lái)設(shè)置圖形數(shù)據(jù)屈留。
  3. varying變量:這個(gè)變量只能用來(lái)在vertex和fragment shader之間傳遞數(shù)據(jù)時(shí)使用局冰,不可以通過(guò)代碼獲取其變量值测蘑。

接來(lái)下我們進(jìn)行數(shù)據(jù)轉(zhuǎn)換:

float color[] = {
            1.0f, 0f, 0f, 1.0f ,
            0f, 1.0f, 0f, 1.0f ,
            0f, 0f, 1.0f, 1.0f
    };
        public Triangle() {
           ... ...
          ByteBuffer dd = ByteBuffer.allocateDirect(
                    color.length * 4);
            dd.order(ByteOrder.nativeOrder());
            colorBuffer = dd.asFloatBuffer();
            colorBuffer.put(color);
            colorBuffer.position(0);
        }

最后我們需要獲取著色器的句柄并設(shè)置著色器的顏色:

public void draw(float[] mvpMatrix){
        ... ... 
         /* // 獲取片段著色器的vColor成員的句柄
        mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");

        // 設(shè)置繪制三角形的顏色
        GLES20.glUniform4fv(mColorHandle, 1, colorBuffer, 0);*/

        //獲取片元著色器的vColor成員的句柄
        mColorHandle = GLES20.glGetAttribLocation(mProgram, "aColor");
        //設(shè)置繪制三角形的顏色
        GLES20.glEnableVertexAttribArray(mColorHandle);
        GLES20.glVertexAttribPointer(mColorHandle,4,
                GLES20.GL_FLOAT,false,
                0,colorBuffer);
                ... ...
                }
這里寫圖片描述

153); font-size: 0.7em;">這里寫圖片描述</figcaption>

6. 參考鏈接:

  1. opengl官網(wǎng)
  2. opengl的環(huán)境搭建及基本教程

7. 項(xiàng)目地址:

AserbaosAndroid此項(xiàng)目為博主所有的系列學(xué)習(xí)的代碼匯總項(xiàng)目,該文章的代碼位于:opengl/OneOpenGl/OneOpenGlActivity

8. 打賞關(guān)注

If you find this repository helpful, you may make a donation to me via alipay or wechat.

堅(jiān)持原創(chuàng)技術(shù)分享康二,您的支持將鼓勵(lì)我繼續(xù)創(chuàng)作碳胳!

|

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市沫勿,隨后出現(xiàn)的幾起案子挨约,更是在濱河造成了極大的恐慌,老刑警劉巖产雹,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件诫惭,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蔓挖,警方通過(guò)查閱死者的電腦和手機(jī)夕土,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)瘟判,“玉大人怨绣,你說(shuō)我怎么就攤上這事】交瘢” “怎么了篮撑?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)刀诬。 經(jīng)常有香客問(wèn)我咽扇,道長(zhǎng)邪财,這世上最難降的妖魔是什么陕壹? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮树埠,結(jié)果婚禮上糠馆,老公的妹妹穿的比我還像新娘。我一直安慰自己怎憋,他們只是感情好又碌,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著绊袋,像睡著了一般毕匀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上癌别,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天皂岔,我揣著相機(jī)與錄音,去河邊找鬼展姐。 笑死躁垛,一個(gè)胖子當(dāng)著我的面吹牛剖毯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播教馆,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼逊谋,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了土铺?” 一聲冷哼從身側(cè)響起胶滋,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎悲敷,沒想到半個(gè)月后镀钓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡镀迂,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年丁溅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片探遵。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡窟赏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出箱季,到底是詐尸還是另有隱情涯穷,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布藏雏,位于F島的核電站拷况,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏掘殴。R本人自食惡果不足惜赚瘦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望奏寨。 院中可真熱鬧起意,春花似錦、人聲如沸病瞳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)套菜。三九已至亲善,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間逗柴,已是汗流浹背蛹头。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人掘而。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓挟冠,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親袍睡。 傳聞我的和親對(duì)象是個(gè)殘疾皇子知染,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容