تثبیت تصاویر دوربین در Android SDK (Kotlin/Java)

ARCore اکنون از تثبیت‌کننده تصویر الکترونیکی (EIS) پشتیبانی می‌کند، که به ایجاد پیش‌نمایش روان دوربین کمک می‌کند. EIS با مشاهده حرکت تلفن با استفاده از ژیروسکوپ و اعمال مش هموگرافی جبرانی در محدوده بافت دوربین که لرزش های جزئی را مقابله می کند، به تثبیت می رسد. EIS فقط در جهت عمودی دستگاه پشتیبانی می شود. همه جهت‌ها در نسخه 1.39.0 ARCore پشتیبانی خواهند شد.

پرس و جو برای پشتیبانی EIS و فعال کردن EIS

برای فعال کردن EIS، جلسه خود را برای استفاده از ImageStabilizationMode.EIS پیکربندی کنید. اگر دستگاه از ویژگی EIS پشتیبانی نمی کند، این باعث می شود که یک استثنا از ARCore پرتاب شود.

جاوا

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 است.

جاوا

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)

هنگامی که EIS خاموش است، مختصات 3 بعدی خروجی معادل مشابه دوبعدی خود هستند، با مقادیر 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 مراجعه کنید.