تثبيت صور الكاميرا على حزمة تطوير البرامج (SDK) لنظام التشغيل Android (Kotlin/Java)

يتيح ARCore الآن ميزة "التثبيت الإلكتروني للصورة" (EIS)، ما يساعد في إنتاج معاينة سلسة للكاميرا. تحقِّق ميزة "التثبيت الإلكتروني للصورة" التثبيت من خلال رصد حركة الهاتف باستخدام أداة التحكّم في الحركة وتطبيق شبكة إصلاح الصور المجسمة ضمن حدود نسيج الكاميرا الذي يتصدّى للهزات البسيطة. لا تتوفّر ميزة EIS إلا في الوضع العمودي للجهاز. ستكون جميع الاتجاهات متاحة في الإصدار 1.39.0 من ARCore.

طلب الدعم بشأن EIS وتفعيله

لتفعيل EIS، اضبط جلستك لاستخدام ImageStabilizationMode.EIS. إذا كان الجهاز لا يتيح استخدام ميزة EIS، سيؤدي ذلك إلى ظهور استثناء من ARCore.

JavaKotlin
if (!session.isImageStabilizationModeSupported(Config.ImageStabilizationMode.EIS)) {
  return;
}
Config config = session.getConfig();
config.setImageStabilizationMode(Config.ImageStabilizationMode.EIS);
session.configure(config);
if (!session.isImageStabilizationModeSupported(Config.ImageStabilizationMode.EIS)) return
session.configure(
  session.config.apply { imageStabilizationMode = Config.ImageStabilizationMode.EIS }
)

تحويل الإحداثيات

عندما يكون وضع EIS مفعّلاً، يجب أن يستخدم برنامج التقديم إحداثيات الجهاز المعدَّلة وإحداثيات النسيج المطابقة التي تتضمّن تعويض EIS عند عرض خلفية الكاميرا. للحصول على الإحداثيات التي تم تعويضها باستخدام EIS، استخدِم Frame.transformCoordinates3d()، مع استخدام OPENGL_NORMALIZED_DEVICE_COORDINATES كمدخل وEIS_NORMALIZED_DEVICE_COORDINATES كمُخرج للحصول على إحداثيات الجهاز الثلاثية الأبعاد وEIS_TEXTURE_NORMALIZED كمُخرج للحصول على إحداثيات الزخرفة الثلاثية الأبعاد. في الوقت الحالي، نوع الإحداثيات الوحيد المسموح به لإدخال Frame.transformCoordinates3d() هو OPENGL_NORMALIZED_DEVICE_COORDINATES.

JavaKotlin
final FloatBuffer cameraTexCoords =
    ByteBuffer.allocateDirect(COORDS_BUFFER_SIZE_3D)
        .order(ByteOrder.nativeOrder())
        .asFloatBuffer();

final FloatBuffer screenCoords =
    ByteBuffer.allocateDirect(COORDS_BUFFER_SIZE_3D)
        .order(ByteOrder.nativeOrder())
        .asFloatBuffer();

final FloatBuffer NDC_QUAD_COORDS_BUFFER =
    ByteBuffer.allocateDirect(COORDS_BUFFER_SIZE_2D)
        .order(ByteOrder.nativeOrder())
        .asFloatBuffer()
        .put(
            new float[] {
              /*0:*/ -1f, -1f, /*1:*/ +1f, -1f, /*2:*/ -1f, +1f, /*3:*/ +1f, +1f,
            });

final VertexBuffer screenCoordsVertexBuffer =
    new VertexBuffer(render, /* numberOfEntriesPerVertex= */ 3, null);
final VertexBuffer cameraTexCoordsVertexBuffer =
    new VertexBuffer(render, /* numberOfEntriesPerVertex= */ 3, null);

NDC_QUAD_COORDS_BUFFER.rewind();
frame.transformCoordinates3d(
    Coordinates2d.OPENGL_NORMALIZED_DEVICE_COORDINATES,
    NDC_QUAD_COORDS_BUFFER,
    Coordinates3d.EIS_NORMALIZED_DEVICE_COORDINATES,
    screenCoords);
screenCoordsVertexBuffer.set(screenCoords);

NDC_QUAD_COORDS_BUFFER.rewind();
frame.transformCoordinates3d(
    Coordinates2d.OPENGL_NORMALIZED_DEVICE_COORDINATES,
    NDC_QUAD_COORDS_BUFFER,
    Coordinates3d.EIS_TEXTURE_NORMALIZED,
    cameraTexCoords);
cameraTexCoordsVertexBuffer.set(cameraTexCoords);
val COORDS_BUFFER_SIZE_2D = 2 * 4 * Float.SIZE_BYTES
val COORDS_BUFFER_SIZE_3D = 3 * 4 * Float.SIZE_BYTES
val cameraTexCoords =
  ByteBuffer.allocateDirect(COORDS_BUFFER_SIZE_3D)
    .order(ByteOrder.nativeOrder())
    .asFloatBuffer()
val screenCoords =
  ByteBuffer.allocateDirect(COORDS_BUFFER_SIZE_3D)
    .order(ByteOrder.nativeOrder())
    .asFloatBuffer()
val cameraTexCoordsVertexBuffer = VertexBuffer(render, /* numberOfEntriesPerVertex= */ 3, null)
val screenCoordsVertexBuffer = VertexBuffer(render, /* numberOfEntriesPerVertex= */ 3, null)
val NDC_QUAD_COORDS_BUFFER =
  ByteBuffer.allocateDirect(COORDS_BUFFER_SIZE_2D)
    .order(ByteOrder.nativeOrder())
    .asFloatBuffer()
    .apply {
      put(
        floatArrayOf(
          /* 0: */
          -1f,
          -1f,
          /* 1: */
          +1f,
          -1f,
          /* 2: */
          -1f,
          +1f,
          /* 3: */
          +1f,
          +1f
        )
      )
    }
NDC_QUAD_COORDS_BUFFER.rewind()
frame.transformCoordinates3d(
  Coordinates2d.OPENGL_NORMALIZED_DEVICE_COORDINATES,
  NDC_QUAD_COORDS_BUFFER,
  Coordinates3d.EIS_NORMALIZED_DEVICE_COORDINATES,
  screenCoords
)
screenCoordsVertexBuffer.set(screenCoords)

NDC_QUAD_COORDS_BUFFER.rewind()
frame.transformCoordinates3d(
  Coordinates2d.OPENGL_NORMALIZED_DEVICE_COORDINATES,
  NDC_QUAD_COORDS_BUFFER,
  Coordinates3d.EIS_TEXTURE_NORMALIZED,
  cameraTexCoords
)
cameraTexCoordsVertexBuffer.set(cameraTexCoords)

عندما تكون ميزة "التثبيت الإلكتروني للصورة" غير مفعّلة، تكون الإحداثيات الثلاثية الأبعاد الناتجة مساوية لنظيراتها ثنائية الأبعاد، ويتم ضبط قيم z بحيث لا تؤدي إلى أي تغيير.

تعديل تأثيرات التظليل

يجب تمرير الإحداثيات الثلاثية الأبعاد التي تم احتسابها إلى أدوات تظليل العرض في الخلفية. أصبحت الآن مخازن الرؤوس ثلاثية الأبعاد باستخدام EIS:

layout(location = 0) in vec4 a_Position;
layout(location = 1) in vec3 a_CameraTexCoord;
out vec3 v_CameraTexCoord;
void main() {
  gl_Position = a_Position;
  v_CameraTexCoord = a_CameraTexCoord;
}

بالإضافة إلى ذلك، يجب أن يطبّق برنامج تظليل الشرائح تصحيح المنظور:

precision mediump float;
uniform samplerExternalOES u_CameraColorTexture;
in vec3 v_CameraTexCoord;
layout(location = 0) out vec4 o_FragColor;
void main() {
  vec3 tc = (v_CameraTexCoord / v_CameraTexCoord.z);
  o_FragColor = texture(u_CameraColorTexture, tc.xy);
}

اطّلِع على نموذج التطبيق hello_eis_kotlin للحصول على مزيد من التفاصيل.