Xoay tùy ý về một hình cầu


12

Tôi đang mã hóa một cơ chế cho phép người dùng di chuyển xung quanh bề mặt của một quả cầu. Vị trí trên quả cầu hiện được lưu trữ dưới dạng thetaphi, trong đó thetagóc giữa trục z và hình chiếu xz của vị trí hiện tại (tức là xoay quanh trục y) và philà góc từ trục y đến vị trí. Tôi giải thích rằng kém, nhưng về cơ bản là theta = yaw,phi = pitch

Vector3 position = new Vector3(0,0,1);
position.X = (float)Math.Sin(phi) * (float)Math.Sin(theta);
position.Y = (float)Math.Sin(phi) * (float)Math.Cos(theta);
position.Z = (float)Math.Cos(phi);
position *= r;

Tôi tin rằng điều này là chính xác, tuy nhiên tôi có thể sai. Tôi cần có khả năng di chuyển theo hướng giả hai chiều tùy ý xung quanh bề mặt của một quả cầu tại điểm gốc của không gian thế giới có bán kính r. Ví dụ, cầm Wnên di chuyển xung quanh quả cầu theo hướng lên trên so với hướng của người chơi.

Tôi tin rằng tôi nên sử dụng Đệ tứ để thể hiện vị trí / định hướng trên quả cầu, nhưng tôi không thể nghĩ ra cách làm đúng. Hình học hình cầu không phải là phù hợp mạnh mẽ của tôi.

Về cơ bản, tôi cần điền vào khối sau:

public void Move(Direction dir)
{   
    switch (dir)
    {
        case Direction.Left:
            // update quaternion to rotate left
            break;
        case Direction.Right:   
            // update quaternion to rotate right
            break;
        case Direction.Up:
            // update quaternion to rotate upward
            break;
        case Direction.Down:
            // update quaternion to rotate downward
            break;
    }
}

Điều gì sẽ xảy ra nếu người chơi đạt đến cực? Tôi nhận thấy bạn đã viết "hướng lên trên", bạn có nghĩa đen là "hướng lên" (nghĩa là cách xa bề mặt của quả cầu), "thẳng về phía trước" hoặc "về phía cực bắc" (hai cái sau giống nhau nếu người chơi không thể thay đổi hướng của họ và "trước mặt họ" hoặc "lên" trên màn hình luôn luôn ở phía bắc)?
Martin Sojka

Có lẽ đó là từ kém. Người chơi không nên rời khỏi bề mặt của quả cầu, và không nên biết về trục chính. Vì vậy, khi bạn di chuyển "lên", bạn di chuyển dọc theo bề mặt của quả cầu theo hướng lên trên so với hướng của người chơi. ví dụ: Nếu bạn đang ở (r, 0,0) và nhấn lên, bạn sẽ đi về phía cực z +, nhưng nếu bạn tiếp tục, bạn nên quấn quanh và tiếp tục đi.
azz

Vẫn còn một câu hỏi: Người chơi có thể thay đổi hướng (xoay "trái" và "phải") không?
Martin Sojka

Có lẽ một ví dụ tốt hơn về những gì tôi sẽ làm: Người chơi ở (1,1,1)bên trái sẽ xoay quanh quả cầu, đi qua (~1.2,0,~-1.2), sau đó (-1,-1,-1), rồi (~-1.2,0,~1.2)quay lại (1,1,1).
azz

1
Nếu bạn có ý định luôn theo dõi thetaphicập nhật vị trí của mình, bạn đang làm cho vấn đề của mình trở nên phức tạp không cần thiết. Dễ dàng hơn nhiều khi chỉ cần tính toán 2 trục xoay mỗi khung (một trong số đó (ngáp) không bao giờ thay đổi) và Vector3.Transormxung quanh quả cầu. Điều này sẽ đơn giản hóa vấn đề của bạn nhưng sẽ khiến bạn ngắt kết nối với phi& theta.
Steve H

Câu trả lời:


5

Trên thực tế, hóa ra bạn không thể có 'cả hai cách': nếu ý định của bạn là không có bất kỳ ý nghĩa nào về 'định hướng tuyệt đối' trên quả cầu (nghĩa là, nếu người chơi không luôn luôn hướng về phía cực ), sau đó bạn sẽ cần có một khái niệm về định hướng người chơi. Điều này là do, trái với những gì trực giác có thể gợi ý, chuyển động trên quả cầu không hoàn toàn giống như chuyển động trên một mặt phẳng, thậm chí không cục bộ (khá); độ cong nội tại của hình cầu có nghĩa là người chơi có thể thực hiện các hành động sẽ tự xoay!

Đối với ví dụ cực đoan nhất về những gì tôi đang nói, hãy tưởng tượng rằng người chơi bắt đầu tại một điểm trên đường xích đạo (để thuận tiện, chúng ta sẽ tưởng tượng một mặt đồng hồ được vẽ trên đường xích đạo từ trên cao và đặt người chơi vào lúc 6 giờ ), đối mặt với 'lên' - nghĩa là về phía Bắc Cực. Giả sử người chơi đi bộ đến Bắc Cực; sau đó họ sẽ phải đối mặt trực tiếp với điểm 12 giờ. Bây giờ, hãy để người chơi di chuyển trực tiếp sang bên phải của họ, từ Bắc Cực trở lại xích đạo; họ sẽ quay lại vào lúc 3 giờ - nhưng vì khuôn mặt của họ không thay đổi khi họ di chuyển đúng(ý tưởng là khuôn mặt của họ không thay đổi cho dù họ di chuyển như thế nào), họ vẫn sẽ phải đối mặt với điểm 12 giờ - giờ họ đang đối mặt dọc theo đường xích đạo! Bây giờ, hãy để họ di chuyển 'ngược' trở lại điểm bắt đầu (6 giờ); sau đó chúng sẽ vẫn quay mặt dọc theo đường xích đạo, vì vậy chúng sẽ hướng về điểm 3 giờ - chỉ di chuyển dọc theo quả cầu mà không bao giờ thay đổi hướng 'cá nhân' của chúng đã khiến chúng quay từ hướng về phía cực bắc sang phải đối mặt dọc theo đường xích đạo! Theo một nghĩa nào đó, đây là một công phu của trò đùa cũ 'một thợ săn di chuyển một dặm về phía nam, một dặm về phía tây và sau đó là một dặm phía bắc' - nhưng ở đây chúng ta đang tận dụng độ cong của quả cầu để thay đổi hướng. Lưu ý rằng hiệu ứng tương tự vẫn xảy ra ngay cả trên quy mô nhỏ hơn nhiều;

May mắn thay, các bậc bốn làm (như bạn lưu ý chính mình) xử lý tình huống này; vì một bậc bốn biểu thị một phép quay tùy ý, nó biểu thị một cách hiệu quả một 'điểm cộng định hướng' tùy ý trên mặt cầu: tưởng tượng bắt đầu bằng một 'triaxis' ở gốc và cho nó một số vòng quay tùy ý, sau đó di chuyển một đơn vị theo hướng xoay trục ' Điểm trục Z; một chút suy nghĩ sẽ thuyết phục bạn rằng điều này đưa bạn đến một điểm trên quả cầu đơn vị với một số 'định hướng' (nghĩa là sự sắp xếp của trục X và Y của bộ ba của bạn) và bạn có thể đến mọi điểm + hướng trên đơn vị hình cầu theo cách này (chỉ cần gán trục Z của bạn để chỉ dọc theo đường thẳng từ điểm gốc qua điểm của bạn trên quả cầu, sau đó vận chuyển bộ ba của bạn trở lại điểm gốc dọc theo đường thẳng đó). Hơn nữa do phép nhân các bậc bốn tương ứng với thành phần của phép quay, mỗi phép toán mà bạn mô tả có thể được biểu diễn bằng cách nhân 'định hướng hiện tại' của bạn với một nhóm bậc bốn được chọn một cách thích hợp: cụ thể, vì bậc bốn (đơn vị) (qx, qy, qz, qw) có nghĩa là 'xoay quanh trục (qx, qy, qz) bởi arccos (qw)', sau đó (tùy thuộc vào sự lựa chọn cụ thể của bạn về hệ tọa độ và để c_a là cos (alpha) và s_a là sin (alpha)) ba phần tư M_x = (s_a, 0, 0, c_a), M_y = (0, s_a, 0, c_a) và M_z = (0, 0, s_a, c_a) sẽ đại diện cho 'xoay (tức là di chuyển) theo hướng I 'Hiện tại tôi đang đối mặt với alpha' và 'xoay theo hướng trực giao với hướng tôi hiện đang đối mặt với alpha'. (Thứ ba trong số các tứ phân vị đó sẽ đại diện cho 'xoay nhân vật của tôi về trục của chính mình'Cur_q = M_x * Cur_qnếu người chơi đã nhấn lên hoặc Cur_q = M_y * Cur_qnếu người chơi nhấn phải (hoặc có thể giống như Cur_q = M_yinv * Cur_qnếu người chơi nhấn trái, trong đó M_yinv là 'nghịch đảo' của nhóm M_y, đại diện cho một vòng quay theo cách khác). Lưu ý rằng bạn phải cẩn thận, 'bên nào' bạn áp dụng xoay vòng trên, cho dù là sớm hay muộn; thành thật mà nói, có thể dễ dàng nhất để giải quyết điều đó với thử và sai, thử cả hai phép nhân và xem cái nào hoạt động.

Đi từ vị trí thứ tư được cập nhật của bạn đến một điểm trên hình cầu (và theo hướng của nhân vật của bạn) cũng tương đối đơn giản: bởi sự tương ứng của đoạn cuối cùng, tất cả những gì bạn phải làm là sử dụng phần tư của bạn trên các vectơ cơ sở (1, 0,0), (0,1,0) và (0,0,1) của khung hình của bạn thông qua 'vectơ xoay theo thao tác bậc bốn v → qvq sẽ là tọa độ của người dùng' đã biến đổi 'trên quả cầu đơn vị (và dĩ nhiên, để có tọa độ trên một hình cầu tùy ý, bạn chỉ cần nhân các số đó với bán kính của hình cầu); các tính toán tương tự hoạt động cho các trục khác, để xác định ví dụ: hướng đối diện của người dùng. -1 (trong đó các phép nhân ở đây là bội số bậc bốn và chúng tôi xác định vectơ v = (x, y, z) với 'bậc bốn suy biến' (x, y, z, 0)). Chẳng hạn, vị trí trên quả cầu đơn vị được nhận bằng cách chuyển đổi vectơ z: pos = (qx, qy, qz, qw) * (0, 0, 1, 0) * (-qx, -qy, -qz, qw) = (qx, qy, qz, qw) * (qy, -qx, qw, qz) = (2 (qy * qw + qz * qx), 2 (qz * qy-qw * qx), (qz ^ 2 + qw ^ 2) - (qx ^ 2 + qy ^ 2), 0), vì vậy(2(qy*qw+qz*qx), 2(qz*qy-qw*qx), (qz^2+qw^2)-(qx^2+qy^2))


Chính xác những gì tôi muốn đạt được. Tôi chỉ không thể nghĩ ra cách chính xác để có được một vị trí trong tư thế định hướng. Sử dụng những gì bạn đã cung cấp, tôi có thể viết Move()thủ tục, nhưng để có được trục chuẩn hóa (tức là vị trí của tôi), tôi sẽ chỉ thực hiện (sin(qx),sin(qy),sin(qw)) * r?
azz

@Der không chính xác - Tôi sẽ cập nhật bài viết của mình với các chi tiết, nhưng phiên bản ngắn là bạn sử dụng bộ tứ của mình để chuyển đổi một vectơ đơn vị, ví dụ (0,0,1), theo v -> qvq <sup> thông thường Hoạt động -1 </ sup>; thực tế là bạn đang chuyển đổi một vectơ đơn giản có nghĩa là (tự nhiên) có một lối tắt ở đây, nhưng tọa độ cuối cùng là bậc hai trong các giá trị bậc bốn của bạn, không phải tuyến tính.
Steven Stadnicki

1

Tôi nghĩ rằng bạn muốn một cái gì đó tương tự như thế này http://www.youtube.com/watch?v=L2YRZbRSD1k

Tôi đã phát triển nó cho một gamejam 48 giờ ... bạn có thể tải mã ở đây ... http://archive.globalgamejam.org/2011/evil-god

Tôi đã sử dụng một cái gì đó tương tự như mã của bạn để có được các hợp âm 3D ... nhưng tôi đã xoay hành tinh và người chơi ở cùng một tư thế, tôi nghĩ rằng bạn quan tâm đến chuyển động của sinh vật, là:

    // To add movement
    protected override void LocalUpdate(float seconds)
    {
        Creature.Alfa += Direction.X * seconds * Speed;
        Creature.Beta += Direction.Y * seconds * Speed;            
    }


    // To calculate position
       World.Planet.GetCartesian(Alfa, Beta, out Position); // as you do
       Matrix PositionMatrix = Matrix.CreateTranslation(Position) * World.Planet.RotationMatrix;           
       LastPositionAbsolute = PositionAbsolute;
       Vector3 Up = PositionAbsolute = Vector3.Transform(Vector3.Zero, PositionMatrix);           
       Up.Normalize();
       // This is to add and offset to the creature model position
       PositionAbsolute += Up * 8;  
      // calculate new forward vector if needed

       if ((PositionAbsolute - LastPositionAbsolute).Length() > 0.1f) {
           Forward = PositionAbsolute - LastPositionAbsolute;
           Forward.Normalize();
       }

       // Calculate the world transform with position, forward vector and up vector
       Matrix LocalWorld = Matrix.CreateWorld(PositionAbsolute, Forward, Up); 

       Transform = Matrix.CreateScale(Scale * ScaleFactor) * LocalWorld;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.