You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vuforia原生Android:绕Y轴旋转相机时AR视频垂直朝向异常求助

解决Vuforia AR视频始终垂直且朝向镜头的姿态问题

我来帮你搞定这个AR视频姿态的难题!你想要实现的核心效果是:

  • AR视频始终保持垂直状态
  • 绕Y轴移动相机时,视频始终朝向镜头
  • 其他姿态下也维持垂直

你修改了Vuforia的VideoPlayback示例并添加了代码,但实现的旋转效果不符合预期。先看你的原代码,再分析问题,最后给出修正方案:

原代码

Matrix44F inverse_view = SampleMath.Matrix44FInverse(Tool.convertPose2GLMatrix(trackableResult.getPose())); 
// line of sight 
Vec3F los = new Vec3F(-inverse_view.getData()[8], -inverse_view.getData()[9], -inverse_view.getData()[10]); 
Vec3F z_axis = new Vec3F(0, 0, 1); 
Vec3F x_axis = Vec3FNormalize(Vec3FCross(los, z_axis)); 
Vec3F y_axis = Vec3FCross(z_axis, x_axis); 
float[] orientationMatrix = new float[]{ 
    x_axis.getData()[0], x_axis.getData()[1], x_axis.getData()[2], 0, 
    y_axis.getData()[0], y_axis.getData()[1], y_axis.getData()[2], 0, 
    z_axis.getData()[0], z_axis.getData()[1], z_axis.getData()[2], 0, 
    0, 0, 0, 1 
}; 
// calculate the model view projection matrix 
float[] viewMatrixVideo = Tool.convertPose2GLMatrix(trackableResult.getPose()).getData(); 
float[] modelViewVideo = new float[16]; 
Matrix.multiplyMM(modelViewVideo, 0, viewMatrixVideo, 0, orientationMatrix, 0); 
float[] modelViewProjectionVideo = new float[16]; 
Matrix.scaleM(modelViewVideo, 0, targetPositiveDimensions[currentTarget].getData()[0], targetPositiveDimensions[currentTarget].getData()[0] * videoQuadAspectRatio[currentTarget], targetPositiveDimensions[currentTarget].getData()[0]); 
Matrix.multiplyMM(modelViewProjectionVideo, 0, projectionMatrix, 0, modelViewVideo, 0); 

期望效果

期望效果示意图

问题分析

你当前的代码有两个关键问题:

  1. 错误固定世界Z轴:你用new Vec3F(0,0,1)作为基准Z轴,这会导致当跟踪目标本身有姿态变化(比如倾斜)时,视频的垂直基准被打乱,无法保持始终垂直。
  2. 矩阵相乘顺序错误:在OpenGL的坐标体系中,模型矩阵应该先应用自定义旋转,再和视图矩阵相乘,而你现在的顺序是视图矩阵在前,旋转矩阵在后,这会导致姿态计算逻辑混乱。

修正方案

我们需要基于相机视线和世界垂直轴(Y轴)来构建视频的旋转姿态,确保视频始终朝向相机且保持垂直。下面是调整后的代码:

// 获取跟踪目标的姿态矩阵(模型空间到世界空间)
Matrix44F targetPose = Tool.convertPose2GLMatrix(trackableResult.getPose());
float[] targetPoseData = targetPose.getData();

// 提取跟踪目标的位置,我们只调整旋转,保留这个位置
Vec3F targetPosition = new Vec3F(targetPoseData[12], targetPoseData[13], targetPoseData[14]);

// 获取相机的视图矩阵,并计算其逆矩阵(得到世界空间到相机空间的转换)
Matrix44F viewMatrix = Tool.convertPose2GLMatrix(CameraDevice.getInstance().getPose());
Matrix44F worldToCamera = SampleMath.Matrix44FInverse(viewMatrix);
float[] worldToCameraData = worldToCamera.getData();

// 计算相机的前向视线方向(相机的负Z轴)
Vec3F cameraForward = new Vec3F(-worldToCameraData[8], -worldToCameraData[9], -worldToCameraData[10]);
cameraForward = Vec3FNormalize(cameraForward);

// 定义世界空间的垂直轴(Y轴向上,确保视频始终垂直)
Vec3F worldUp = new Vec3F(0, 1, 0);

// 计算视频的右轴:视线方向与世界上方向量的叉乘,确保水平方向正确
Vec3F videoRight = Vec3FNormalize(Vec3FCross(cameraForward, worldUp));

// 计算视频的垂直轴:基于右轴和视线方向的叉乘,保证视频始终垂直
Vec3F videoUp = Vec3FNormalize(Vec3FCross(videoRight, cameraForward));

// 构建视频的旋转矩阵:让视频始终朝向相机,且垂直轴对齐世界Y轴
float[] rotationMatrix = new float[]{
    videoRight.getData()[0], videoRight.getData()[1], videoRight.getData()[2], 0,
    videoUp.getData()[0], videoUp.getData()[1], videoUp.getData()[2], 0,
    -cameraForward.getData()[0], -cameraForward.getData()[1], -cameraForward.getData()[2], 0,
    0, 0, 0, 1
};

// 构建视频的模型矩阵:先应用旋转,再平移到跟踪目标的位置
float[] modelMatrix = new float[16];
Matrix.setIdentityM(modelMatrix, 0);
// 复制旋转矩阵到模型矩阵
System.arraycopy(rotationMatrix, 0, modelMatrix, 0, 12);
// 设置模型矩阵的位置为跟踪目标的位置
modelMatrix[12] = targetPosition.getData()[0];
modelMatrix[13] = targetPosition.getData()[1];
modelMatrix[14] = targetPosition.getData()[2];

// 计算模型视图矩阵:视图矩阵 * 模型矩阵(符合OpenGL的矩阵运算顺序)
float[] modelViewVideo = new float[16];
Matrix.multiplyMM(modelViewVideo, 0, viewMatrix.getData(), 0, modelMatrix, 0);

// 对视频进行缩放,适配跟踪目标尺寸
Matrix.scaleM(modelViewVideo, 0, 
    targetPositiveDimensions[currentTarget].getData()[0], 
    targetPositiveDimensions[currentTarget].getData()[0] * videoQuadAspectRatio[currentTarget], 
    targetPositiveDimensions[currentTarget].getData()[0]);

// 计算最终的模型视图投影矩阵,用于渲染视频
float[] modelViewProjectionVideo = new float[16];
Matrix.multiplyMM(modelViewProjectionVideo, 0, projectionMatrix, 0, modelViewVideo, 0);

关键修正点说明

  • 姿态基准调整:不再依赖固定的世界Z轴,而是用相机视线和世界Y轴来构建视频的方向轴,确保视频始终垂直且朝向相机。
  • 矩阵顺序修正:先构建视频的模型矩阵(旋转+平移),再与视图矩阵相乘,符合OpenGL的坐标转换逻辑(视图矩阵负责将世界空间转换到相机空间,模型矩阵负责将视频模型转换到世界空间)。
  • 保留目标位置:只调整视频的旋转姿态,保持视频始终贴合跟踪目标的位置,不会出现偏移。

内容的提问来源于stack exchange,提问作者Mirza Ahmed Baig

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:45:00