Opengles 2.0 使用Matrix 讓三角形、圓形、等不會因爲手機的長寬比被拉伸

應用投影和相機視圖

在 OpenGL ES 環境中,通過投影和相機視圖,顯示的繪製對象更接近於眼睛看到的實物。這種對看實物的模擬是通過對繪製對象座標進行數學轉換完成的:

  • 投影 - 這種轉換可根據顯示繪製對象的 GLSurfaceView 的寬度和高度調整繪製對象的座標。如果不進行這種計算,由 OpenGL ES 繪製的對象會被不等比例的視圖窗口所扭曲。通常只有在 OpenGL 視圖的比例確定下來或在渲染程序的 onSurfaceChanged() 方法中更改時才需要計算投影轉換。如需詳細瞭解 OpenGL ES 投影和座標映射,請參閱映射繪製對象的座標
  • 相機視圖 - 這種轉換可根據虛擬相機的位置調整繪製對象的座標。請務必注意,OpenGL ES 不會定義實際的相機對象,而是通過轉換繪製對象的顯示方式來提供模擬相機的實用程序方法。相機視圖轉換可能僅在您確定 GLSurfaceView 時計算一次,也可能會根據用戶操作或應用的功能動態變化。

 

映射已繪製對象的座標

在 Android 設備上顯示圖形時,一個的基本問題在於屏幕的尺寸和形狀各不相同。OpenGL 假設屏幕採用均勻的方形座標系,默認情況下,您可以將這些座標恰當地繪製到通常爲非方形的屏幕上,就好像這些屏幕是完美的方形一樣。

爲了應用投影和相機視圖,您可以創建一個投影矩陣和一個相機視圖矩陣,並將它們應用到 OpenGL 渲染管道。投影矩陣會重新計算圖形的座標,以便它們能夠正確地映射到 Android 設備屏幕。相機視圖矩陣會創建一個轉換,用於從特定的眼睛位置渲染對象 

OpenGL ES 2.0 及更高版本中的投影和相機視圖

在 ES 2.0 和 3.0 API 中,您可以應用投影或相機視圖,只需先分別向圖形對象的頂點着色程序添加投影矩陣或相機視圖矩陣成員即可。添加此矩陣成員後,您便可以分別生成投影和相機視圖矩陣,並將它們應用到您的對象。

  1. 爲頂點着色程序添加矩陣 - 爲視圖投影矩陣創建一個變量,並將其作爲着色程序位置的調節係數添加。以下頂點着色程序代碼示例中包含一個 uMVPMatrix 成員,它使您可以將投影和相機視圖矩陣應用到使用此着色程序的對象的座標。
    private final String vertexShaderCode =

        // This matrix member variable provides a hook to manipulate
        // the coordinates of objects that use this vertex shader.
        "uniform mat4 uMVPMatrix;   \n" +

        "attribute vec4 vPosition;  \n" +
        "void main(){               \n" +
        // The matrix must be included as part 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; \n" +

        "}  \n";
    

 2.訪問着色程序矩陣 - 在頂點着色程序中創建鉤子機制以分別應用投影和相機視圖後,您可以訪問該變量,以應用投影和相機查看矩陣。以下代碼展示瞭如何通過修改 GLSurfaceView.Renderer 實現的 onSurfaceCreated() 方法來訪問上述頂點着色程序中定義的矩陣變量。

    public void onSurfaceCreated(GL10 unused, EGLConfig config) {
        ...
        muMVPMatrixHandle = GLES20.glGetUniformLocation(program, "uMVPMatrix");
        ...
    }
    

3創建投影和相機視圖矩陣 - 生成要應用於圖形對象的投影和視圖矩陣。以下示例代碼展示瞭如何通過修改 GLSurfaceView.Renderer 實現的 onSurfaceCreated() 和 onSurfaceChanged() 方法,根據設備的屏幕寬高比創建相機視圖矩陣和投影矩陣。

    public void onSurfaceCreated(GL10 unused, EGLConfig config) {
        ...
        // Create a camera view matrix 填充視圖矩陣
        Matrix.setLookAtM(vMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
    }

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

        float ratio = (float) width / height;

        // create a projection matrix from device screen geometry 填充投影矩陣
        Matrix.frustumM(projMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
    }
    

4應用投影和相機視圖矩陣 - 要應用投影和相機視圖轉換,請將這些矩陣相乘,然後再將它們設置到頂點着色程序中。以下示例代碼展示瞭如何通過修改 GLSurfaceView.Renderer 實現的 onDrawFrame() 方法,合併在上述代碼中創建的投影矩陣和相機視圖,然後將其應用到將由 OpenGL 渲染的圖形對象。

    public void onDrawFrame(GL10 unused) {
        ...
        // Combine the projection and camera view matrices 將投影矩陣和視圖矩陣疊加
        Matrix.multiplyMM(vPMatrix, 0, projMatrix, 0, vMatrix, 0);

        // Apply the combined projection and camera view transformations
        //使用前面編譯的着色程序應用疊加後的矩陣
        GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, vPMatrix, 0);

        // Draw objects
        ...
    }
    

源碼

Renderer

public class GLRender_Matrix implements GLSurfaceView.Renderer {

    //    private MatrixTriangle matrixTriangle;
//    private MatrixSquare matrixSquare;
    private float[] vPMatrix = new float[16];
    //camera
    private float[] viewMatrix = new float[16];

    private float[] projectMatrix = new float[16];

    MatrixCircle matrixCircle;

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        //設置背景顏色
        GLES20.glClearColor(0, 0, 0, 1);
//        matrixTriangle = new MatrixTriangle();
//        matrixSquare = new MatrixSquare();
        matrixCircle = new MatrixCircle(0.4f, 30, 0, 0, 0);
        // Set the camera position (View matrix)
        Matrix.setLookAtM(viewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLES20.glViewport(0, 0, width, height);
        float ratial = (float) width / height;
        Matrix.frustumM(projectMatrix, 0, -ratial, ratial, -1, 1, 3, 7);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        //重新設置背景
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

        Matrix.multiplyMM(vPMatrix, 0, projectMatrix, 0, viewMatrix, 0);

        matrixCircle.draw(vPMatrix);
    }
}
public class MatrixCircle {

    private FloatBuffer vertexBuffer;
    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;" +
                    "}";
    private final String fragmentShaderCode =
            "precision mediump float;" +
                    "uniform vec4 vColor;" +
                    "void main() {" +
                    "  gl_FragColor = vColor;" +
                    "}";

    private float radius;
    private int count;

    private float x;
    private float y;
    private float z;

    //頂點的個數
    private int vertexCount;
    private static final int COORDS_PER_VERTEX = 3;
    private static final int vertexStride = COORDS_PER_VERTEX * 4;

    private int program;
    private int vPMatrixHandle;
    private int positionHandle;
    private int colorHandle;

    float color[] = {0.63671875f, 0.76953125f, 0.22265625f, 1.0f};

    public MatrixCircle(float radius, int count, float x, float y, float z) {
        this.radius = radius;
        this.count = count;
        this.x = x;
        this.y = y;
        this.z = z;

        ByteBuffer bb = ByteBuffer.allocateDirect((vertexCount = count + 2) * COORDS_PER_VERTEX * 4);
        bb.order(ByteOrder.nativeOrder());
        vertexBuffer = bb.asFloatBuffer();
        vertexBuffer.put(circleCoords());
        vertexBuffer.position(0);

        program = GLES20.glCreateProgram();
        int vertexShader = GlRender_1.loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
        int fragmentShader = GlRender_1.loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);

        GLES20.glAttachShader(program, vertexShader);
        GLES20.glAttachShader(program, fragmentShader);
        GLES20.glLinkProgram(program);
    }


    /*
     *將360°平均分成n 等份,求每個點的座標
     * */
    private float[] circleCoords() {
        //將圓形切成n塊有n+1個頂點+圓心
        float[] coords = new float[vertexCount * COORDS_PER_VERTEX];
        int offset = 0;
        coords[offset++] = x;
        coords[offset++] = y;
        coords[offset++] = z;
        for (int i = 0; i < count + 1; i++) {
            float angleInRadians = ((float) i / (float) count)
                    * ((float) Math.PI * 2f);
            coords[offset++] = x + radius * (float) Math.sin(angleInRadians);
            coords[offset++] = y + radius * (float) Math.cos(angleInRadians);
            coords[offset++] = z;
        }
        return coords;
    }

    public void draw(float[] mvpMatrix) {
        GLES20.glUseProgram(program);

        vPMatrixHandle = GLES20.glGetUniformLocation(program, "uMVPMatrix");
        GLES20.glUniformMatrix4fv(vPMatrixHandle, 1, false, mvpMatrix, 0);

        positionHandle = GLES20.glGetAttribLocation(program, "vPosition");
        GLES20.glEnableVertexAttribArray(positionHandle);
        GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);

        colorHandle = GLES20.glGetUniformLocation(program, "vColor");
        GLES20.glUniform4fv(colorHandle, 1, color, 0);

        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, vertexCount);
        GLES20.glDisableVertexAttribArray(positionHandle);
    }
}

 

總結:繪製二維圖形

1.opengles2.0 兩種繪製方式  頂點繪製和索引繪製,頂點繪製的模式有設置好的繪製順序,索引繪製可以自定義繪製順序

2.只能使用 ByteBuffer.allocateDirect()去分配內存,否則opengles 會報錯找不到數組ByteBuffer.allocate()分配的內存是在JVM中,而opengles使用的是系統內存

3.投影矩陣、視圖矩陣

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章