ตอนนี้ ARCore รองรับระบบป้องกันภาพสั่นไหวแบบอิเล็กทรอนิกส์ (EIS) ซึ่งจะช่วยให้การแสดงตัวอย่างจากกล้องเป็นไปอย่างราบรื่น EIS รักษาภาพสั่นไหวด้วยการสังเกตการเคลื่อนไหวของโทรศัพท์โดยใช้เครื่องวัดการหมุนและใช้ Mesh ของการชดเชยค่าชดเชยภายในขอบเขตของพื้นผิวกล้องซึ่งจะช่วยรับมือกับการสั่นสะเทือนของผู้เยาว์ EIS รองรับเฉพาะในแนวตั้งของอุปกรณ์ ARCore รุ่น 1.39.0 จะรองรับการวางแนวทั้งหมด
การค้นหาการรองรับ 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
เป็นเอาต์พุตเพื่อรับพิกัดของอุปกรณ์ 3 มิติและ EIS_TEXTURE_NORMALIZED
เป็นเอาต์พุตเพื่อรับพิกัดพื้นผิว 3 มิติ ปัจจุบันพิกัดประเภทอินพุตเดียวที่สนับสนุนสำหรับ 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 ปิดอยู่ พิกัด 3 มิติของเอาต์พุตจะเทียบเท่ากับค่าคู่ 2 มิติ โดยตั้งค่า z ไว้ไม่ให้มีการเปลี่ยนแปลง
แก้ไขตัวปรับแสงเงา
พิกัด 3 มิติที่คำนวณได้ควรส่งผ่านไปยังตัวปรับแสงเงาพื้นหลัง ตอนนี้บัฟเฟอร์ Vertex เป็นแบบ 3 มิติด้วย 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