ARCore 現在支援電子影像穩定技術 (EIS),可提供流暢的相機預覽畫面。EIS 會使用陀螺儀觀察手機的移動情形,並在相機紋理邊界內套用補償的正交投影網格,以抵銷輕微的晃動。EIS 僅支援裝置的直向模式。ARCore 1.39.0 版將支援所有方向。
查詢 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
做為輸出內容,取得 3D 裝置座標,並將 EIS_TEXTURE_NORMALIZED
做為輸出內容,取得 3D 紋理座標。目前 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 後,輸出的 3D 座標會與 2D 座標相同,z 值則會設為不產生變化。
修改著色器
計算的 3D 座標應傳遞至背景算繪著色器。頂點緩衝區現在是 3D 並含有 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 範例應用程式。