يوفّر تطبيق ARCore الآن ميزة التثبيت الإلكتروني للصورة (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 لمزيد من التفاصيل.