ARCore est désormais compatible avec la stabilisation électronique de l'image (EIS), qui permet de produire un aperçu de l'appareil photo fluide. La stabilisation par EIS consiste à observer le mouvement du téléphone à l'aide du gyroscope et à appliquer un maillage d'homogénie de compensation dans les limites de la texture de l'appareil photo pour contrer les tremblements mineurs. La stabilisation électronique de l'image n'est disponible que dans l'orientation portrait de l'appareil. Toutes les orientations seront prises en charge dans la version 1.39.0 d'ARCore.
Demander l'assistance EIS et l'activer
Pour activer l'EIS, configurez votre session pour qu'elle utilise ImageStabilizationMode.EIS
. Si l'appareil n'est pas compatible avec la fonctionnalité EIS, une exception est générée à partir d'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 } )
Transformer des coordonnées
Lorsque l'EIS est activé, le moteur de rendu doit utiliser les coordonnées de l'appareil modifiées et les coordonnées de texture correspondantes qui intègrent la compensation EIS lors de l'affichage de l'arrière-plan de l'appareil photo. Pour obtenir les coordonnées compensées par l'EIS, utilisez Frame.transformCoordinates3d()
, en utilisant OPENGL_NORMALIZED_DEVICE_COORDINATES
comme entrée et EIS_NORMALIZED_DEVICE_COORDINATES
comme sortie pour obtenir les coordonnées de l'appareil 3D et EIS_TEXTURE_NORMALIZED
comme sortie pour obtenir les coordonnées de la texture 3D. Pour le moment, le seul type de coordonnées d'entrée compatible avec Frame.transformCoordinates3d()
est 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)
Lorsque l'EIS est désactivé, les coordonnées 3D de sortie sont équivalentes à leurs homologues 2D, et les valeurs z sont définies pour ne produire aucun changement.
Modifier les nuanceurs
Les coordonnées 3D calculées doivent être transmises aux nuanceurs de rendu en arrière-plan. Les tampons de sommets sont désormais en 3D avec 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;
}
De plus, le nuanceur de fragment doit appliquer une correction de perspective:
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);
}
Pour en savoir plus, consultez l'application exemple hello_eis_kotlin.