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

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

طلب دعم EIS وتفعيل EIS

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

Java

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

Kotlin

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.

Java

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

Kotlin

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)

عند إيقاف تشغيل EIS، تكون إحداثيات الإخراج ثلاثية الأبعاد مكافئة لنظيراتها ثنائية الأبعاد، مع تعيين قيم 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 لمزيد من التفاصيل.