Saturday, March 15, 2014

OpenGL ES 2.0 trên android - Trả lời sự kiện chạm

Trả lời sự kiện chạm

Làm cho đối tượng di chuyển theo một chương trình định sẵn như hình tam giác xoay rất hữu ích để có được một số sự chú ý, nhưng những gì nếu bạn muốn có người dùng tương tác với đồ họa OpenGL ES của bạn? Chìa khóa để làm ứng dụng cảm ứng OpenGL ES của bạn tương tác được mở rộng thực hiện của bạn GLSurfaceView để ghi đè lên onTouchEvent () để lắng nghe cho các sự kiện liên lạc.
Bài học này cho bạn thấy làm thế nào để lắng nghe cho các sự kiện liên lạc cho phép người dùng xoay một đối tượng OpenGL ES.

1.1.   Cài đặt một Touch Listener

Để làm cho ứng dụng OpenGL ES phản ứng với các sự kiện chạm vào, bạn phải thực hiện onTouchEvent() trong lớp GLSurfaceView của bạn. Việc thực hiện ví dụ dưới đây cho thấy làm thế nào để lắng nghe cho các sự kiện MotionEvent.ACTION_MOVE và dịch chúng vào một góc quay cho một shape.
@Override

public boolean onTouchEvent(MotionEvent e) {

    // MotionEvent reports input details from the touch screen
    // and other input controls. In this case, you are only
    // interested in events where the touch position changed.

    float x = e.getX();
    float y = e.getY();

    switch (e.getAction()) {
        case MotionEvent.ACTION_MOVE:

            float dx = x - mPreviousX;
            float dy = y - mPreviousY;

            // reverse direction of rotation above the mid-line
            if (y > getHeight() / 2) {
              dx = dx * -1 ;
            }

            // reverse direction of rotation to left of the mid-line
            if (x < getWidth() / 2) {
              dy = dy * -1 ;
            }

            mRenderer.setAngle(
                    mRenderer.getAngle() +
                    ((dx + dy) * TOUCH_SCALE_FACTOR);  // = 180.0f / 320
            requestRender();
    }

    mPreviousX = x;
    mPreviousY = y;
    return true;
}
Chú ý rằng sau khi tính toán góc quay, phương pháp này gọi requestRender () để cho các renderer rằng đó là thời gian để làm cho khung. Phương pháp này là hiệu quả nhất trong ví dụ này vì khung không cần phải được vẽ lại, trừ khi có một sự thay đổi trong vòng quay. Tuy nhiên, nó không có bất kỳ tác động hiệu quả trừ khi bạn cũng yêu cầu các renderer chỉ vẽ lại khi dữ liệu thay đổi bằng cách sử dụng hàm setRenderMode(), do đó hãy chắc chắn rằng dòng này là không chú thích trong renderer:
public MyGLSurfaceView(Context context) {

    ...
    // Render the view only when there is a change in the drawing data
    setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);

}

1.2.   Phơi bày các góc quay

Mã ví dụ trên yêu cầu bạn tiếp xúc với các góc quay thông qua renderer của bạn bằng cách thêm một thành viên public. Kể từ khi mã renderer đang chạy trên một thread khác từ phía giao diện người dùng chính thread của ứng dụng của bạn, bạn phải khai báo biến công cộng này là volatile. Đây là mã để làm điều đó:
public class MyGLRenderer implements GLSurfaceView.Renderer {

    ...
    public volatile float mAngle;

1.3.   Áp dụng phép xoay

Áp dụng phép xoay được tạo ra bởi đầu vào cảm ứng, chú thích ra các mã để tạo một góc và thêm mAngle, trong đó có các đầu vào tạo ra góc cảm ứng:
public void onDrawFrame(GL10 gl) {

    ...
    float[] scratch = new float[16];

    // Create a rotation for the triangle
    // long time = SystemClock.uptimeMillis() % 4000L;
    // float angle = 0.090f * ((int) time);
    Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, -1.0f);

    // Combine the rotation matrix with the projection and camera view
    // 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);
}
Khi bạn đã hoàn thành các bước mô tả ở trên, chạy chương trình và kéo tay của bạn trên màn hình để xoay hình tam giác:

Figure 1. Triangle being rotated with touch input (circle shows touch location).

OpenGL ES 2.0 trên android - Thêm chuyển động

Thêm chuyển động

Vẽ các đối tượng trên màn hình là một tính năng khá cơ bản của OpenGL, nhưng bạn có thể làm điều này với các lớp học framwork đồ họa Android khác, bao gồm cả Canvas và các đối tượng drawable. OpenGL ES cung cấp khả năng bổ sung cho việc di chuyển và chuyển đối tượng được vẽ với không gian ba chiều hoặc theo những cách độc đáo khác để tạo ra những trải nghiệm người dùng hấp dẫn.
Trong bài học này, bạn lấy một bước chuyển tiếp vào sử dụng OpenGL ES bằng cách học làm thế nào để thêm chuyển động với một shape với phép xoay.

1.1.   Xoay một Shape

Quay một đối tượng vẽ với OpenGL ES 2.0 là tương đối đơn giản. Bạn tạo ra một ma trận biến đổi (một ma trận xoay) và sau đó kết hợp nó với chiếu của bạn với camera ma trận biến đổi:
private float[] mRotationMatrix = new float[16];

public void onDrawFrame(GL10 gl) {

    ...

    float[] scratch = new float[16];

    // Create a rotation transformation for the triangle
    long time = SystemClock.uptimeMillis() % 4000L;
    float angle = 0.090f * ((int) time);
    Matrix.setRotateM(mRotationMatrix, 0, angle, 0, 0, -1.0f);

    // Combine the rotation matrix with the projection and camera view
    // 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);
}
Nếu tam giác của bạn không quay sau khi thực hiện những thay đổi này, chắc chắn rằng bạn đã chú thích thiết lập GLSurfaceView.RENDERMODE_WHEN_DIRTY, như được mô tả trong phần tiếp theo.

1.2.   Cho phép Rendering liên tục

Nếu bạn đã tinh tấn làm theo cùng với các mã ví dụ trong lớp này đến thời điểm này, chắc chắn rằng bạn nhận xét ra các dòng thiết lập chế độ làm chỉ vẽ khi thay đổi, nếu không OpenGL chỉ quay một lần và chờ đợi cho một cuộc gọi đến requestRender () từ container GLSurfaceView:
Trừ khi bạn có các đối tượng thay đổi mà không cần bất kỳ tương tác người dùng, nó thường là một ý tưởng tốt có lá cờ này bật. Hãy sẵn sàng để bỏ ghi chú mã này, bởi vì bài học tiếp theo làm cho cuộc gọi này được áp dụng một lần nữa.

OpenGL ES 2.0 trên android - Áp dụng phép chiếu và camera

Áp dụng phép chiếu và camera

Trong môi trường OpenGL ES, phép chiếu và máy quay cho phép bạn hiển thị các đối tượng hình vẽ theo cách giống cách bạn nhìn thấy đối tượng vật lý với đôi mắt của bạn. Mô phỏng vật lý này được thực hiển với biến đối toán học của tọa độ hình vẽ:
·         Chiếu - chuyển đổi này điều chỉnh tọa độ của đối tượng được vẽ dựa trên chiều rộng và chiều cao của GLSurfaceView nơi chúng được hiển thị. Mà không cần tính toán này, các đối tượng được vẽ bởi OpenGL ES đang sai lệch bởi tỷ lệ đồng đều của cửa sổ xem. Một chuyển đổi chiếu thường chỉ phải được tính toán khi tỷ lệ của khung nhìn OpenGL được thành lập hoặc thay đổi trong onSurfaceChanged() của renderer của bạn. Để biết thêm thông tin về OpenGL ES dự và phối hợp ánh xạ, xem bản đồ tọa độ cho các đối tượng được vẽ.
·         Camera - chuyển đổi này điều chỉnh tọa độ của đối tượng được vẽ dựa trên một vị trí máy quay ảo. Điều quan trọng cần lưu ý là OpenGL ES không định nghĩa một đối tượng máy ảnh thực tế, nhưng thay vì cung cấp phương pháp hữu ích mà mô phỏng một máy ảnh bằng cách biến đổi màn hình hiển thị của các đối tượng vẽ. Một biến đổi camera có thể được tính toán một lần khi bạn thiết lập GLSurfaceView của bạn, hoặc có thể thay đổi tự động dựa trên các hành động sử dụng hay chức năng ứng dụng của bạn.
Bài học này mô tả làm thế nào để tạo ra một chiếu và xem máy ảnh và áp dụng nó cho Shapes được vẽ trong GLSurfaceView của bạn.

1.1.   Định nghĩa phép chiếu

Dữ liệu cho một biến đổi của phép chiếu được tính toán trong hàm onSurfaceChanged() của lớp GLSurfaceView.Renderer. Ví dụ dưới đây lấy chiều cao và rộng của GLSurfaceView và sử dụng để chứa biến đổi ma trận phép chiếu sử dụng hàm Matrix.frustumM().
@Override
public void onSurfaceChanged(GL10 unused, int width, int height) {

    GLES20.glViewport(0, 0, width, height);

    float ratio = (float) width / height;

    // this projection matrix is applied to object coordinates
    // in the onDrawFrame() method
    Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
}
Mã này chứa một ma trận chiếu, mProjectionMatrix mà bạn có thể kết hợp với sự chuyển đổi camera quan sát trong hàm onDrawFrame(), được thể hiện trong phần tiếp theo.
Lưu ý: Chỉ cần áp dụng một phép chiếu cho đối tượng vẽ của bạn, nếu không kết quả sẽ là một màn hình trống rỗng. Nói chung, bạn cũng phải áp dụng các chuyển camera để cho bất cứ điều gì để hiển thị trên màn hình.

1.2.   Định nghĩa một Camera

Hoàn tất quá trình chuyển đối tượng vẽ của bạn bằng cách thêm một biến đổi camera như một phần của quá trình vẽ. Trong mã ví dụ sau đây, việc chuyển đổi camera được tính toán bằng cách sử dụng Matrix.setLookAtM () và sau đó kết hợp với ma trận chiếu tính trước đó. Các ma trận biến đổi kết hợp này sau đó được thông qua để vẽ ra shape.
@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.3.   Áp dụng phép chiếu và camera

Để sử dụng chiếu kết hợp với camera ma trận biến đổi thể hiện trong phần xem trước, chỉnh sửa hàm draw () của đối tượng đồ họa của bạn để chấp nhận các ma trận biến đổi kết hợp và áp dụng nó vào shape:
public void draw(float[] mvpMatrix) { // pass in the calculated transformation matrix

    ...

    // get handle to shape's transformation matrix
    mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");

    // Pass the projection and view transformation to the shader
    GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);

    // Draw the triangle
    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
    ...
}
Một khi bạn đã tính toán một cách chính xác và áp dụng phép chiếu với camera chuyển đổi, các đối tượng đồ họa của bạn được rút ra theo tỷ lệ chính xác và sẽ giống như thế này:
Figure 1. Triangle drawn with a projection and camera view applied.
Bây giờ bạn có một ứng dụng hiển thị shape của bạn với tỷ lệ chính xác, đã đến lúc để thêm chuyển động cho hình của bạn.

OpenGL ES 2.0 trên android - Định nghĩa Shapes - Hình

Định nghĩa Shapes

Việc có thể để xác định Shapes để vẽ trong context của một khung nhìn OpenGL ES là bước đầu tiên trong việc tạo ra tác phẩm đồ họa cao cấp của bạn. Vẽ với OpenGL ES có thể là một chút khó khăn mà không biết một số điều cơ bản về cách OpenGL ES hy vọng bạn xác định đối tượng đồ họa.
Bài học này giải thích OpenGL ES phối hợp hệ thống liên quan đến một màn hình thiết bị Android, những điều cơ bản của việc xác định Shapes, bề mặt Shapes, cũng như xác định một tam giác và một hình vuông.

1.1.   Định nghĩa một Tam giác

OpenGL ES cho phép bạn xác định đối tượng rút ra sử dụng tọa độ trong không gian ba chiều. Vì vậy, trước khi bạn có thể vẽ một hình tam giác, bạn phải xác định tọa độ của nó. Trong OpenGL, cách điển hình để làm điều này là để xác định một mảng đỉnh của số thực. Cho hiệu quả tối đa, bạn viết các tọa độ vào một ByteBuffer, nó sẽ thông qua vào các đường ống dẫn đồ họa OpenGL ES để xử lý.
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.622008459f, 0.0f, // top
            -0.5f, -0.311004243f, 0.0f, // bottom left
             0.5f, -0.311004243f, 0.0f  // bottom right
    };

    // Set color with red, green, blue and alpha (opacity) values

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

    public Triangle() {

        // initialize vertex byte buffer for shape coordinates
        ByteBuffer bb = ByteBuffer.allocateDirect(
                // (number of coordinate values * 4 bytes per float)
                triangleCoords.length * 4);
        // use the device hardware's native byte order
        bb.order(ByteOrder.nativeOrder());

        // create a floating point buffer from the ByteBuffer
        vertexBuffer = bb.asFloatBuffer();
        // add the coordinates to the FloatBuffer
        vertexBuffer.put(triangleCoords);
        // set the buffer to read the first coordinate
        vertexBuffer.position(0);
    }
}
Theo mặc định, OpenGL ES giả định một hệ thống phối hợp nơi [0,0,0] (X, Y, Z) xác định tâm của khung GLSurfaceView, [1,1,0] là góc trên bên phải của khung và [- 1, -1,0] là góc dưới bên trái của khung. Cho một minh họa của hệ thống phối hợp này, hãy xem hướng phát triển OpenGL ES.
Lưu ý rằng các tọa độ của Shapes này được xác định theo một thứ tự ngược chiều kim đồng hồ. Thứ tự bản vẽ là quan trọng vì nó xác định mặt trước và mặt sau của bề mặt của Shapes mà bạn vẽ, với mặt sau bề mặt, bạn có thể chọn không vẽ bằng cách sử dụng tính năng tiêu hủy mặt OpenGL ES. Để biết thêm thông tin về mặt và hủy, xem hướng dẫn phát triển OpenGL ES.

1.2.   Định nghĩa hình vuông

Xác định tam giác là khá dễ dàng trong OpenGL, nhưng nếu bạn muốn phức tạp hơn một chút? Gì nhỉ? một hình vuông? Có một số cách để làm điều này, nhưng một con đường điển hình để vẽ một hình dạng như vậy trong OpenGL ES là sử dụng hai hình tam giác được vẽ với nhau:
Figure 1. Drawing a square using two triangles.
Một lần nữa, bạn nên xác định các đỉnh theo một thứ tự ngược chiều kim đồng hồ cho cả hai hình tam giác mà đại diện cho hình dạng này, và đặt các giá trị trong một ByteBuffer. Để tránh việc xác định tọa độ hai chia sẻ bởi mỗi tam giác hai lần, sử dụng một danh sách bản vẽ để cho các đường ống dẫn đồ họa OpenGL ES cách vẽ các đỉnh. Đây là mã cho hình này:
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() {
        // initialize vertex byte buffer for shape coordinates
        ByteBuffer bb = ByteBuffer.allocateDirect(
        // (# of coordinate values * 4 bytes per float)
                squareCoords.length * 4);

        bb.order(ByteOrder.nativeOrder());
        vertexBuffer = bb.asFloatBuffer();
        vertexBuffer.put(squareCoords);
        vertexBuffer.position(0);

        // initialize byte buffer for the draw list
        ByteBuffer dlb = ByteBuffer.allocateDirect(
        // (# of coordinate values * 2 bytes per short)
                drawOrder.length * 2);
        dlb.order(ByteOrder.nativeOrder());
        drawListBuffer = dlb.asShortBuffer();
        drawListBuffer.put(drawOrder);
        drawListBuffer.position(0);
    }
}
Ví dụ này cung cấp cho bạn một cái nhìn vào những gì nó cần để tạo ra hình dạng phức tạp hơn với OpenGL. Nói chung, bạn sử dụng danh sách các hình tam giác để vẽ các đối tượng. Trong bài học tiếp theo, bạn tìm hiểu làm thế nào để vẽ các hình dạng trên màn hình.

2.      Vẽ Shapes

Sau khi bạn xác định shapes để được vẽ với OpenGL, bạn có thể muốn vẽ chúng. Vẽ hình với OpenGL ES 2.0 có thêm một chút mã hơn bạn có thể tưởng tượng, bởi vì các API cung cấp rất nhiều quyền kiểm soát các đường ống dẫn vẽ đồ họa.
Bài học này giải thích làm thế nào để vẽ các hình dạng bạn định nghĩa trong bài học trước bằng cách sử dụng OpenGL ES 2.0 API.

2.1.   Khởi tạo Shapes

Trước khi bạn vẽ bất kỳ thứ gì, bạn phải khởi tạo và tải các Shapes mà bạn muốn vẽ. Trừ khi cấu trúc của các Shapes thay đổi trong quá trình thực hiện, bạn nên khởi tạo chúng trong onSurfaceCreated () của renderer của bạn cho bộ nhớ và xử lý hiệu quả.
public void onSurfaceCreated(GL10 unused, EGLConfig config) {

    ...
    // initialize a triangle
    mTriangle = new Triangle();
    // initialize a square
    mSquare = new Square();
}

2.2.   Vẽ Shape

Vẽ một định nghĩa Shape bằng cách sử dụng OpenGL ES 2.0 đòi hỏi một số lượng đáng kể của mã, bởi vì bạn phải cung cấp rất nhiều chi tiết để các đường ống dẫn vẽ đồ họa. Cụ thể, bạn phải xác định như sau:
·         Vertex Shader - đồ họa OpenGL ES đang để hiển thị các đỉnh của một Shape.
·         Fragment Shader – mã OpenGL ES cho dựng hình bề mặt của một shape với màu sắc hoặc Textures.
·         Program - Một đối tượng OpenGL ES có chứa các bóng đổ bạn muốn sử dụng để vẽ một hoặc nhiều hình dạng.
Bạn cần ít nhất một vertex shader để vẽ một shape và một fragment shader để tô màu cho shape. Các bóng đổ phải được hoàn chỉnh và sau đó thêm vào một chương trình OpenGL ES, sau đó được sử dụng để vẽ hình dạng. Dưới đây là một ví dụ về cách xác định shaders cơ bản bạn có thể sử dụng để vẽ một hình dạng:
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;" +
    "}";
Shaders chứa OpenGL Shading Language (GLSL) mã phải được biên dịch trước khi sử dụng nó trong môi trường OpenGL ES. Để biên dịch mã này, tạo ra một utility method trong lớp renderer của bạn:
public static int loadShader(int type, String shaderCode){

    // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
    // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
    int shader = GLES20.glCreateShader(type);

    // add the source code to the shader and compile it
    GLES20.glShaderSource(shader, shaderCode);
    GLES20.glCompileShader(shader);

    return shader;
}
Để vẽ shape của bạn, bạn phải biên dịch shader code, thêm chúng vào một đối tượng program OpenGL ES và sau đó liên kết các program. Làm điều này trong hàm khởi tạo đối tượng, vì vậy nó chỉ được thực hiện một lần.
Lưu ý: Biên dịch shaders OpenGL ES và các liên kết program là tốn kém về chu kỳ CPU và thời gian xử lý, vì vậy bạn nên tránh làm điều này nhiều hơn một lần. Nếu bạn không biết nội dung của các bóng đổ của bạn tại thời gian chạy, bạn nên xây dựng mã của bạn như vậy mà họ chỉ được tạo ra một lần và sau đó lưu lại để sử dụng sau này.
public class Triangle() {

    ...

    int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);

    int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);

    mProgram = GLES20.glCreateProgram();             // create empty OpenGL ES Program
    GLES20.glAttachShader(mProgram, vertexShader);   // add the vertex shader to program
    GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
    GLES20.glLinkProgram(mProgram);                  // creates OpenGL ES program executables
}
Tại thời điểm này, bạn đã sẵn sàng để thêm các lời gọi vẽ shape của bạn. Vẽ hình với OpenGL ES yêu cầu bạn chỉ định một số thông số để cho các đường ống dẫn vẽ những gì bạn muốn vẽ và làm thế nào để vẽ nó. Kể từ khi lựa chọn vẽ có thể khác nhau bởi hình dạng, đó là một ý tưởng tốt để có các lớp hình dạng của bạn chứa logic vẽ của mình.
Tạo ra một hàm draw() để vẽ shape. Mã này thiết đặt vị trí và giá trị màu cho vertex shader và fragment shader, và sau đó thực hiện chức năng vẽ.
public void draw() {

    // Add program to OpenGL ES environment
    GLES20.glUseProgram(mProgram);

    // get handle to vertex shader's vPosition member
    mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");

    // Enable a handle to the triangle vertices
    GLES20.glEnableVertexAttribArray(mPositionHandle);

    // Prepare the triangle coordinate data
    GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
                                 GLES20.GL_FLOAT, false,
                                 vertexStride, vertexBuffer);

    // get handle to fragment shader's vColor member
    mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");

    // Set color for drawing the triangle
    GLES20.glUniform4fv(mColorHandle, 1, color, 0);

    // Draw the triangle
    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);

    // Disable vertex array
    GLES20.glDisableVertexAttribArray(mPositionHandle);
}
Một khi bạn có tất cả các mã này, vẽ đối tượng này chỉ đòi hỏi một lời gọi đến draw() từ bên trong phương pháp onDrawFrame()của renderer của bạn. Khi bạn chạy ứng dụng, nó sẽ giống như thế này:
Figure 1. Triangle drawn without a projection or camera view.
Có một vài vấn đề với mã ví dụ này. Trước hết, nó sẽ không gây ấn tượng với bạn bè của bạn. Thứ hai, hình tam giác là một chút bị đè nén và thay đổi hình dạng khi bạn thay đổi hướng màn hình của thiết bị. Lý do hình dạng bị lệch là do thực tế là các đỉnh của đối tượng đã không được sửa chữa cho tỷ lệ diện tích màn hình, nơi các GLSurfaceView được hiển thị. Bạn có thể khắc phục vấn đề bằng cách sử dụng chiếu và xem ảnh trong bài học tiếp theo.

Cuối cùng, hình tam giác là hình cơ bản, nên có một chút nhàm chán. Trong bài học thêm chuyển độn, bạn làm cho hình dạng này xoay và sử dụng thú vị hơn của đường ống đồ họa OpenGL ES.