在 Android NDK 上稳定相机图像 (C)

ARCore 现在支持电子防抖 (EIS),该技术有助于生成流畅的相机预览。EIS 采用陀螺仪观察手机的运动情况,并在相机纹理边界内应用补偿单应矩阵网格来达到防抖效果。EIS 仅在设备的纵向模式下受支持。1.39.0 版本的 ARCore 将支持所有屏幕方向。

查询 EIS 支持并启用 EIS

如需启用 EIS,请将会话配置为使用 AR_IMAGE_STABILIZATION_MODE_EIS。如果设备不支持 EIS 功能,会导致 ARCore 抛出异常。

int enableEis = 0;
ArSession_isImageStabilizationModeSupported(
    ar_session, AR_IMAGE_STABILIZATION_MODE_EIS, &enableEis);
if (!enableEis) {
  return;
}
// Create a session config.
ArConfig* ar_config = NULL;
ArConfig_create(ar_session, &ar_config);

// Enable Electronic Image Stabilization.
ArConfig_setImageStabilizationMode(ar_session, ar_config, AR_IMAGE_STABILIZATION_MODE_EIS);
CHECK(ArSession_configure(ar_session, ar_config) == AR_SUCCESS);

// Release config resources.
ArConfig_destroy(ar_config);

转换坐标

开启 EIS 后,渲染程序在渲染相机背景时需要使用修改后的设备坐标和包含 EIS 补偿的匹配纹理坐标。如需获取 EIS 补偿坐标,请使用 ArFrame_transformCoordinates3d,使用 AR_COORDINATES_2D_OPENGL_NORMALIZED_DEVICE_COORDINATES 作为输入,使用 AR_COORDINATES_3D_EIS_NORMALIZED_DEVICE_COORDINATES 作为输出以获取 3D 设备坐标,使用 AR_COORDINATES_3D_EIS_TEXTURE_NORMALIZED 作为输出来获取 3D 纹理坐标。目前,ArFrame_transformCoordinates3d 唯一支持的输入坐标类型是 AR_COORDINATES_2D_OPENGL_NORMALIZED_DEVICE_COORDINATES

int kNumVertices = 4;
// Positions of the quad vertices in clip space (X, Y).
const GLfloat kVertices[] = {
    -1.0f, -1.0f, +1.0f, -1.0f, -1.0f, +1.0f, +1.0f, +1.0f,
};
float transformed_vertices_[4 * 3];
float transformed_uvs_[4 * 3];

ArFrame_transformCoordinates3d(
    session, frame, AR_COORDINATES_2D_OPENGL_NORMALIZED_DEVICE_COORDINATES,
    kNumVertices, kVertices,
    AR_COORDINATES_3D_EIS_NORMALIZED_DEVICE_COORDINATES,
    transformed_vertices_);
ArFrame_transformCoordinates3d(
    session, frame, AR_COORDINATES_2D_OPENGL_NORMALIZED_DEVICE_COORDINATES,
    kNumVertices, kVertices, AR_COORDINATES_3D_EIS_TEXTURE_NORMALIZED,
    transformed_uvs_);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_EXTERNAL_OES, camera_texture_id_);
glUseProgram(camera_program_);
glUniform1i(camera_texture_uniform_, 0);

// Set the vertex positions and texture coordinates.
glVertexAttribPointer(camera_position_attrib_, 3, GL_FLOAT, false, 0,
                      transformed_vertices_);
glVertexAttribPointer(camera_tex_coord_attrib_, 3, GL_FLOAT, false, 0,
                      transformed_uvs_);
glEnableVertexAttribArray(camera_position_attrib_);
glEnableVertexAttribArray(camera_tex_coord_attrib_);

当 EIS 处于关闭状态时,输出的 3D 坐标等同于对应的 2D 坐标,并将 z 值设为不产生任何变化。

修改着色器

计算的 3D 坐标应传递给背景渲染着色器。现在,顶点缓冲区为具有 EIS 的 3D:

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;
}

此外,fragment 着色器需要应用透视校正:

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 示例应用。