Tôi đang xoay một vật thể trên hai trục, vậy tại sao nó cứ xoay quanh trục thứ ba?


38

Tôi thấy các câu hỏi xuất hiện khá thường xuyên có vấn đề tiềm ẩn này, nhưng tất cả chúng đều bị cuốn vào các chi tiết của một tính năng hoặc công cụ nhất định. Đây là một nỗ lực để tạo ra một câu trả lời kinh điển mà chúng ta có thể giới thiệu cho người dùng khi điều này xuất hiện - với rất nhiều ví dụ hoạt hình! :)


Hãy nói rằng chúng tôi đang làm một máy ảnh đầu tiên. Ý tưởng cơ bản là nó nên ngáp để nhìn trái & phải, và ném để nhìn lên & xuống. Vì vậy, chúng tôi viết một chút mã như thế này (sử dụng Unity làm ví dụ):

void Update() {
    float speed = lookSpeed * Time.deltaTime;

    // Yaw around the y axis using the player's horizontal input.        
    transform.Rotate(0f, Input.GetAxis("Horizontal") * speed, 0f);

    // Pitch around the x axis using the player's vertical input.
    transform.Rotate(-Input.GetAxis("Vertical") * speed,  0f, 0f);
}

hoặc có thể

// Construct a quaternion or a matrix representing incremental camera rotation.
Quaternion rotation = Quaternion.Euler(
                        -Input.GetAxis("Vertical") * speed,
                         Input.GetAxis("Horizontal") * speed,
                         0);

// Fold this change into the camera's current rotation.
transform.rotation *= rotation;

Và nó chủ yếu hoạt động, nhưng theo thời gian, khung nhìn bắt đầu bị vẹo. Máy ảnh dường như đang bật trục cuộn của nó (z) mặc dù chúng tôi chỉ bảo nó xoay trên x và y!

Ví dụ hoạt hình của máy ảnh góc nhìn thứ nhất nghiêng sang một bên

Điều này cũng có thể xảy ra nếu chúng ta đang cố gắng điều khiển một vật thể trước máy ảnh - nói rằng đó là một quả địa cầu mà chúng ta muốn quay lại để nhìn xung quanh:

Ví dụ hoạt hình của một quả cầu nghiêng sang một bên

Vấn đề tương tự - sau một thời gian, cực Bắc bắt đầu đi lang thang sang trái hoặc phải. Chúng tôi đang đưa ra đầu vào trên hai trục nhưng chúng tôi đang nhận được sự xoay vòng khó hiểu này vào một phần ba. Và điều đó xảy ra cho dù chúng ta áp dụng tất cả các phép quay của mình xung quanh các trục cục bộ của đối tượng hoặc các trục toàn cầu của thế giới.

Trong nhiều động cơ, bạn cũng sẽ thấy điều này trong thanh tra - xoay đối tượng trên thế giới và đột nhiên số thay đổi trên một trục mà chúng ta thậm chí không chạm vào!

Ví dụ hoạt hình cho thấy thao tác với một đối tượng bên cạnh đọc ra các góc quay của nó.  Góc z thay đổi mặc dù người dùng không thao tác trục đó.

Vì vậy, đây có phải là một lỗi động cơ? Làm thế nào để chúng tôi nói với chương trình mà chúng tôi không muốn nó thêm vòng quay bổ sung?

Nó có liên quan gì đến góc Euler không? Thay vào đó, tôi nên sử dụng Quancyions hoặc Ma trận Xoay hoặc Vectơ cơ sở?



1
Tôi tìm thấy câu trả lời cho câu hỏi sau đây cũng rất hữu ích. gamedev.stackexchange.com/questions/123535/ từ
Travis Pettry

Câu trả lời:


51

Không, đây không phải là lỗi động cơ hoặc tạo tác của một đại diện xoay cụ thể (những điều này cũng có thể xảy ra, nhưng hiệu ứng này áp dụng cho mọi hệ thống đại diện cho các phép quay, bao gồm các bậc bốn).

Bạn đã khám phá ra một thực tế thực tế về cách thức hoạt động của vòng quay trong không gian ba chiều và nó rời khỏi trực giác của chúng ta về các biến đổi khác như dịch thuật:

Ví dụ hoạt hình cho thấy rằng áp dụng các phép quay theo thứ tự khác nhau cho kết quả khác nhau

Khi chúng tôi soạn các phép quay trên nhiều trục, kết quả chúng tôi nhận được không chỉ là tổng giá trị / giá trị ròng mà chúng tôi áp dụng cho mỗi trục (như chúng tôi có thể mong đợi cho bản dịch). Thứ tự mà chúng ta áp dụng các phép quay thay đổi kết quả, vì mỗi vòng quay sẽ di chuyển các trục mà các phép quay tiếp theo được áp dụng (nếu xoay quanh trục cục bộ của đối tượng) hoặc mối quan hệ giữa đối tượng và trục (nếu xoay quanh thế giới trục).

Việc thay đổi mối quan hệ trục theo thời gian có thể gây nhầm lẫn cho trực giác của chúng ta về việc mỗi trục được "cho là" để làm gì. Cụ thể, một số kết hợp nhất định của xoay vòng ngáp và cao độ cho kết quả tương tự như xoay vòng!

Ví dụ hoạt hình hiển thị một chuỗi các bước phát âm cục bộ cho cùng một đầu ra như một cuộn cục bộ

Bạn có thể xác minh rằng mỗi bước quay chính xác về trục mà chúng tôi yêu cầu - không có trục trặc động cơ hoặc tạo tác trong ký hiệu của chúng tôi can thiệp hoặc đoán thứ hai đầu vào của chúng tôi - tính chất hình cầu (hoặc siêu đối xứng / tứ phương) chỉ có nghĩa là biến đổi của chúng tôi xung quanh "lên nhau. Chúng có thể trực giao cục bộ, cho các phép quay nhỏ, nhưng khi chúng chồng chất lên nhau, chúng ta thấy chúng không trực giao trên toàn cầu.

Điều này là ấn tượng và rõ ràng nhất đối với các góc quay 90 độ như những người ở trên, nhưng các trục rình rập leo qua nhiều góc quay nhỏ, như đã được chứng minh trong câu hỏi.

Vậy thì chúng ta làm gì với nó?

Nếu bạn đã có một hệ thống xoay cao độ, một trong những cách nhanh nhất để loại bỏ cuộn không mong muốn là thay đổi một trong các phép quay để hoạt động trên trục chuyển đổi toàn cầu hoặc cha mẹ thay vì trục cục bộ của đối tượng. Bằng cách đó, bạn không thể bị lây nhiễm chéo giữa hai trục - một trục vẫn được kiểm soát tuyệt đối.

Đây là một chuỗi tương tự cao độ đã trở thành một cuộn trong ví dụ trên, nhưng bây giờ chúng ta áp dụng ngáp của mình quanh trục Y toàn cầu thay vì của đối tượng

Ví dụ hoạt hình của một chiếc cốc xoay với cao độ cục bộ và ngáp toàn cầu, không có vấn đề cuộnVí dụ hoạt hình của máy ảnh góc nhìn thứ nhất sử dụng ngáp toàn cầu

Vì vậy, chúng tôi có thể sửa máy ảnh của người đầu tiên với câu thần chú "Sân địa phương, Yaw toàn cầu":

void Update() {
    float speed = lookSpeed * Time.deltaTime;

    transform.Rotate(0f, Input.GetAxis("Horizontal") * speed, 0f, Space.World);
    transform.Rotate(-Input.GetAxis("Vertical") * speed,  0f, 0f, Space.Self);
}

Nếu bạn kết hợp các phép quay của mình bằng cách nhân, bạn sẽ lật thứ tự trái / phải của một trong các phép nhân để có được hiệu ứng tương tự:

// Yaw happens "over" the current rotation, in global coordinates.
Quaternion yaw = Quaternion.Euler(0f, Input.GetAxis("Horizontal") * speed, 0f);
transform.rotation =  yaw * transform.rotation; // yaw on the left.

// Pitch happens "under" the current rotation, in local coordinates.
Quaternion pitch = Quaternion.Euler(-Input.GetAxis("Vertical") * speed, 0f, 0f);
transform.rotation = transform.rotation * pitch; // pitch on the right.

(Thứ tự cụ thể sẽ phụ thuộc vào các quy ước nhân trong môi trường của bạn, nhưng left = more global / right = more local là một lựa chọn phổ biến)

Điều này tương đương với việc lưu trữ tổng số ngáp ròng và tổng độ cao bạn muốn dưới dạng các biến float, sau đó luôn luôn áp dụng tất cả kết quả ròng cùng một lúc, xây dựng một ma trận định hướng hoặc ma trận mới duy nhất từ ​​các góc này (miễn là bạn tiếp tục totalPitchkẹp):

// Construct a new orientation quaternion or matrix from Euler/Tait-Bryan angles.
var newRotation = Quaternion.Euler(totalPitch, totalYaw, 0f);
// Apply it to our object.
transform.rotation = newRotation;

hoặc tương đương ...

// Form a view vector using total pitch & yaw as spherical coordinates.
Vector3 forward = new Vector3(
                    Mathf.cos(totalPitch) * Mathf.sin(totalYaw),
                    Mathf.sin(totalPitch),
                    Mathf.cos(totalPitch) * Mathf.cos(totalYaw));

// Construct an orientation or view matrix pointing in that direction.
var newRotation = Quaternion.LookRotation(forward, new Vector3(0, 1, 0));
// Apply it to our object.
transform.rotation = newRotation;

Sử dụng sự phân chia toàn cầu / cục bộ này, các phép quay không có cơ hội kết hợp và ảnh hưởng lẫn nhau, bởi vì chúng được áp dụng cho các bộ trục độc lập.

Ý tưởng tương tự có thể giúp nếu đó là một đối tượng trong thế giới mà chúng ta muốn xoay. Đối với một ví dụ như toàn cầu, chúng ta thường muốn đảo ngược nó và áp dụng ngáp của mình cục bộ (để nó luôn xoay quanh các cực của nó) và ném trên toàn cầu (vì vậy, nó hướng tới / ra khỏi tầm nhìn của chúng ta, thay vì hướng về / ra khỏi Úc , bất cứ nơi nào nó chỉ ...)

Ví dụ hoạt hình của một quả địa cầu cho thấy vòng quay hoạt động tốt hơn

Hạn chế

Chiến lược lai toàn cầu / cục bộ này không phải lúc nào cũng đúng. Ví dụ: trong trò chơi có chuyến bay / bơi 3D, bạn có thể muốn chỉ thẳng lên / xuống thẳng mà vẫn có toàn quyền điều khiển. Nhưng với thiết lập này, bạn sẽ nhấn khóa gimbal - trục ngáp của bạn (toàn cầu) trở thành song song với trục cuộn của bạn (chuyển tiếp cục bộ) và bạn không có cách nào để nhìn sang trái hoặc phải mà không vặn.

Thay vào đó, những gì bạn có thể làm trong các trường hợp như thế này là sử dụng các phép quay cục bộ thuần túy như chúng tôi đã bắt đầu trong câu hỏi ở trên (để các điều khiển của bạn cảm thấy giống nhau cho dù bạn đang tìm kiếm ở đâu), ban đầu sẽ cho phép một số cuộn vào - nhưng sau đó chúng tôi sửa cho nó

Ví dụ: chúng ta có thể sử dụng các phép quay cục bộ để cập nhật vectơ "chuyển tiếp" của mình, sau đó sử dụng vectơ chuyển tiếp đó cùng với một vectơ "lên" tham chiếu để xây dựng hướng cuối cùng của chúng ta. (Ví dụ, sử dụng phương pháp Quancyion.LookRotation của Unity hoặc xây dựng thủ công ma trận trực giao từ các vectơ này) Bằng cách điều khiển vectơ lên, chúng tôi điều khiển cuộn hoặc xoắn.

Đối với ví dụ về chuyến bay / bơi, bạn sẽ muốn áp dụng các hiệu chỉnh này dần dần theo thời gian. Nếu quá đột ngột, chế độ xem có thể mất đi một cách mất tập trung. Thay vào đó, bạn có thể sử dụng vectơ hiện tại của người chơi và gợi ý nó theo chiều dọc, từng khung hình, cho đến khi tầm nhìn của họ vượt ra ngoài. Áp dụng điều này trong một lượt đôi khi có thể ít buồn nôn hơn so với vặn máy ảnh trong khi các điều khiển của người chơi không hoạt động.


1
Tôi có thể hỏi làm thế nào bạn thực hiện để gifs? Họ trông tuyệt thật.
Bálint

1
@ Bálint Tôi sử dụng một chương trình miễn phí có tên LICEcap - nó cho phép bạn ghi lại một phần màn hình của mình vào một gif. Sau đó, tôi cắt hình ảnh động / thêm văn bản chú thích bổ sung / nén gif bằng Photoshop.
DMGregory

Có phải câu trả lời này chỉ dành cho Unity? Có một câu trả lời chung chung hơn trên Web?
posfan12

2
@ posfan12 Mã ví dụ sử dụng cú pháp Unity để cụ thể hóa, nhưng các tình huống và toán học liên quan được áp dụng như nhau trên bảng. Bạn có gặp khó khăn gì khi áp dụng các ví dụ cho môi trường của bạn không?
DMGregory

2
Toán học đã có mặt ở trên. Đánh giá bản chỉnh sửa của bạn, bạn chỉ đang nghiền nát những phần sai của nó. Có vẻ như bạn đang sử dụng loại vectơ được xác định ở đây . Điều này bao gồm một phương pháp để xây dựng một ma trận xoay từ một tập hợp các góc như mô tả ở trên. Bắt đầu với một ma trận danh tính và cuộc gọi TCVector::calcRotationMatrix(totalPitch, totalYaw, identity)- điều này tương đương với ví dụ "tất cả cùng một lúc" ở trên.
DMGregory

1

SAU 16 giờ chức năng làm cha mẹ và luân phiên lol.

Tôi chỉ muốn có mã có một khoảng trống, về cơ bản, xoay vòng tròn và cũng lật trước khi nó quay.

Hay nói cách khác, mục tiêu là xoay ngáp và ném mà không có bất kỳ ảnh hưởng nào đến cuộn.

Dưới đây là một số ít thực sự hoạt động trong ứng dụng của tôi

Trong đoàn kết:

  1. Tạo một sản phẩm nào. Gọi nó là ForYaw.
  2. Cho rằng một đứa trẻ trống rỗng, được gọi là ForPitch.
  3. Cuối cùng, tạo một khối lập phương và di chuyển nó đến z = 5 (chuyển tiếp). Bây giờ chúng ta có thể thấy những gì chúng ta đang cố gắng tránh, đó sẽ là xoắn khối (vô tình "cuộn" trong khi chúng ta ngáp và ném) .

Bây giờ kịch bản trong Visual Studio, thực hiện thiết lập của bạn và kết thúc với phần này trong Cập nhật:

objectForYaw.Rotate(objectForYaw.up, 1f, Space.World);
    objectForPitch.Rotate(objectForPitch.right, 3f, Space.World);

    //this will work on the pitch object as well
    //objectForPitch.RotateAround
    //    (objectForPitch.position, objectForPitch.right, 3f);

Lưu ý rằng tất cả đã phá vỡ đối với tôi khi tôi cố gắng thay đổi thứ tự nuôi dạy con cái. Hoặc nếu tôi thay đổi Space.World cho đối tượng sân trẻ em (cha mẹ Yaw không bận tâm đến việc xoay vòng qua Space.Self). Gây nhầm lẫn. Tôi chắc chắn có thể sử dụng một số giải thích về lý do tại sao tôi phải đi một trục cục bộ nhưng áp dụng nó qua không gian thế giới - tại sao Space.Self lại phá hỏng mọi thứ?


Tôi không rõ liệu điều này có nhằm trả lời câu hỏi không, hoặc nếu bạn đang yêu cầu trợ giúp hiểu lý do tại sao mã bạn viết lại hoạt động theo cách này. Nếu đó là câu hỏi sau, bạn nên đăng một câu hỏi mới, liên kết đến trang này để lấy ngữ cảnh nếu bạn thích. Như một gợi ý, me.Rotate(me.right, angle, Space.World)cũng giống như me.Rotate(Vector3.right, angle, Space.Selfvà các biến đổi lồng nhau của bạn tương đương với các ví dụ "Sân địa phương, Yaw toàn cầu" trong câu trả lời được chấp nhận.
DMGregory

Một chút của cả hai. Một phần để chia sẻ trong câu trả lời (ví dụ ở đây chính xác là cách nó hoạt động với tôi). Và cũng để giới thiệu câu hỏi. Gợi ý đó đã khiến tôi suy nghĩ. Cảm ơn rất nhiều!
Jeh

Thật ngạc nhiên khi điều này không được nêu lên. Sau nhiều giờ đọc các câu trả lời toàn diện mà mặc dù giáo dục vẫn không hoạt động, câu trả lời đơn giản về việc sử dụng trục xoay đối tượng thay vì trục Vector3.right là tất cả những gì cần thiết để giữ xoay cố định trên đối tượng. Thật đáng kinh ngạc những gì tôi đang tìm kiếm đã bị chôn vùi ở đây với 0 phiếu và trên trang 2 của Google có rất nhiều câu hỏi tương tự.
dùng4779
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.