2012-07-13 2 views
4

android 용 OpenGL ES 1에서 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)))); 

두 회전 쿼터니언을 사용합니다. 사용자가 액션을 선택할 때마다 그 회전 중 하나가 발생해야합니다. 코드는 다음과 같습니다.

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 인 (0, X 90도 반 시계 방향 (CCW 약 0,1)

먼저 회전)

  • X를 생성 '- 오른쪽 (1,0,0)
  • '추정치 인 (0,0,1)
  • Z '다운 (0, -1, 0)

및 Z의 주위에 제 2 회전 도 90는 CCW

  • 는 x'- 인 (0,1,0)
  • '추정치 왼쪽 (-1,0,0)
  • Z'다운 (0, -1, 0을 생성)

하지만 기대

  • X 업 (0,1,0)
  • Y-에서 (0,0,1)
  • Z-오른쪽 (1,0,0)

문제는 resultVector (원점에서 시점으로 나오는 두 번째 벡터)가 제대로 변환되지 않는다고 생각합니다. 세계 좌표를 어떻게 개체 좌표로 변환 할 수 있습니까? 누구든지 객체가 회전했을 때 객체 좌표를 어떻게 결정할 수 있는지 알고 있습니다.

+0

나는 귀하의 문제를 완전히 이해한다고 말할 수없고 출력물을 게시하지 않았습니다. 이 시나리오를 알려주십시오. (생산을 시도하십시오) : 좌표계가 X-right (1,0,0) , Y-up (0,1,0), Z-in (0,0,1) 먼저 시계 반대 방향으로 90 ° 시계 반대 방향 (CCW)으로 회전 적용 결과는 X'- 오른쪽 (1,0, 0 ', 0', 0 ', 0', 0 ', 0', 0 ', 0', 0 ', 0', 0 ' , 0), Y'-left (-1,0,0), Z'-down (0, -1,0). 그리고 X-up (0,1,0), Y-in (0,0,1), Z-right (1,0,0)가 예상됩니다. 어떻게됩니까? –

+0

@MaticOblak 네, 네, 맞습니다. 무슨 일이 일어 났는지 정확하게 설명합니다. 나는 어떻게해야합니까? 왜 이런 일이 일어 났습니까? –

+0

@MaticOblak 질문을 편집하고 예제를 추가했습니다. –

답변

1

이 변형을 모델에 적용하면 (케이스 회전) 기본 벡터가 회전합니다. 마치 좌표계를 회전 시키거나 모델의 첫 번째 사람보기에서 보는 것처럼 생각하면됩니다. 당신이 만드는 모든 변환은 다음 변환에 영향을 미칩니다.

일반적으로 자체 좌표계를 유지하려는 경우 큐브를 회전하는 대신 카메라를 큐브 주위로 이동하는 것이 좋습니다. API 또는 웹에서 "lookAt"메서드를 찾을 수있을 것으로 확신합니다. cameraPosition, lookAtPoint, upVector의 세 가지 벡터가 필요합니다. 이 접근법으로 큐브를 "lookAtPoint"인 (0,0,0)에 배치 할 수 있습니다. 첫 번째 cameraPosition은 (0,0, -1)과 같아야하고 첫 번째 upVector는 (0,1,0)이어야합니다. 이제 운동 (당신은 아마 단지/아래 입력으로까지 왼쪽/오른쪽 사용) : 를 위/아래를 이동하려면 (X 주위 회전) 옆 다음을 수행합니다 :

originalDistance = (cameraPosition-objectPosition).lenght 
leftVector = normalizedVector(crossProduct(camearPosition, upVector))//generaly cameraPosition-objectPosition 
camearPosition = cameraPosition + upVector*inputScalar //inputScalar should be a small floating value 
cameraPosition = normalizedVector(cameraPosition)*originalDistance //put camera to original distance from object 
upVector = normalizedVector(crossProduct(cameraPosition, leftVector))//generaly cameraPosition-objectPosition 

왼쪽으로 이동하려면를/오른쪽 (X 주위 회전) 다음에 다음을 수행하는 (열심히하여이 쓰고 나는 실수를하면 행복이 말해)

originalDistance = (cameraPosition-objectPosition).lenght 
leftVector = normalizedVector(crossProduct(camearPosition, upVector))//generaly cameraPosition-objectPosition 
camearPosition = cameraPosition + leftVector*inputScalar //inputScalar should be a small floating value 
cameraPosition = normalizedVector(cameraPosition)*originalDistance //put camera to original distance from object 
leftVector = normalizedVector(crossProduct(cameraPosition, upVector))//generaly cameraPosition-objectPosition 
upVector = normalizedVector(crossProduct(cameraPosition, leftVector))//generaly cameraPosition-objectPosition 

이 일반적으로 .. 문제를 해결해야

을 객체 자체를 회전시키는 접근 방식에 관해서는, 당신은 당신의 무언가가 무엇인지 알아 내야합니다. 객체 자체의 좌표계에서 회전하고 그 주위를 회전시킵니다. 수학 능력이 있으면 꽤 쉽습니다. 그런 다음 다른 각도 (X, Y)를 정의하고 입력을 통해 직접 변경하고 (1,0,0, X) 및 (0,1,0, Y) 쿼터니언을 사용할 수도 있지만 문제가 될 수 있습니다. 이 접근법은 Y가 90 도일 때 ..

나는 이것이 도움이되기를 바란다.

+0

당신의 노력에 너무 감사드립니다. 하지만 실제로 당신의 대답은 내 문제를 해결하지 못합니다. 기본 벡터도 객체 자체와 함께 회전한다는 것을 알기 때문에 modelViewMatrix의 역수에 곱하여 두 번째 벡터 (원점에서 시점으로 오는 벡터)를 변환합니다. –

+0

문제는 resultVector (원점에서 시점으로 나오는 두 번째 벡터)가 제대로 변환되지 않는다고 생각합니다. 세계 좌표를 어떻게 개체 좌표로 변환 할 수 있는지 알고 계십니까? 오브젝트가 회전 할 때 오브젝트 좌표를 어떻게 결정할 수 있는지 알고 있습니까? –

+0

첫 번째 회전에서만 잘 작동합니다. 나는 완전히 혼란스러워. 왜 그것이 다음에 작동하지 않습니까? 처음에 문제가있는 이유는 무엇입니까? 당신은 내가 ArcBall 회전을 구현했고 사용자가 이동 액션으로 큐브를 회전시킬 수 있고 사용자가 회전해야하는 특정 큐브를 탭할 때 벡터를 변환해야한다는 것을 알고 있습니다. –

2

어제 나는 과거에 시도한 것이 정말 불편해서 Ruby Cube 퍼즐을 코딩하기로 마음 먹었습니다. 나는이 좋은 선택으로 쿼터니언이 표시되지 않는

  1. Rubic 큐브 표현 : 나는 그것을 마무리 이미 여기 내 통찰력이다. 대신에 나는 더 편안 해요 :

    그래서 내가 3*3*3=27 변환 행렬의 목록을 더한 전체 큐브 회전에 대한 추가 하나 끝났다. 상태를 시작하는 모든 서브 큐브 부 회전부를 상기 원점은 전체 Rubic 큐브 기입 { -1 , 0 ,+1 }의 모든 조합을 덮도록 설정된다 (각 서브 큐브 메쉬 크기 1.0을 주위 (0,0,0) 중심)

    axises

    내 큐브 C++ 코드에는 다음과 같이 정의한다 :

    reper cube[27]; // reper is transform matrix 
    
  2. GUI

    나는 내가 할 수있는 한 통제하고 보는 것이 진짜에 가깝기를 바랐다. 따라서 회전은 대상 서브 큐브 (area0 또는 area1)를 클릭 한 다음 마우스 드래그 방향에서 어떤 축이 회전하고 어느 방향으로 결정되는지 결정하기 만하면 마우스로 제어됩니다.

    시작 위치에서 아무런 문제가 없습니다 (코드가 잘 작동하기 때문에).로컬 좌표계가 이미 변경 되었기 때문에 다음 회전에서 문제가 발생합니다 (특히 회전축을 변경하는 경우). 이 모든 것을 망칠 것이기 때문에 글로벌 뷰 회전에서도 마찬가지입니다.

  3. 로컬 좌표계 변경 문제를 해결하려면 어떻게해야합니까?

    각 좌표계에서 축과 처음 일치시키는 모호한 솔루션이 떠오릅니다. 어떤 축이 있는지 알아 내려면 변환 행렬의 모든 축 대 쿼리 한 방향의 내적을 단순히 수행하고 가장 높은 복점 곱을 갖는 축을 선택하십시오. 기호는 좌표계가 반대인지 (회전이 반전되어야 함을 의미) 알려줍니다. 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가 직접 invers 변환 매트릭스를 포함하는 클래스입니다. get_axis 그냥 직접 매트릭스 내부를 들여다보고 선택한 축 방향 단위 벡터를 반환합니다. vector_mul은 내적이며 vector_ld은 좌표가 x,y,z 인 3D 벡터를 채 웁니다.

    축이 단위 행렬에 맞춰 정렬되지 않은 전역 큐브 행렬을 얻었습니다. (뷰가 ​​위의 이미지와 유사하도록 회전되므로)이 축을 특수 벡터 (초기 뷰 행렬 값) 내 경우가 이것이다 :

    두 함수의 방향이 부 비교 (SX, SY, SZ)과 반대 인 경우 변환 매트릭스되는 x,y,z하고있는 축 돌아
    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; 
        } 
    

    .

  4. 슬라이스 회전이 퍼즐의 핵심입니다. 그것은 축을 중심으로 슬라이스를 돌리는 것입니다. 이것은 애니메이션에 사용되어 각 단계가 작기 때문에 (저는 9도를 사용합니다) 전체 회전이 90 도가되어야합니다. 그렇지 않으면 루빅 큐브가 깨질 것입니다. reper::gpos_get 반환 매트릭스 3D 벡터 (점) 등의 기원과 reper::gpos_set은 기본적으로 새로운 매트릭스 위치를 설정

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

    . vector_rotx(p0,p,a)은 벡터 pp0으로, 축 x을 각도 a만큼 회전시킵니다. +/- 표지판은 reper 클래스의 회전 수와 일치해야합니다 (차이가 있습니다). reper::lrotx은 로컬 x 축을 중심으로 reper으로 회전합니다. 자세한 내용은 첫 번째 링크를 참조하십시오.

    위에서 볼 수 있듯이 각 행렬 원점을 토폴로지로 직접 연결하여 조각 큐브를 선택합니다.여기에 몇 가지 회전의 애니메이션 GIF를Win32+OpenGL Rubic Cube Demo

    : 그리고 여기

내 데모를 시도 할 수 있습니다 내 RubiCube 간단한 해석을 추가 [EDIT1]

animation

이렇게 구현하려면 lver 표면 평면 컬러 맵을 추가했습니다 (왼쪽에 ... 중간 사각형은 사용하는면의 이름과 인덱스입니다). RubiCube 내부 표현.

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 

그리고지도는 다음과 같습니다 axises

각 명령

2 개 문자열로 표현된다 : 나는 또한 솔버 (오른쪽 axises 방향)에 대한 내부 명령의 가야를 추가 : map[side][u][v] 측면 s에 사각형의 색상을 포함

int map[6][3][3]; 

, u 및 공동 행 열 v. I (레알 인간에 의해 큐브 해결 같은) 간단한 7 단계 솔루션을 구현 :

solution steps

  1. 입력 상태 (하지 공정) 옐로우 중간 (옐로우 중간 직면 앞면)
  2. 화이트 크로스
  3. 화이트 크로스 (화이트 중간가 향하는 전면)
  4. 화이트 모서리 (백색면이 아래로 향)
  5. 중간층 (제 3 개 명령을 사용)
  6. ,536 측면 일치 (5 명령) 및 상위 계층 모서리의 방향을 재정렬 코너 (6 명령)
  7. 큐브 (7 명령)을 완료 할 수 있도록
  8. 재정렬 크로스 (4 명령을 사용하여)
  9. 탑 레이어 노란색 크로스

해 찾기는 간단하고 문자열 (최적화되지 않음)에서 작동하므로 약간 느리지 만 어쨌든 전체 솔루션은 설치시 최대 50ms가 소요됩니다. 당신은 여기에 업그레이드 된 데모를 시도 할 수 있습니다 :

아직 (때문에 버그의 코드에서 놓친 경우에) 해결하면서 몇 가지 정의되지 않은 경우가있을 수 있습니다. 이 경우 앱은 거친 상태로 정지합니다 (아직 워치 독을 구현하지 않았 음). 키는 포함 된 텍스트 파일에 설명되어 있습니다.

가벼운 해결책 (cca 300 줄의 코드)을 사용하여 발견 된 솔루션이 최적이 아닙니다. 예를 들어 4 코너를 테스트하는 대신 하나만 테스트하고 루프에서 큐브를 회전시켜 불필요한 회전을 유발합니다.그들 중 일부는 후자이지만 평균 인간 (내) 솔루션은 최대 200 턴이고이 솔버는 최대 300 턴으로 돌아갑니다 (최악의 경우 지금까지 발견).

관련 문제