草庐IT

android - 四元数旋转不能正常工作

coder 2023-12-14 原文

在 OpenGL ES 1 for android 中,我有一个由 27 个较小的立方体组成的 Rubic 立方体。我想要旋转导致特定的小立方体恰好位于视点前方。所以我需要两个向量。一个是从对象原点到特定立方体的向量。另一个是从原点到视点的向量。然后它们的叉积给了我旋转的轴,点积给了我角度。

我将 (0,0,1) - 这是从原点到世界坐标中的视点的向量 - 转换为对象坐标。这是代码:

    matrixGrabber.getCurrentModelView(gl);
    temporaryMatrix.set(matrixGrabber.mModelView);

    inputVector[0] = 0f; 
    inputVector[1] = 0f;
    inputVector[2] = 1f;
    inputVector[3] = 1f;
    Matrix.multiplyMV(resultVector, 0, temporaryMatrix.InvertMatrix(), 0, inputVector,0);
    resultVector[0]/=resultVector[3];
    resultVector[1]/=resultVector[3];
    resultVector[2]/=resultVector[3];

    inputVector = ..... // appropriate vector due to user-selection 

    axis = Vector.normalized(Vector.crossProduct(Vector.normalized(inputVector), Vector.normalized(resultVector)));
    degree = (float)Math.toDegrees(Math.acos(Vector.dot(Vector.normalized(inputVector), Vector.normalized(resultVector))));

我使用两个四元数进行旋转。每次用户选择一个 Action 时,应该发生一个轮换。这是代码:
    Quaternion currentRotation = new Quaternion();
    Quaternion temporaryRotation = new Quaternion();
    .
    .
    .
     currentRotation = (currentRotation).mulLeft(temporaryRotation.set(axis, degree));
     currentRotation.toMatrix(matrix);
     gl.glMatrixMode(GL10.GL_MODELVIEW);
     gl.glMultMatrixf(matrix, 0);

现在的问题是它在第一次旋转时效果很好。无论第一次轮换是什么。它运行良好,但对于下一次旋转,它似乎得到了错误的轴和度数。

例如,如果坐标系是
  • X 右 (1,0,0)
  • Y 向上 (0,1,0)
  • Z-in (0,0,1)

  • 然后第一次逆时针 (CCW) 旋转 X 90 度产生
  • X'-右 (1,0,0)
  • Y'-in (0,0,1)
  • Z'-向下 (0,-1,0)

  • 绕 Z 轴逆时针 90 度的第二次旋转产生
  • X'-in (0,1,0)
  • Y'-左 (-1,0,0)
  • Z'-向下 (0,-1,0)

  • 但我期待
  • X 向上 (0,1,0)
  • Y 输入 (0,0,1)
  • Z-右(1,0,0)

  • 我认为问题在于 resultVector(我使用的第二个向量从原点到视点)没有正确转换。有谁知道如何将世界坐标转换为对象坐标?有谁知道当物体旋转时我们如何确定物体坐标?

    最佳答案

    好吧,昨天我决定编写 Rubic Cube 拼图,因为我过去尝试过的任何拼图对我来说都非常不舒服,最后我有一些心情/时间自己编写代码。当我完成它时,这里是我的见解:

  • 魔方表示

    我不认为四元数是一个好的选择。相反,我更喜欢:
  • 4x4 uniform transform matrices

  • 所以我最终得到了 3*3*3=27 的列表变换矩阵加上一个用于整个立方体旋转的附加矩阵。在起始状态下,所有子立方体都有单位旋转部分,原点设置为覆盖{ -1 , 0 ,+1 }的所有组合。填充整个魔方(每个子立方体网格的大小为 1.0 并以 (0,0,0) 为中心)



    我的多维数据集在 C++ 代码中定义如下:
    reper cube[27]; // reper is transform matrix
    
  • 界面

    我希望控制和查看尽可能接近真实的东西。因此,旋转由鼠标通过单击目标子立方体(在 area0area1 中)来控制,然后从鼠标拖动的方向决定哪个轴旋转以及向哪个方向旋转。

    从起始位置没有问题(因为即使您的代码也适用于此)。问题从下一次旋转开始(尤其是在改变旋转轴时),因为局部坐标系已经改变。全局 View 旋转也是如此,因为它会弄乱所有这些。
  • 如何补救不断变化的局部坐标系?

    我想出了一个模糊的解决方案,我首先从每个坐标系匹配轴。要检测哪个轴是哪个轴,我只需对查询方向与变换矩阵的所有轴进行点积,然后选择 abs 点积最高的那个轴。该符号仅说明坐标系是否相反(意味着应该反转旋转)。

    C++ OpenGL 样式矩阵看起来像这样:

    void RubiCube::axises_unit(reper &rep,int &x,int &y,int &z,int &sx,int &sy,int &sz)
        {
        int i;
        double p[3],xyz[3][3],a,b;
        rep.axisx_get(xyz[0]);
        rep.axisy_get(xyz[1]);
        rep.axisz_get(xyz[2]);
        vector_ld(p,1.0,0.0,0.0); for (b=0.0,i=0;i<3;i++) { a=vector_mul(xyz[i],p); if (fabs(a)>=fabs(b)) { x=i; b=a; } } sx=+1; if (b<0) sx=-1;
        vector_ld(p,0.0,1.0,0.0); for (b=0.0,i=0;i<3;i++) { a=vector_mul(xyz[i],p); if (fabs(a)>=fabs(b)) { y=i; b=a; } } sy=+1; if (b<0) sy=-1;
        vector_ld(p,0.0,0.0,1.0); for (b=0.0,i=0;i<3;i++) { a=vector_mul(xyz[i],p); if (fabs(a)>=fabs(b)) { z=i; b=a; } } sz=+1; if (b<0) sz=-1;
        }
    

    哪里reper是包含直接和逆变换矩阵的类。 get_axis只需查看直接矩阵内部并返回选定的轴方向单位向量。 vector_mul是点积和 vector_ld只需用 x,y,z 填充 3D 矢量坐标。

    因为我也得到了全局立方体矩阵,它不是与单位矩阵对齐的(因为它被旋转,所以 View 看起来像上图)然后我需要对特殊向量(初始 View 矩阵值)进行这个轴匹配在我的情况下就是这个:

    void RubiCube::axises_obj(reper &rep,int &x,int &y,int &z,int &sx,int &sy,int &sz)
        {
        int i;
        double p[3],xyz[3][3],a,b;
        rep.axisx_get(xyz[0]);
        rep.axisy_get(xyz[1]);
        rep.axisz_get(xyz[2]);
        vector_ld(p,+0.707,-0.299,-0.641); for (b=0.0,i=0;i<3;i++) { a=vector_mul(xyz[i],p); if (fabs(a)>=fabs(b)) { x=i; b=a; } } sx=+1; if (b<0) sx=-1;
        vector_ld(p,-0.000,-0.906,+0.423); for (b=0.0,i=0;i<3;i++) { a=vector_mul(xyz[i],p); if (fabs(a)>=fabs(b)) { y=i; b=a; } } sy=+1; if (b<0) sy=-1;
        vector_ld(p,-0.707,-0.299,-0.641); for (b=0.0,i=0;i<3;i++) { a=vector_mul(xyz[i],p); if (fabs(a)>=fabs(b)) { z=i; b=a; } } sz=+1; if (b<0) sz=-1;
        }
    

    这两个函数都返回哪个轴是哪个 x,y,z如果方向与单位变换矩阵相反(sx,sy,sz)。
  • 切片旋转

    这是拼图的核心。它简单地绕轴旋转切片。这用于动画,因此角度步长很小(我使用 9 度),但整个转弯必须总共 90 度,否则魔方会破裂。

    void RubiCube::cube_rotate(int axis,int slice,double ang)
        {
        int j,k,a[3],s[3];
        double p[3],p0[3]={0.0,0.0,0.0},lang;
        reper *r;
        _redraw=true;
        for (k=0;k<27;k++)
            {
            r=&cube[k];
            // local axis,sign
            axises_unit(*r,a[0],a[1],a[2],s[0],s[1],s[2]);
            // lang is local signed angle change
            lang=ang; if (s[axis]<0) lang=-lang;
            // select slice
            r->gpos_get(p);
            j=round(p[axis]+1.0);
            if (j!=slice) continue;
            // rotate global position
            if (axis==0) vector_rotx(p0,p,+ang);
            if (axis==1) vector_roty(p0,p,-ang);
            if (axis==2) vector_rotz(p0,p,+ang);
            r->gpos_set(p);
            // rotate local cube orientation
            if (a[axis]==0) r->lrotx(-lang);
            if (a[axis]==1) r->lroty(-lang);
            if (a[axis]==2) r->lrotz(-lang);
            }
        }
    

    哪里reper::gpos_get返回矩阵原点作为 3D 向量(点)和 reper::gpos_set基本上设置新的矩阵位置。 vector_rotx(p0,p,a)旋转矢量 p附近p0和轴 x按角度 a . +/-标志仅用于匹配来自 reper 的旋转类(我在某处有所不同)。 reper::lrotx旋转 reper围绕其本地x轴有关更多信息,请参阅第一个链接。

    如您所见,我直接使用每个矩阵原点坐标作为拓扑来选择切片立方体。

  • 在这里你可以试试我的演示: Win32+OpenGL Rubic Cube Demo

    这里是一些转弯的动画 gif:



    [Edit1] 我在我的 RubiCube 中添加了简单的求解器

    为了实现一个求解器,我添加了表面平面颜色贴图(在左边......中间的方块是我使用的边的名称和索引)从我的 计算出来的魔方内部代表。我还为求解器添加了内部命令 que(右侧的轴和方向):



    每个命令由 2 个字符串表示:
    edge slice  CW: R L U D F B
    edge slice CCW: R'L'U'D'F'B'
    mid  slice  CW: R0L0U0D0F0B0
    whole cube  CW: RcLcUcDcFcBc
    

    map 如下所示:
    int map[6][3][3];
    

    哪里map[side][u][v]包含侧面正方形的颜色 s , 行 u和专栏v .我实现了简单 7 steps solution (就像人类解决真正的立方体一样):


  • 输入状态(不是一步)
  • 白叉黄中(黄中朝前)
  • 白色十字(白色中间朝前)
  • 白色边角(白色面朝下)
  • 中间层(使用前 3 个命令)
  • 顶层黄色十字(使用第四个命令)
  • 重新排序交叉使边匹配(第 5 个命令)并重新排序角(第 6 个命令)
  • 定向顶层角以完成立方体(第 7 个命令)

  • 求解器很简单,对字符串进行操作(未优化),所以它有点慢,但无论如何,完整的解决方案在我的设置中最多需要 50 毫秒。您可以在这里尝试升级的演示:
  • Win32+OpenGL Rubic Cube Demo with solver

  • 解决时可能仍然存在一些未定义的情况(由于代码中的错误或遗漏情况)。在这种情况下,应用程序会很粗糙(尚未实现看门狗)。 key 在包含的文本文件中进行了描述。

    我做了轻量级的求解器(大约 300 行代码),所以找到的解决方案远非最佳。例如,代替测试 4 个角,我只测试一个角并循环旋转立方体,导致不必要的转弯。其中一些后来被过滤掉了,但人类(我的)解决方案的平均次数高达 200 圈,而这个求解器却返回了 300 圈(在最坏的情况下,我直到现在才发现)。

    关于android - 四元数旋转不能正常工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11467919/

    有关android - 四元数旋转不能正常工作的更多相关文章

    1. ruby-on-rails - 由于 "wkhtmltopdf",PDFKIT 显然无法正常工作 - 2

      我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-

    2. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

      我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

    3. ruby - 无法让 RSpec 工作—— 'require' : cannot load such file - 2

      我花了三天的时间用头撞墙,试图弄清楚为什么简单的“rake”不能通过我的规范文件。如果您遇到这种情况:任何文件夹路径中都不要有空格!。严重地。事实上,从现在开始,您命名的任何内容都没有空格。这是我的控制台输出:(在/Users/*****/Desktop/LearningRuby/learn_ruby)$rake/Users/*******/Desktop/LearningRuby/learn_ruby/00_hello/hello_spec.rb:116:in`require':cannotloadsuchfile--hello(LoadError) 最佳

    4. ruby-on-rails - rspec should have_select ('cars' , :options => ['volvo' , 'saab' ] 不工作 - 2

      关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion在首页我有:汽车:VolvoSaabMercedesAudistatic_pages_spec.rb中的测试代码:it"shouldhavetherightselect"dovisithome_pathit{shouldhave_select('cars',:options=>['volvo','saab','mercedes','audi'])}end响应是rspec./spec/request

    5. ruby-on-rails - s3_direct_upload 在生产服务器中不工作 - 2

      在Rails4.0.2中,我使用s3_direct_upload和aws-sdkgems直接为s3存储桶上传文件。在开发环境中它工作正常,但在生产环境中它会抛出如下错误,ActionView::Template::Error(noimplicitconversionofnilintoString)在View中,create_cv_url,:id=>"s3_uploader",:key=>"cv_uploads/{unique_id}/${filename}",:key_starts_with=>"cv_uploads/",:callback_param=>"cv[direct_uplo

    6. ruby-on-rails - 无法让 rspec、spork 和调试器正常运行 - 2

      GivenIamadumbprogrammerandIamusingrspecandIamusingsporkandIwanttodebug...mmm...let'ssaaay,aspecforPhone.那么,我应该把“require'ruby-debug'”行放在哪里,以便在phone_spec.rb的特定点停止处理?(我所要求的只是一个大而粗的箭头,即使是一个有挑战性的程序员也能看到:-3)我已经尝试了很多位置,除非我没有正确测试它们,否则会发生一些奇怪的事情:在spec_helper.rb中的以下位置:require'rubygems'require'spork'

    7. ruby - JetBrains RubyMine 3.2.4 调试器不工作 - 2

      使用Ruby1.9.2运行IDE提示说需要gemruby​​-debug-base19x并提供安装它。但是,在尝试安装它时会显示消息Failedtoinstallgems.Followinggemswerenotinstalled:C:/ProgramFiles(x86)/JetBrains/RubyMine3.2.4/rb/gems/ruby-debug-base19x-0.11.30.pre2.gem:Errorinstallingruby-debug-base19x-0.11.30.pre2.gem:The'linecache19'nativegemrequiresinstall

    8. 旋转矩阵的几何意义 - 2

      点向量坐标矩阵的几何意义介绍旋转矩阵的几何含义之前,先介绍一下点向量坐标矩阵的几何含义点:在一维空间下就是一个标量,如同一条直线上,以任意某一个位置为0点,以一定的尺度间隔为1,2,3...,相反方向为-1,-2,-3...;如此就形成了一维坐标系,这时候任何一个点都可以用一个数值表示,如点p1=5,即即从原点出发沿着x轴正方向移动5个尺度;点p2=-3,负方向移动3个尺度;     在一维坐标系上过原点做垂直于一维坐标系的直线,则形成了二维坐标系,此时描述一个点需要两个数值来表示点p3=(3,2),即从原点出发沿着x轴正方向移动3个尺度,在此基础上沿着y轴正方向移动两个尺度的位置就是点p3。

    9. Unity 3D 制作开关门动画,旋转门制作,推拉门制作,门把手动画制作 - 2

      Unity自动旋转动画1.开门需要门把手先动,门再动2.关门需要门先动,门把手再动3.中途播放过程中不可以再次进行操作觉得太复杂?查看我的文章开关门简易进阶版效果:如果这个门可以直接打开的话,就不需要放置"门把手"如果门把手还有钥匙需要旋转,那就可以把钥匙放在门把手的"门把手",理论上是可以无限套娃的可调整参数有:角度,反向,轴向,速度运行时点击Test进行测试自己写的代码比较垃圾,命名与结构比较拉,高手轻点喷,新手有类似的需求可以拿去做参考上代码usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;u

    10. ruby - `rescue $!` 是如何工作的? - 2

      我知道全局变量$!包含最新的异常对象,但我对下面的语法感到困惑。谁能帮助我理解以下语法?rescue$! 最佳答案 此构造可防止异常停止您的程序并使堆栈跟踪冒泡。它还会将该异常作为值返回,这很有用。a=get_me_datarescue$!在此行之后,a将保存请求的数据或异常。然后您可以分析该异常并采取相应措施。defget_me_dataraise'Nodataforyou'enda=get_me_datarescue$!puts"Executioncarrieson"pa#>>Executioncarrieson#>>#更现实的

    随机推荐