1. 程式人生 > >android平臺下OpenGL ES 3.0從矩形中看矩陣和正交投影

android平臺下OpenGL ES 3.0從矩形中看矩陣和正交投影

OpenGL ES 3.0學習實踐

目錄

繪製矩形

新建一個矩形渲染器:

public class RectangleRenderer implements GLSurfaceView.Renderer

定義頂點著色器:

#version 300 es
layout (location = 0) in vec4 vPosition;
layout (location = 1) in vec4 aColor;
out vec4 vColor;
void main() {
     gl_Position  =  vPosition;
     gl_PointSize = 10.0;
     vColor =
aColor; }

定義片段著色器:

#version 300 es
precision mediump float;
in vec4 vColor;
out vec4 fragColor;
void main() {
     fragColor = vColor;
}

定義座標點資料:

private float[] vertexPoints = new float[]{
            //前兩個是座標,後三個是顏色RGB
            0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
            -0.5f, -0.5f, 1.0f, 1.0f, 1.0f,
            0.5f
, -0.5f, 1.0f, 1.0f, 1.0f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, -0.5f, 0.5f, 1.0f, 1.0f, 1.0f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.25f, 0.5f, 0.5f, 0.5f, 0.0f, -0.25f, 0.5f, 0.5f, 0.5f, };
public RectangleRenderer() {
        //分配記憶體空間,每個浮點型佔4位元組空間
        vertexBuffer = ByteBuffer.
allocateDirect(vertexPoints.length * 4) .order(ByteOrder.nativeOrder()) .asFloatBuffer(); //傳入指定的座標資料 vertexBuffer.put(vertexPoints); vertexBuffer.position(0); }

開始編譯和連結著色器:

@Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        //設定背景顏色
        GLES30.glClearColor(0.5f, 0.5f, 0.5f, 0.5f);

        //編譯
        final int vertexShaderId = ShaderUtils.compileVertexShader(vertextShader);
        final int fragmentShaderId = ShaderUtils.compileFragmentShader(fragmentShader);
        //連結程式片段
        mProgram = ShaderUtils.linkProgram(vertexShaderId, fragmentShaderId);
        //在OpenGLES環境中使用程式片段
        GLES30.glUseProgram(mProgram);

        aPositionLocation = GLES30.glGetAttribLocation(mProgram, "vPosition");
        aColorLocation = GLES30.glGetAttribLocation(mProgram, "aColor");

        vertexBuffer.position(0);
        //獲取頂點陣列 (POSITION_COMPONENT_COUNT = 2)
        GLES30.glVertexAttribPointer(aPositionLocation, POSITION_COMPONENT_COUNT, GLES30.GL_FLOAT, false, STRIDE, vertexBuffer);

        GLES30.glEnableVertexAttribArray(aPositionLocation);

        vertexBuffer.position(POSITION_COMPONENT_COUNT);
        //顏色屬性分量的數量 COLOR_COMPONENT_COUNT = 3
        GLES30.glVertexAttribPointer(aColorLocation, COLOR_COMPONENT_COUNT, GLES30.GL_FLOAT, false, STRIDE, vertexBuffer);

        GLES30.glEnableVertexAttribArray(aColorLocation);
}

注意:glVertexAttribPointer這個方法的第5個引數stride,這個引數表示:

每個頂點由size指定的頂點屬性分量順序儲存。stride指定頂 點索引I和(I+1),表示的頂點資料之間的位移。如果stride為0,則每個頂點的屬性資料順序儲存。如果stride大於0, 則使用該值作為獲取下一個索引表示的頂點資料的跨距。

//之前定義的座標資料中,每一行是5個數據,前兩個表示座標(x,y),後三個表示顏色(r,g,b)
private static final int STRIDE = (POSITION_COMPONENT_COUNT + COLOR_COMPONENT_COUNT) * BYTES_PER_FLOAT;
//所以這裡實際是 STRIDE = (2 + 3) x 4

開始繪製:

@Override
public void onDrawFrame(GL10 gl) {
    GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);

    //繪製矩形
    GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 6);

    //繪製兩個點
    GLES30.glDrawArrays(GLES30.GL_POINTS, 6, 2);
}

豎屏狀態下顯示:

橫屏模式下顯示:

寬高比的問題

假設實際手機解析度以畫素為單位是720x1280,我們預設使用OpenGL佔用整個顯示屏。
裝置在豎屏模式下,那麼[-1,1]的範圍對應的高有1280畫素,而寬卻只有720畫素
影象會在x軸顯得扁平,如果在橫屏模式,影象會在y軸顯得扁平。通過上面的例子可以看到豎屏橫屏模式下就有種被拉伸的感覺。

其實在OpenGL中,我們要渲染的一切物體都要對映到x軸y軸[-1, 1]的範圍內,對z軸也是一樣的。這個範圍內的座標被稱為歸一化裝置座標,其獨立於螢幕實際的尺寸或形狀,但是因為它們獨立於實際的螢幕尺寸,如果直接使用它們,我們就會遇到剛才的問題。

歸一化裝置座標假定座標空間是一個正方形,然而,我們實際的視口viewport可能不是一個正方形,就像我剛剛手機上顯示的一樣,影象在一個方向上被拉伸,在另外一個方向上被壓扁。因此在一個豎屏裝置上,歸一化裝置座標上定義的影象看上去就是在水平方向上被壓扁,在橫屏模式下,同樣的影象就在垂直方向上看起來是壓扁的。

適應寬高比

這個時候我們就需要調整座標空間,讓它把螢幕的形狀考慮在內,可行的一個方法是把較小的範圍固定在[-1,1]內,而按螢幕尺寸的比例調整較大的範圍。

舉例來說,在豎屏情況下,其寬度是720,而髙度是1280,因此我們可以把寬度範圍限定在[-1,1],並把高度範圍調整為[-1280/720,1280/720][-1.78,1.78]。同理,在橫屏模式情況下,把寬度範圍設為[-1.78,1.78],而把高度範圍設為[-1,1]
通過調整已有的座標空間,最終會改變我們可用的空間,通過這個方法,不論是豎屏模式還是橫屏模式,物體看起來就都一樣了。

使用虛擬座標空間

我們需要調整座標空間,以便我們把螢幕方向考慮進來,需要停止直接在歸一化裝置座標上工作,而開始在虛擬座標空間裡工作。需要找到某種可以把虛擬空間座標轉換回歸一化裝置座標的方法,讓OpenGL可以正確地渲染它們,這個操作叫作正交投影,不管多遠 或多近,所有的物體看上去大小總是相同的。

矩陣和向量

正交投影之前,可以先來複習一下矩陣以及向量相關的知識,因為在OpenGL中大量地使用了向量和矩陣,矩陣的最重要的用途之一就是建立正交和透視投影。其原因之一是,使用矩陣做投影只涉及對一組資料按順序執行大量的加法和乘法,這些運算在現代GPU上執行得非常快。

  • 向量

一個向量是一個有多個元索的一維陣列。在OpenGL裡,一個位置通常是一個四元素向量,顏色也是一樣。我們使用的大多數向量一般都有四個元素。一個位置向量,它有一個x、一個y、一個z和一個w分量。

[xyzw] \begin{bmatrix} x \\ y \\ z \\ w \end{bmatrix}

我們在三維空間中,x,y,z分量用的比較多

  • 矩陣

—個矩陣(Matrix)是一個有多個元素的二維陣列。在OpenGL裡,我們一般使用矩陣作向量投影,如正交或者透視投影,並且也用它們使物體旋轉(rotation)、平移(translatum)以及縮放(scaling)。我們把矩陣與每個要變換的向最相乘即可實現這些變換。

[xxxyxzxwyxyyyzywzxzyzzzwwxwywzww] \begin{bmatrix} {x_{x}}&{x_{y}}&{x_{z}}&{x_{w}}\\ {y_{x}}&{y_{y}}&{y_{z}}&{y_{w}}\\ {z_{x}}&{z_{y}}&{z_{z}}&{z_{w}}\\ {w_{x}}&{w_{y}}&{w_{z}}&{w_{w}}\\ \end{bmatrix}

矩陣與向量相乘

[xxxyxzxwyxyyyzywzxzyzzzwwxwywzww][xyzw]=[xxxxyyxzzxwwyxxyyyyzzywwzxxzyyzzzzwwwxxwyywzzwww] \begin{bmatrix} {x_{x}}&{x_{y}}&{x_{z}}&{x_{w}}\\ {y_{x}}&{y_{y}}&{y_{z}}&{y_{w}}\\ {z_{x}}&{z_{y}}&{z_{z}}&{z_{w}}\\ {w_{x}}&{w_{y}}&{w_{z}}&{w_{w}}\\ \end{bmatrix} \begin{bmatrix} x \\ y \\ z \\ w \end{bmatrix}= \begin{bmatrix} {x_{x}x}&{x_{y}y}&{x_{z}z}&{x_{w}w}\\ {y_{x}x}&{y_{y}y}&{y_{z}z}&{y_{w}w}\\ {z_{x}x}&{z_{y}y}&{z_{z}z}&{z_{w}w}\\ {w_{x}x}&{w_{y}y}&{w_{z}z}&{w_{w}w}\\ \end{bmatrix}