Pengontrol tangan dan gerakan di DirectX
Catatan
Artikel ini berkaitan dengan API asli WinRT warisan. Untuk proyek aplikasi asli baru, sebaiknya gunakan OpenXR API.
Dalam Windows Mixed Reality, input pengontrol tangan dan gerakan ditangani melalui API input spasial, yang ditemukan di namespace Windows.UI.Input.Spatial. Ini memungkinkan Anda untuk dengan mudah menangani tindakan umum seperti Pilih menekan cara yang sama di kedua pengontrol tangan dan gerakan.
Memulai
Untuk mengakses input spasial di Windows Mixed Reality, mulailah dengan antarmuka SpatialInteractionManager. Anda dapat mengakses antarmuka ini dengan memanggil SpatialInteractionManager::GetForCurrentView, biasanya kadang-kadang selama pengaktifan aplikasi.
using namespace winrt::Windows::UI::Input::Spatial;
SpatialInteractionManager interactionManager = SpatialInteractionManager::GetForCurrentView();
Tugas SpatialInteractionManager adalah menyediakan akses ke SpatialInteractionSources, yang mewakili sumber input. Ada tiga jenis SpatialInteractionSources yang tersedia dalam sistem.
- Tangan mewakili tangan pengguna yang terdeteksi. Sumber tangan menawarkan berbagai fitur berdasarkan perangkat, mulai dari gerakan dasar di HoloLens hingga pelacakan tangan yang sepenuhnya diartikulasikan pada HoloLens 2.
- Pengontrol mewakili pengontrol gerakan yang dipasangkan. Pengontrol gerakan dapat menawarkan kemampuan yang berbeda, misalnya, Pemicu Pilih, tombol Menu, tombol Grasp, touchpad, dan thumbstick.
- Voice mewakili kata kunci yang terdeteksi sistem berbicara suara pengguna. Misalnya, sumber ini akan menyuntikkan tekan dan rilis Pilih setiap kali pengguna mengatakan "Pilih".
Data per bingkai untuk sumber diwakili oleh antarmuka SpatialInteractionSourceState . Ada dua cara berbeda untuk mengakses data ini, tergantung pada apakah Anda ingin menggunakan model berbasis peristiwa atau polling di aplikasi Anda.
Input berbasis peristiwa
SpatialInteractionManager menyediakan sejumlah peristiwa yang dapat didengarkan aplikasi Anda. Beberapa contoh termasuk SourcePressed, [SourceReleased, dan SourceUpdated.
Misalnya, kode berikut menghubungkan penanganan aktivitas yang disebut MyApp::OnSourcePressed ke peristiwa SourcePressed. Ini memungkinkan aplikasi Anda mendeteksi penekanan pada semua jenis sumber interaksi.
using namespace winrt::Windows::UI::Input::Spatial;
auto interactionManager = SpatialInteractionManager::GetForCurrentView();
interactionManager.SourcePressed({ this, &MyApp::OnSourcePressed });
Peristiwa yang ditekan ini dikirim ke aplikasi Anda secara asinkron, bersama dengan SpatialInteractionSourceState yang sesuai pada saat pers terjadi. Aplikasi atau mesin game Anda mungkin ingin segera mulai memproses atau mengantre data peristiwa dalam rutinitas pemrosesan input Anda. Berikut adalah fungsi penanganan aktivitas untuk peristiwa SourcePressed, yang memeriksa apakah tombol pilih telah ditekan.
using namespace winrt::Windows::UI::Input::Spatial;
void MyApp::OnSourcePressed(SpatialInteractionManager const& sender, SpatialInteractionSourceEventArgs const& args)
{
if (args.PressKind() == SpatialInteractionPressKind::Select)
{
// Select button was pressed, update app state
}
}
Kode di atas hanya memeriksa tekan 'Pilih', yang sesuai dengan tindakan utama pada perangkat. Contohnya termasuk melakukan AirTap di HoloLens atau menarik pemicu pada pengontrol gerakan. Tekan 'Pilih' mewakili niat pengguna untuk mengaktifkan hologram yang mereka targetkan. Peristiwa SourcePressed akan diaktifkan untuk sejumlah tombol dan gerakan yang berbeda, dan Anda dapat memeriksa properti lain pada SpatialInteractionSource untuk menguji kasus-kasus tersebut.
Input berbasis polling
Anda juga dapat menggunakan SpatialInteractionManager untuk melakukan polling untuk status input saat ini setiap bingkai. Untuk melakukan ini, panggil GetDetectedSourcesAtTimestamp setiap bingkai. Fungsi ini mengembalikan array yang berisi satu SpatialInteractionSourceState untuk setiap SpatialInteractionSource aktif. Ini berarti satu untuk setiap pengontrol gerakan aktif, satu untuk setiap tangan yang dilacak, dan satu untuk ucapan jika perintah 'pilih' baru-baru ini diucapkan. Anda kemudian dapat memeriksa properti di setiap SpatialInteractionSourceState untuk mendorong input ke dalam aplikasi Anda.
Berikut adalah contoh cara memeriksa tindakan 'pilih' menggunakan metode polling. Variabel prediksi mewakili objek HolographicFramePrediction , yang dapat diperoleh dari HolographicFrame.
using namespace winrt::Windows::UI::Input::Spatial;
auto interactionManager = SpatialInteractionManager::GetForCurrentView();
auto sourceStates = m_spatialInteractionManager.GetDetectedSourcesAtTimestamp(prediction.Timestamp());
for (auto& sourceState : sourceStates)
{
if (sourceState.IsSelectPressed())
{
// Select button is down, update app state
}
}
Setiap SpatialInteractionSource memiliki ID, yang dapat Anda gunakan untuk mengidentifikasi sumber baru dan menghubungkan sumber yang ada dari bingkai ke bingkai. Tangan mendapatkan ID baru setiap kali mereka pergi dan memasukkan FOV, tetapi ID pengontrol tetap statis selama durasi sesi. Anda dapat menggunakan peristiwa di SpatialInteractionManager seperti SourceDetected dan SourceLost, untuk bereaksi saat tangan masuk atau meninggalkan tampilan perangkat, atau saat pengontrol gerakan dinyalakan/dimatikan atau dipasangkan/tidak dipasangkan.
Pose yang diprediksi vs. historis
GetDetectedSourcesAtTimestamp memiliki parameter tanda waktu. Ini memungkinkan Anda untuk meminta status dan pose data yang diprediksi atau historis, memungkinkan Anda menghubungkan interaksi spasial dengan sumber input lainnya. Misalnya, saat merender posisi tangan dalam bingkai saat ini, Anda dapat meneruskan tanda waktu yang diprediksi yang disediakan oleh HolographicFrame. Ini memungkinkan sistem untuk memprediksi posisi tangan untuk selaras erat dengan output bingkai yang dirender, meminimalkan latensi yang dirasakan.
Namun, pose yang diprediksi seperti itu tidak menghasilkan sinar penunjuk yang ideal untuk penargetan dengan sumber interaksi. Misalnya, ketika tombol pengontrol gerakan ditekan, diperlukan waktu hingga 20 md agar peristiwa tersebut menggelegak melalui Bluetooth ke sistem operasi. Demikian pula, setelah pengguna melakukan gerakan tangan, beberapa waktu dapat berlalu sebelum sistem mendeteksi gerakan dan aplikasi Anda kemudian melakukan polling untuk itu. Pada saat aplikasi Anda melakukan polling untuk perubahan status, pose kepala dan tangan digunakan untuk menargetkan interaksi tersebut benar-benar terjadi di masa lalu. Jika Anda menargetkan dengan meneruskan tanda waktu HolographicFrame Anda saat ini ke GetDetectedSourcesAtTimestamp, pose akan diprediksi ke sinar penargetan pada saat bingkai akan ditampilkan, yang bisa lebih dari 20 md di masa depan. Pose di masa depan ini baik untuk merender sumber interaksi, tetapi senyawa masalah waktu kita untuk menargetkan interaksi, karena penargetan pengguna terjadi di masa lalu.
Untungnya, peristiwa SourcePressed, [SourceReleased, dan SourceUpdated menyediakan Status historis yang terkait dengan setiap peristiwa input. Ini secara langsung mencakup pose kepala dan tangan historis yang tersedia melalui TryGetPointerPose, bersama dengan Tanda Waktu historis yang dapat Anda teruskan ke API lain untuk berkorelasi dengan peristiwa ini.
Itu mengarah pada praktik terbaik berikut saat merender dan menargetkan dengan tangan dan pengontrol setiap bingkai:
- Untuk penyajian tangan/pengontrol setiap bingkai, aplikasi Anda harus melakukan polling untuk pose yang diprediksi ke depan dari setiap sumber interaksi pada waktu foton bingkai saat ini. Anda dapat melakukan polling untuk semua sumber interaksi dengan memanggil GetDetectedSourcesAtTimestamp setiap bingkai, meneruskan tanda waktu yang diprediksi yang disediakan oleh HolographicFrame::CurrentPrediction.
- Untuk penargetan tangan/pengontrol setelah pers atau rilis, aplikasi Anda harus menangani peristiwa yang ditekan/dirilis, raycasting berdasarkan kepala historis atau pose tangan untuk peristiwa tersebut. Anda mendapatkan sinar penargetan ini dengan menangani peristiwa SourcePressed atau SourceReleased , mendapatkan properti Status dari argumen peristiwa, lalu memanggil metode TryGetPointerPose-nya .
Properti input lintas perangkat
SpatialInteractionSource API mendukung pengontrol dan sistem pelacakan tangan dengan berbagai kemampuan. Sejumlah kemampuan ini umum di antara jenis perangkat. Misalnya, pelacakan tangan dan pengontrol gerakan menyediakan tindakan 'pilih' dan posisi 3D. Jika memungkinkan, API memetakan kemampuan umum ini ke properti yang sama pada SpatialInteractionSource. Ini memungkinkan aplikasi untuk lebih mudah mendukung berbagai jenis input. Tabel berikut ini menjelaskan properti yang didukung, dan bagaimana perbandingannya di seluruh jenis input.
Properti | Deskripsi | Gerakan HoloLens(generasi ke-1) | Pengontrol Gerakan | Tangan Artikulasi |
---|---|---|---|---|
SpatialInteractionSource::Handedness | Tangan kanan atau kiri/ pengontrol. | Tidak Didukung | Didukung | Didukung |
SpatialInteractionSourceState::IsSelectPressed | Status tombol utama saat ini. | Ketuk Udara | Pemicu | Keran Udara Santai (mencubit tegak) |
SpatialInteractionSourceState::IsGrasped | Status tombol ambil saat ini. | Tidak Didukung | Tombol Ambil | Mencubit atau Tangan Tertutup |
SpatialInteractionSourceState::IsMenuPressed | Status tombol menu saat ini. | Tidak Didukung | Tombol Menu | Tidak Didukung |
SpatialInteractionSourceLocation::Posisi | Lokasi XYZ dari posisi tangan atau pegangan pada pengontrol. | Lokasi telapak tangan | Posisi pose genggaman | Lokasi telapak tangan |
SpatialInteractionSourceLocation::Orientation | Quaternion mewakili orientasi tangan atau pose genggaman pada pengontrol. | Tidak Didukung | Orientasi pose genggaman | Orientasi telapak tangan |
SpatialPointerInteractionSourcePose::Position | Asal sinar penunjuk. | Tidak Didukung | Didukung | Didukung |
SpatialPointerInteractionSourcePose::ForwardDirection | Arah sinar penunjuk. | Tidak Didukung | Didukung | Didukung |
Beberapa properti di atas tidak tersedia di semua perangkat, dan API menyediakan sarana untuk menguji ini. Misalnya, Anda dapat memeriksa properti SpatialInteractionSource::IsGraspSupported untuk menentukan apakah sumber menyediakan tindakan pemahaman.
Pose genggaman vs. pose menunjuk
Windows Mixed Reality mendukung pengontrol gerakan dalam berbagai faktor bentuk. Ini juga mendukung sistem pelacakan tangan artikulasi. Semua sistem ini memiliki hubungan yang berbeda antara posisi tangan dan arah "maju" alami yang harus digunakan aplikasi untuk menunjuk atau merender objek di tangan pengguna. Untuk mendukung semua ini, ada dua jenis pose 3D yang disediakan untuk pelacakan tangan dan pengontrol gerakan. Yang pertama adalah pose genggaman, yang mewakili posisi tangan pengguna. Yang kedua adalah menunjuk pose, yang mewakili sinar penunjuk yang berasal dari tangan atau pengontrol pengguna. Jadi, jika Anda ingin merender tangan pengguna atau objek yang dipegang di tangan pengguna, seperti pedang atau pistol, gunakan pose genggaman. Jika Anda ingin raycast dari pengontrol atau tangan, misalnya ketika pengguna **mengarah ke UI, gunakan pose penunjuk.
Anda dapat mengakses pose genggaman melalui SpatialInteractionSourceState::P roperties::TryGetLocation(...). Ini didefinisikan sebagai berikut:
- Posisi genggaman: Sentroid telapak tangan saat memegang pengontrol secara alami, disesuaikan ke kiri atau kanan untuk memusatkan posisi di dalam pegangan.
- Sumbu kanan orientasi genggaman: Ketika Anda benar-benar membuka tangan Anda untuk membentuk pose 5 jari yang datar, sinar yang normal ke telapak tangan Anda (maju dari telapak kiri, mundur dari kanan telapak tangan)
- Sumbu Teruskan orientasi genggaman: Saat Anda menutup sebagian tangan Anda (seolah-olah memegang pengontrol), sinar yang menunjuk "maju" melalui tabung yang dibentuk oleh jari-jari anda yang bukan jempol.
- Sumbu Atas orientasi genggaman: Sumbu Atas tersirat oleh definisi Kanan dan Teruskan.
Anda dapat mengakses pose pointer melalui SpatialInteractionSourceState::P roperties::TryGetLocation(...)::SourcePointerPose atau SpatialInteractionSourceState::TryGetPointerPose(...)::TryGetInteractionSourcePose.
Properti input khusus pengontrol
Untuk pengontrol, SpatialInteractionSource memiliki properti Pengontrol dengan kemampuan tambahan.
- HasThumbstick: Jika true, pengontrol memiliki thumbstick. Periksa properti ControllerProperties dari SpatialInteractionSourceState untuk memperoleh nilai thumbstick x dan y (ThumbstickX dan ThumbstickY), serta keadaannya yang ditekan (IsThumbstickPressed).
- HasTouchpad: Jika true, pengontrol memiliki touchpad. Periksa properti ControllerProperties dari SpatialInteractionSourceState untuk memperoleh nilai touchpad x dan y (TouchpadX dan TouchpadY), dan untuk mengetahui apakah pengguna menyentuh pad (IsTouchpadTouched) dan jika mereka menekan touchpad ke bawah (IsTouchpadPressed).
- SimpleHapticsController: API SimpleHapticsController untuk pengontrol memungkinkan Anda memeriksa kemampuan haptics pengontrol, dan juga memungkinkan Anda untuk mengontrol umpan balik haptic.
Rentang untuk touchpad dan thumbstick adalah -1 hingga 1 untuk kedua sumbu (dari bawah ke atas, dan dari kiri ke kanan). Rentang untuk pemicu analog, yang diakses menggunakan properti SpatialInteractionSourceState::SelectPressedValue, memiliki rentang 0 hingga 1. Nilai 1 berkorelasi dengan IsSelectPressed sama dengan true; nilai lain yang berkorelasi dengan IsSelectPressed sama dengan false.
Pelacakan tangan artikulasi
API Windows Mixed Reality menyediakan dukungan penuh untuk pelacakan tangan artikulasi, misalnya pada HoloLens 2. Pelacakan tangan artikulasi dapat digunakan untuk mengimplementasikan manipulasi langsung dan model input titik dan penerapan dalam aplikasi Anda. Ini juga dapat digunakan untuk menulis interaksi kustom sepenuhnya.
Kerangka tangan
Pelacakan tangan artikulasi menyediakan 25 kerangka sendi yang memungkinkan berbagai jenis interaksi. Kerangka menyediakan lima sendi untuk jari indeks/tengah/cincin/kecil, empat sendi untuk ibu jari, dan satu sendi pergelangan tangan. Sambungan pergelangan tangan berfungsi sebagai dasar hierarki. Gambar berikut mengilustrasikan tata letak kerangka.
Dalam kebanyakan kasus, setiap sendi dinamai berdasarkan tulang yang diwakilinya. Karena ada dua tulang di setiap sendi, kami menggunakan konvensi penamaan setiap sendi berdasarkan tulang anak di lokasi itu. Tulang anak didefinisikan sebagai tulang lebih jauh dari pergelangan tangan. Misalnya, sendi "Index Proximal" berisi posisi awal tulang proksimal indeks, dan orientasi tulang itu. Itu tidak mengandung posisi akhir tulang. Jika Anda membutuhkannya, Anda akan mendapatkannya dari sambungan berikutnya dalam hierarki, gabungan "Index Intermediate".
Selain 25 sendi hierarkis, sistem menyediakan sendi telapak tangan. Telapak tangan biasanya tidak dianggap sebagai bagian dari struktur kerangka. Ini disediakan hanya sebagai cara mudah untuk mendapatkan posisi dan orientasi tangan secara keseluruhan.
Informasi berikut disediakan untuk setiap sambungan:
Nama | Deskripsi |
---|---|
Position | Posisi 3D sambungan, tersedia dalam sistem koordinat yang diminta. |
Orientasi | Orientasi tulang 3D, tersedia dalam sistem koordinat yang diminta. |
Radius | Jarak ke permukaan kulit pada posisi sendi. Berguna untuk menyetel interaksi langsung atau visualisasi yang mengandalkan lebar jari. |
Akurasi | Memberikan petunjuk tentang seberapa yakin sistem tentang informasi bersama ini. |
Anda dapat mengakses data kerangka tangan melalui fungsi pada SpatialInteractionSourceState. Fungsi ini disebut TryGetHandPose, dan mengembalikan objek yang disebut HandPose. Jika sumber tidak mendukung tangan artikulasi, maka fungsi ini akan mengembalikan null. Setelah memiliki HandPose, Anda bisa mendapatkan data bersama saat ini dengan memanggil TryGetJoint, dengan nama gabungan yang Anda minati. Data dikembalikan sebagai struktur JointPose . Kode berikut mendapatkan posisi ujung jari indeks. Variabel currentState mewakili instans SpatialInteractionSourceState.
using namespace winrt::Windows::Perception::People;
using namespace winrt::Windows::Foundation::Numerics;
auto handPose = currentState.TryGetHandPose();
if (handPose)
{
JointPose joint;
if (handPose.TryGetJoint(desiredCoordinateSystem, HandJointKind::IndexTip, joint))
{
float3 indexTipPosition = joint.Position;
// Do something with the index tip position
}
}
Jala tangan
API pelacakan tangan artikulasi memungkinkan jala tangan segitiga yang sepenuhnya cacat. Jala ini dapat berubah secara real time bersama dengan kerangka tangan, dan berguna untuk visualisasi dan teknik fisika tingkat lanjut. Untuk mengakses jala tangan, Anda harus terlebih dahulu membuat objek HandMeshObserver dengan memanggil TryCreateHandMeshObserverAsync di SpatialInteractionSource. Ini hanya perlu dilakukan sekali per sumber, biasanya pertama kali Anda melihatnya. Itu berarti Anda akan memanggil fungsi ini untuk membuat objek HandMeshObserver setiap kali tangan memasuki FOV. Ini adalah fungsi asinkron, jadi Anda harus berurusan dengan sedikit konkurensi di sini. Setelah tersedia, Anda dapat meminta objek HandMeshObserver untuk buffer indeks segitiga dengan memanggil GetTriangleIndices. Indeks tidak mengubah bingkai di atas bingkai, sehingga Anda bisa mendapatkannya sekali dan menyimpannya selama masa pakai sumber. Indeks disediakan dalam urutan berliku searah jajar.
Kode berikut memutar std::thread yang dilepas untuk membuat pengamat jala dan mengekstrak buffer indeks setelah pengamat jala tersedia. Ini dimulai dari variabel yang disebut currentState, yang merupakan instans SpatialInteractionSourceState yang mewakili tangan yang dilacak.
using namespace Windows::Perception::People;
std::thread createObserverThread([this, currentState]()
{
HandMeshObserver newHandMeshObserver = currentState.Source().TryCreateHandMeshObserverAsync().get();
if (newHandMeshObserver)
{
unsigned indexCount = newHandMeshObserver.TriangleIndexCount();
vector<unsigned short> indices(indexCount);
newHandMeshObserver.GetTriangleIndices(indices);
// Save the indices and handMeshObserver for later use - and use a mutex to synchronize access if needed!
}
});
createObserverThread.detach();
Memulai utas yang dilepas hanyalah salah satu opsi untuk menangani panggilan asinkron. Atau, Anda dapat menggunakan fungsionalitas co_await baru yang didukung oleh C++/WinRT.
Setelah Anda memiliki objek HandMeshObserver, Anda harus memegangnya selama durasi SpatialInteractionSource yang sesuai aktif. Kemudian setiap bingkai, Anda dapat memintanya untuk buffer vertex terbaru yang mewakili tangan dengan memanggil GetVertexStateForPose dan meneruskan instans HandPose yang mewakili pose yang Anda inginkan simpulnya. Setiap puncak dalam buffer memiliki posisi dan normal. Berikut adalah contoh cara mendapatkan set simpul saat ini untuk jala tangan. Seperti sebelumnya, variabel currentState mewakili instans SpatialInteractionSourceState.
using namespace winrt::Windows::Perception::People;
auto handPose = currentState.TryGetHandPose();
if (handPose)
{
std::vector<HandMeshVertex> vertices(handMeshObserver.VertexCount());
auto vertexState = handMeshObserver.GetVertexStateForPose(handPose);
vertexState.GetVertices(vertices);
auto meshTransform = vertexState.CoordinateSystem().TryGetTransformTo(desiredCoordinateSystem);
if (meshTransform != nullptr)
{
// Do something with the vertices and mesh transform, along with the indices that you saved earlier
}
}
Berbeda dengan sendi kerangka, API jala tangan tidak memungkinkan Anda menentukan sistem koordinat untuk simpul. Sebagai gantinya, HandMeshVertexState menentukan sistem koordinat tempat simpul disediakan. Anda kemudian bisa mendapatkan transformasi jala dengan memanggil TryGetTransformTo dan menentukan sistem koordinat yang Anda inginkan. Anda harus menggunakan transformasi jala ini setiap kali Anda bekerja dengan simpul. Pendekatan ini mengurangi overhead CPU, terutama jika Anda hanya menggunakan jala untuk tujuan penyajian.
Tatapan dan Terapkan gerakan komposit
Untuk aplikasi yang menggunakan model input tatapan dan penerapan, terutama pada HoloLens (gen pertama), API Input Spasial menyediakan SpatialGestureRecognizer opsional yang dapat digunakan untuk mengaktifkan gerakan komposit yang dibangun di atas peristiwa 'pilih'. Dengan merutekan interaksi dari SpatialInteractionManager ke SpatialGestureRecognizer hologram, aplikasi dapat mendeteksi peristiwa Tap, Hold, Manipulation, dan Navigation secara seragam di seluruh perangkat input tangan, suara, dan spasial, tanpa harus menangani pers dan rilis secara manual.
SpatialGestureRecognizer hanya melakukan disambiguasi minimal antara serangkaian gerakan yang Anda minta. Misalnya, jika Anda meminta hanya Ketuk, pengguna dapat menahan jari mereka selama mereka suka dan Ketukan akan tetap terjadi. Jika Anda meminta Ketuk dan Tahan, setelah sekitar satu detik menahan jarinya, gerakan akan dipromosikan ke Penangguhan dan Ketukan tidak akan lagi terjadi.
Untuk menggunakan SpatialGestureRecognizer, tangani peristiwa InteractionDetected SpatialInteractionManager dan ambil SpatialPointerPose yang terekspos di sana. Gunakan sinar tatapan kepala pengguna dari pose ini untuk bersinggungan dengan hologram dan jala permukaan di lingkungan pengguna untuk menentukan apa yang ingin berinteraksi dengan pengguna. Kemudian, rutekan SpatialInteraction dalam argumen peristiwa ke SpatialGestureRecognizer hologram target, menggunakan metode CaptureInteraction-nya . Ini mulai menafsirkan interaksi tersebut sesuai dengan SpatialGestureSettings yang diatur pada recognizer tersebut pada waktu pembuatan - atau oleh TrySetgestureSettings.
Pada HoloLens (generasi pertama), interaksi dan gerakan harus memperoleh penargetan mereka dari tatapan kepala pengguna, daripada merender atau berinteraksi di lokasi tangan. Setelah interaksi dimulai, gerakan relatif tangan dapat digunakan untuk mengontrol gerakan, seperti halnya gerakan Manipulasi atau Navigasi.