带有 Sensor.TYPE_ROTATION_VECTOR 奇怪行为的 Rajawali 旋转相机

Posted

技术标签:

【中文标题】带有 Sensor.TYPE_ROTATION_VECTOR 奇怪行为的 Rajawali 旋转相机【英文标题】:Rajawali rotating camera with Sensor.TYPE_ROTATION_VECTOR strange behavior 【发布时间】:2016-02-23 14:19:21 【问题描述】:

我正在创建一个全景视图,它允许用户通过旋转他的智能手机来环顾四周。为此,我使用了 Rajawali 的 Skybox 和 TYPE_ROTATION_VECTOR 传感器。

我让它工作了,但只有当我向前看时(它实际上是基于我的旋转(偏航))

这是行为:

向前看:yaw = yaw,pitch = pitch,roll = roll 向左看:yaw = yaw,pitch = roll,roll = pitch 向后看:yaw = yaw,pitch = pitch * -1 和 roll = roll * -1。

现在我确实有预感发生了什么事。似乎“相机对象”一直在看同一个方向,即使它看起来没有。这意味着俯仰看起来与滚动相同,但实际上它仍然在俯仰,因为对象没有旋转。我把它比作在飞机上环顾四周。

我需要做什么来解决这个问题?

我有一种感觉,我将不得不使用 lookAt() 来旋转相机,但我不确定如何。

public class SkyboxFragment extends RajawaliFragment implements SensorEventListener 

    public static final String TAG = "SkyBoxFragment";
    private SensorManager mSensorManager;
    private float[] orientationVals = new float[3];
    private float[] mRotationMatrix = new float[16];
    private Sensor mRotVectSensor;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) 
        super.onCreateView(inflater, container, savedInstanceState);

        LinearLayout ll = new LinearLayout(getActivity());
        ll.setOrientation(LinearLayout.VERTICAL);
        ll.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.TOP);
        mSensorManager = (SensorManager) getActivity().getSystemService(
                Context.SENSOR_SERVICE);
        mRotVectSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
        mLayout.addView(ll);
        mSensorManager.registerListener(this,
                mRotVectSensor,
                10000);
        return mLayout;
    

    @Override
    public AExampleRenderer createRenderer() 
        mRenderer = new SkyboxRenderer(getActivity());
        return ((SkyboxRenderer) mRenderer);
    

    @Override
    public void onClick(View v) 

    

    @Override
    public void onSensorChanged(SensorEvent event) 

        if (event.sensor.getType() == Sensor.TYPE_ROTATION_VECTOR) 
            SensorManager.getRotationMatrixFromVector(mRotationMatrix, event.values);
            SensorManager.remapCoordinateSystem(mRotationMatrix, SensorManager.AXIS_X, SensorManager.AXIS_Z, mRotationMatrix);
            SensorManager.getOrientation(mRotationMatrix, orientationVals);
            orientationVals[0] = (float) Math.toDegrees(orientationVals[0]);
            orientationVals[1] = (float) Math.toDegrees(orientationVals[1]) * -1;
            orientationVals[2] = (float) Math.toDegrees(orientationVals[2]) * -1;
            //Log.d(TAG,  "YAW:" + orientationVals[0] + " PITCH:" + orientationVals[1] + "ROLL:" + orientationVals[2]);
        

    

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) 

    

    private final class SkyboxRenderer extends AExampleRenderer implements View.OnClickListener 

        private final Vector3 mAccValues;
        boolean odd = true;

        public SkyboxRenderer(Context context) 
            super(context);
            mAccValues = new Vector3();
        

        @Override
        protected void initScene() 
            getCurrentCamera().setFarPlane(1000);
            /**
             * Skybox images by Emil Persson, aka Humus. http://www.humus.name humus@comhem.se
             */
            try 
                getCurrentScene().setSkybox(R.drawable.posx, R.drawable.negx,
                        R.drawable.posy, R.drawable.negy, R.drawable.posz, R.drawable.negz);
             catch (ATexture.TextureException e) 
                e.printStackTrace();
            
        

        @Override
        protected void onRender(long ellapsedRealtime, double deltaTime) 
            super.onRender(ellapsedRealtime, deltaTime);
            getCurrentCamera().setRotation(orientationVals[2], orientationVals[0], orientationVals[1]);
        

        @Override
        public void onClick(View v) 
            try 
                if (odd) 
                    /**
                     * Skybox images by Emil Persson, aka Humus. http://www.humus.name humus@comhem.se
                     */
                    getCurrentScene().updateSkybox(R.drawable.posx2, R.drawable.negx2,
                            R.drawable.posy2, R.drawable.negy2, R.drawable.posz2, R.drawable.negz2);
                 else 
                    /**
                     * Skybox images by Emil Persson, aka Humus. http://www.humus.name humus@comhem.se
                     */
                    getCurrentScene().updateSkybox(R.drawable.posx, R.drawable.negx,
                            R.drawable.posy, R.drawable.negy, R.drawable.posz, R.drawable.negz);
                
             catch (Exception e) 
                e.printStackTrace();
             finally 
                odd = !odd;
            
        

        public void setAccelerometerValues(float x, float y, float z) 
            mAccValues.setAll(-x, -y, -z);
        
    


【问题讨论】:

【参考方案1】:

你有两个问题。第一个是您描述的问题,但另一个问题是TYPE_ROTATION_VECTOR 的传感器受到附近磁铁(例如手机壳中的磁铁)的影响。

解决磁铁问题

一种解决方案可能是结合使用加速度计和陀螺仪。幸运的是,Google Cardboard SDK 已经把它抽象出来了。

您可以通过使用HeadTracker.createFromContext(this.getActivity()) 实例化com.google.vrtoolkit.cardboard.sensors.HeadTracker 的实例并在其上调用startTracking() 来跟踪当前旋转。

现在您不再需要onSensorChanged。相反,在您的 onRender 中,您可以这样做:

float[] R = new float[16];
headTracker.getLastHeadView(R, 0);

获取旋转矩阵。这解决了您未说明的磁场问题。

让相机正确环顾四周

使用此旋转矩阵将相机指向正确方向的最简单方法是将其转换为org.rajawali3d.math.Quaternion,然后调用getCurrentCamera().setCameraOrientation(quaternion);

float[16] 到四元数的转换可能很难正确完成,但 Google Cardboard SDK 再次为您做到了。在这种情况下,它位于不再使用的旧类的源代码中:HeadTransform。

您可以轻松地将该代码修改为return new Quaternion(w, x, y, z);

现在,如果您没有将 orientationVals[1]orientationVals[2] 乘以 -1,这将导致与当前代码相同的问题。

然而,这个问题很容易通过反转旋转矩阵来解决。这将导致onRender 中的以下代码(假设getQuaternion(R) 返回org.rajawali3d.math.Quaternion):

@Override
protected void onRender(long ellapsedRealtime, double deltaTime) 
    super.onRender(ellapsedRealtime, deltaTime);

    float[] R = new float[16];
    headTracker.getLastHeadView(R, 0);

    android.opengl.Matrix.invertM(R, 0, R, 0);

    Quaternion q = getQuaternion(R);

    getCurrentCamera().setCameraOrientation(q);

【讨论】:

以上是关于带有 Sensor.TYPE_ROTATION_VECTOR 奇怪行为的 Rajawali 旋转相机的主要内容,如果未能解决你的问题,请参考以下文章

带有和不带有聚合的 sql 查询

如何翻转正面带有标签而背面带有另一个标签的视图 - 参见图片

CakePHP 如何处理带有/不带有 'id' 字段的 HABTM 表?

带有滚动的 Div 和带有绝对位置的内容

带有 RecyclerView 的 DialogFragment 比带有 Recyclerview 的 Fragment 慢

访问控制允许带有和不带有 www 的来源