تطبيق مرات العرض والكاميرات

في بيئة OpenGL ES، تتيح لك عروض الإسقاط والكاميرا عرض الكائنات المرسومة بطريقة تشبه إلى حدٍ أكبر الطريقة التي ترى بها الأشياء المادية بالعينين. تتم محاكاة الرؤية الجسدية هذه من خلال التحويلات الرياضية لإحداثيات الكائنات المرسومة:

  • التوقع: تعمل عملية التحويل هذه على ضبط إحداثيات الكائنات المرسومة استنادًا إلى عرض وارتفاع GLSurfaceView حيث يتم عرضها. وبدون هذه العملية الحسابية، ستتأثر الكائنات التي ترسمها OpenGL ES بالنسب غير المتساوية لنافذة العرض. يجب عادةً حساب تحويل الإسقاط فقط عند تحديد نِسب عرض OpenGL أو تغييرها في طريقة onSurfaceChanged() الخاصة بالعارض. ولمزيد من المعلومات حول إسقاطات OpenGL ES وربطها بالإحداثيات، يُرجى الاطّلاع على ربط إحداثيات الكائنات المرسومة.
  • عرض الكاميرا - تعمل عملية التحويل هذه على تعديل إحداثيات العناصر المرسومة استنادًا إلى موضع الكاميرا الافتراضي. تجدر الإشارة إلى أنّ OpenGL ES لا يحدّد كائن كاميرا فعليًا، ولكنه يوفر طرقًا مساعدة تحاكي الكاميرا عن طريق تحويل عرض العناصر المرسومة. قد يتم احتساب تحويل عرض الكاميرا مرة واحدة فقط عند إنشاء GLSurfaceView، أو قد يتم تغييره ديناميكيًا استنادًا إلى إجراءات المستخدم أو وظيفة تطبيقك.

يشرح هذا الدرس طريقة إنشاء إسقاط وعرض الكاميرا وتطبيقه على الأشكال المرسومة في GLSurfaceView.

تحديد التوقع

يتم حساب بيانات تحويل التوقع في طريقة onSurfaceChanged() لفئة GLSurfaceView.Renderer. يأخذ الرمز البرمجي التالي المثالي ارتفاع وعرض GLSurfaceView ويستخدمه لتعبئة Matrix لتحويل الإسقاط باستخدام طريقة 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);
}

يعمل هذا الرمز على تعبئة مصفوفة إسقاط، mProjectionMatrix، والتي يمكنك دمجها بعد ذلك مع تحويل عرض الكاميرا في طريقة onDrawFrame()، والذي يتم عرضه في القسم التالي.

ملاحظة: مجرد تطبيق تحويل الإسقاط على كائنات الرسم لديك يؤدي عادةً إلى عرض فارغ جدًا. بشكل عام، يجب عليك أيضًا تطبيق تحويل عرض الكاميرا لكي يظهر أي شيء على الشاشة.

تحديد عرض الكاميرا

أكمل عملية تحويل الكائنات المرسومة عن طريق إضافة تحويل عرض الكاميرا كجزء من عملية الرسم في العارض. في المثال التالي للرمز، يتم احتساب تحويل عرض الكاميرا باستخدام طريقة Matrix.setLookAtM() ثم دمجه مع مصفوفة الإسقاط التي تم حسابها سابقًا. ثم يتم تمرير مصفوفات التحويل المدمجة إلى الشكل المرسوم.

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);
}

تطبيق تغييرات الإسقاط والكاميرا

لاستخدام مصفوفة تحويل عرض الكاميرا والإسقاط المركّب المعروضة في أقسام المعاينات، عليك أولاً إضافة متغيّر مصفوفة إلى أداة تظليل الرأس المحدّدة سابقًا في فئة 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;

    ...
}

بعد ذلك، عدِّل طريقة draw() لكائنات الرسومات لقبول مصفوفة التحويل المدمجة وتطبيقها على الشكل:

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);
}

بمجرد حساب وتطبيق تحويلات الإسقاط وعرض الكاميرا بشكل صحيح، يتم رسم الكائنات الرسومية بنسب صحيحة وينبغي أن تبدو كما يلي:

الشكل 1. مثلث تم رسمه مع تطبيق الإسقاط وعرض الكاميرا.

الآن بعد أن أصبح لديك تطبيق يعرض الأشكال الخاصة بك بنسب صحيحة، فقد حان الوقت لإضافة حركة إلى أشكالك.