Trong môi trường OpenGL ES, các khung hiển thị phép chiếu và máy ảnh cho phép bạn hiển thị các đối tượng đã vẽ theo cách gần giống với cách bạn nhìn các đối tượng thực tế bằng mắt của mình. Việc mô phỏng hoạt động xem thực tế này được thực hiện bằng các phép biến đổi toán học của toạ độ đối tượng được vẽ:
- Phép chiếu – Phép biến đổi này điều chỉnh toạ độ của các đối tượng được vẽ dựa trên
chiều rộng và chiều cao của
GLSurfaceView
mà các đối tượng đó hiển thị. Nếu không có cách tính này, các đối tượng do OpenGL ES vẽ sẽ bị lệch theo tỷ lệ không bằng nhau của cửa sổ chế độ xem. Thông thường, bạn chỉ phải tính toán một phép biến đổi phép chiếu khi tỷ lệ của khung hiển thị OpenGL được thiết lập hoặc thay đổi trong phương thứconSurfaceChanged()
của trình kết xuất. Để biết thêm thông tin về các phép chiếu OpenGL ES và ánh xạ toạ độ, hãy xem bài viết Liên kết toạ độ cho các đối tượng đã vẽ. - Camera View (Khung hiển thị máy ảnh) – Phép biến đổi này điều chỉnh toạ độ của các đối tượng được vẽ dựa trên vị trí máy ảnh ảo. Điều quan trọng cần lưu ý là OpenGL ES không xác định đối tượng máy ảnh thực tế, mà thay vào đó cung cấp các phương thức tiện ích mô phỏng máy ảnh bằng cách chuyển đổi màn hình của các đối tượng đã vẽ. Phép biến đổi khung hiển thị máy ảnh có thể chỉ được tính một lần khi bạn thiết lập
GLSurfaceView
hoặc có thể thay đổi linh động dựa trên thao tác của người dùng hoặc chức năng của ứng dụng.
Bài học này mô tả cách tạo một phép chiếu và khung hiển thị camera, đồng thời áp dụng cho các hình dạng được vẽ trong GLSurfaceView
.
Xác định phép chiếu
Dữ liệu cho phép biến đổi phép chiếu được tính toán trong phương thức onSurfaceChanged()
của lớp GLSurfaceView.Renderer
. Mã ví dụ sau đây lấy chiều cao và chiều rộng của GLSurfaceView
rồi dùng giá trị này để điền sẵn một phép biến đổi phép chiếu Matrix
bằng phương thức Matrix.frustumM()
:
Kotlin
// vPMatrix is an abbreviation for "Model View Projection Matrix" private val vPMatrix = FloatArray(16) private val projectionMatrix = FloatArray(16) private val viewMatrix = FloatArray(16) override fun onSurfaceChanged(unused: GL10, width: Int, height: Int) { GLES20.glViewport(0, 0, width, height) val ratio: Float = width.toFloat() / height.toFloat() // this projection matrix is applied to object coordinates // in the onDrawFrame() method Matrix.frustumM(projectionMatrix, 0, -ratio, ratio, -1f, 1f, 3f, 7f) }
Java
// vPMatrix is an abbreviation for "Model View Projection Matrix" private final float[] vPMatrix = new float[16]; private final float[] projectionMatrix = new float[16]; private final float[] viewMatrix = new float[16]; @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(projectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7); }
Mã này sẽ điền sẵn một ma trận chiếu, mProjectionMatrix
, sau đó bạn có thể kết hợp với phép biến đổi thành phần hiển thị máy ảnh trong phương thức onDrawFrame()
, như minh hoạ trong phần tiếp theo.
Lưu ý: Việc chỉ áp dụng phép biến đổi phép chiếu cho các đối tượng vẽ thường dẫn đến màn hình rất trống. Nhìn chung, bạn cũng phải áp dụng phép biến đổi thành phần hiển thị máy ảnh để mọi nội dung xuất hiện trên màn hình.
Xác định chế độ xem camera
Hoàn tất quy trình chuyển đổi các đối tượng được vẽ bằng cách thêm phép biến đổi thành phần hiển thị máy ảnh trong quy trình vẽ trong trình kết xuất. Trong mã ví dụ sau, phép biến đổi khung hiển thị camera được tính toán bằng phương thức Matrix.setLookAtM()
, sau đó kết hợp với ma trận chiếu đã tính toán trước đó. Sau đó, các ma trận biến đổi tổng hợp được truyền vào hình dạng đã vẽ.
Kotlin
override fun onDrawFrame(unused: GL10) { ... // Set the camera position (View matrix) Matrix.setLookAtM(viewMatrix, 0, 0f, 0f, 3f, 0f, 0f, 0f, 0f, 1.0f, 0.0f) // Calculate the projection and view transformation Matrix.multiplyMM(vPMatrix, 0, projectionMatrix, 0, viewMatrix, 0) // Draw shape triangle.draw(vPMatrix)
Java
@Override public void onDrawFrame(GL10 unused) { ... // Set the camera position (View matrix) Matrix.setLookAtM(viewMatrix, 0, 0, 0, 3, 0f, 0f, 0f, 0f, 1.0f, 0.0f); // Calculate the projection and view transformation Matrix.multiplyMM(vPMatrix, 0, projectionMatrix, 0, viewMatrix, 0); // Draw shape triangle.draw(vPMatrix); }
Áp dụng các phép biến đổi phép chiếu và camera
Để sử dụng ma trận biến đổi phép chiếu và chế độ xem máy ảnh kết hợp như trong phần xem trước, trước tiên hãy thêm một biến ma trận vào trình đổ bóng đỉnh đã được xác định trước đó trong lớp Triangle
:
Kotlin
class Triangle { private val 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 var vPMatrixHandle: Int = 0 ... }
Java
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 vPMatrixHandle; ... }
Tiếp theo, hãy sửa đổi phương thức draw()
của các đối tượng đồ hoạ để chấp nhận ma trận biến đổi kết hợp và áp dụng cho hình dạng:
Kotlin
fun draw(mvpMatrix: FloatArray) { // pass in the calculated transformation matrix // get handle to shape's transformation matrix vPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix") // Pass the projection and view transformation to the shader GLES20.glUniformMatrix4fv(vPMatrixHandle, 1, false, mvpMatrix, 0) // Draw the triangle GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount) // Disable vertex array GLES20.glDisableVertexAttribArray(positionHandle) }
Java
public void draw(float[] mvpMatrix) { // pass in the calculated transformation matrix ... // get handle to shape's transformation matrix vPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); // Pass the projection and view transformation to the shader GLES20.glUniformMatrix4fv(vPMatrixHandle, 1, false, mvpMatrix, 0); // Draw the triangle GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount); // Disable vertex array GLES20.glDisableVertexAttribArray(positionHandle); }
Sau khi bạn tính toán và áp dụng chính xác các phép biến đổi phép chiếu và khung hiển thị máy ảnh, các đối tượng đồ hoạ của bạn sẽ được vẽ theo tỷ lệ chính xác và có dạng như sau:
Giờ đây, bạn đã có ứng dụng hiển thị hình dạng theo tỷ lệ chính xác, đã đến lúc thêm chuyển động vào hình dạng.