ייצוב תמונות מצלמה ב-Android SDK (Kotlin/Java)

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.