Semua tentang loop frame
Baru-baru ini, saya memublikasikan Virtual reality hadir di web, sebuah artikel yang memperkenalkan konsep dasar di balik WebXR Device API. Saya juga memberikan petunjuk untuk meminta, memasukkan, dan mengakhiri sesi XR.
Artikel ini menjelaskan loop frame, yang merupakan loop tak terbatas yang dikontrol agen pengguna dan kontennya berulang kali digambar ke layar. Konten digambar dalam blok terpisah yang disebut frame. Urutan frame menciptakan ilusi gerakan.
Yang tidak dibahas dalam artikel ini
WebGL dan WebGL2 adalah satu-satunya cara untuk merender konten selama loop frame di Aplikasi WebXR. Untungnya, banyak framework menyediakan lapisan abstraksi di atas WebGL dan WebGL2. Framework tersebut mencakup three.js, babylonjs, dan PlayCanvas, sementara A-Frame dan React 360 dirancang untuk berinteraksi dengan WebXR.
Artikel ini menjelaskan dasar-dasar loop frame, menggunakan contoh Sesi VR Imersif Immersive Web Working Group (demo, sumber). Jika Anda ingin mempelajari WebGL atau salah satu framework, ada daftar referensi online yang terus bertambah.
Pemain dan game
Saat mencoba memahami loop frame, saya terus tersesat dalam detailnya. Ada banyak objek yang terlibat, dan beberapa di antaranya hanya diberi nama berdasarkan properti referensi pada objek lain. Untuk membantu Anda memahaminya, saya akan menjelaskan objek, yang saya sebut 'pemain'. Kemudian, saya akan menjelaskan cara mereka berinteraksi, yang saya sebut 'game'.
Para pemain
XRViewerPose
Pose adalah posisi dan orientasi sesuatu dalam ruang 3D. Penampil dan perangkat input memiliki pose, tetapi di sini kita hanya berfokus pada pose penampil. Pose perangkat input dan penampil memiliki atribut transform yang menjelaskan
posisinya sebagai vektor dan orientasinya sebagai quaternion relatif terhadap
asal. Asal ditentukan berdasarkan jenis ruang referensi yang diminta saat
memanggil XRSession.requestReferenceSpace().
Ruang referensi agak sulit dijelaskan. Saya membahasnya secara mendalam di Augmented
reality. Contoh yang saya gunakan sebagai dasar artikel ini menggunakan ruang referensi 'local' yang berarti asal berada di posisi penonton pada saat pembuatan sesi tanpa lantai yang ditentukan dengan baik, dan posisi tepatnya dapat bervariasi menurut platform.
XRView
Tampilan sesuai dengan kamera yang melihat adegan virtual. Tampilan juga memiliki atribut
transform yang menjelaskan posisinya sebagai vektor dan orientasinya.
Data ini diberikan sebagai pasangan vektor/kuaternion dan sebagai matriks yang setara,
Anda dapat menggunakan representasi mana pun yang paling sesuai dengan kode Anda. Setiap
tampilan sesuai dengan layar atau sebagian layar yang digunakan oleh perangkat untuk
menampilkan gambar kepada penonton. Objek XRView ditampilkan dalam array dari objek XRViewerPose. Jumlah tampilan dalam array bervariasi. Di perangkat seluler, adegan AR memiliki satu tampilan, yang mungkin atau mungkin tidak menutupi layar perangkat.
Headset biasanya memiliki dua tampilan, satu untuk setiap mata.
XRWebGLLayer
Lapisan menyediakan sumber gambar bitmap dan deskripsi cara gambar tersebut dirender di perangkat. Deskripsi ini tidak cukup menggambarkan fungsi pemutar ini. Saya menganggapnya sebagai perantara antara perangkat dan
WebGLRenderingContext. MDN memiliki pandangan yang sama, yang menyatakan bahwa 'memberikan
keterkaitan' antara keduanya. Dengan demikian, pemain lain dapat mengaksesnya.
Secara umum, objek WebGL menyimpan informasi status untuk merender grafik 2D dan 3D.
WebGLFramebuffer
Frame buffer menyediakan data gambar ke WebGLRenderingContext. Setelah
mengambilnya dari XRWebGLLayer, Anda meneruskannya ke
WebGLRenderingContext saat ini. Selain memanggil bindFramebuffer() (selengkapnya tentang hal ini nanti), Anda tidak akan pernah mengakses objek ini secara langsung. Anda hanya akan meneruskannya dari
XRWebGLLayer ke WebGLRenderingContext.
XRViewport
Area pandang memberikan koordinat dan dimensi wilayah persegi panjang di
WebGLFramebuffer.
WebGLRenderingContext
Konteks rendering adalah titik akses terprogram untuk kanvas (ruang tempat kita
menggambar). Untuk melakukannya, diperlukan WebGLFramebuffer dan XRViewport.
Perhatikan hubungan antara XRWebGLLayer dan WebGLRenderingContext. Satu
sesuai dengan perangkat penonton dan yang lainnya sesuai dengan halaman web.
WebGLFramebuffer dan XRViewport diteruskan dari yang pertama ke yang terakhir.
XRWebGLLayer dan WebGLRenderingContext
Game
Setelah mengetahui siapa pemainnya, mari kita lihat game yang mereka mainkan. Ini adalah game yang dimulai ulang dengan setiap frame. Ingat bahwa frame adalah bagian dari loop frame yang terjadi pada kecepatan yang bergantung pada hardware yang mendasarinya. Untuk aplikasi VR, frame per detik dapat berkisar dari 60 hingga 144. AR untuk Android berjalan pada 30 frame per detik. Kode Anda tidak boleh mengasumsikan frame rate tertentu.
Proses dasar untuk loop frame terlihat seperti ini:
- Hubungi
XRSession.requestAnimationFrame(). Sebagai respons, agen pengguna memanggilXRFrameRequestCallback, yang ditentukan oleh Anda. - Di dalam fungsi callback:
- Telepon
XRSession.requestAnimationFrame()lagi. - Dapatkan pose penonton.
- Teruskan ('bind')
WebGLFramebufferdariXRWebGLLayerkeWebGLRenderingContext. - Lakukan iterasi pada setiap objek
XRView, ambilXRViewport-nya dariXRWebGLLayer, lalu teruskan keWebGLRenderingContext. - Menggambar sesuatu ke framebuffer.
- Telepon
Karena langkah 1 dan 2a telah dibahas dalam artikel sebelumnya, saya akan memulai dari langkah 2b.
Mendapatkan pose penonton
Mungkin ini terdengar jelas. Untuk menggambar apa pun dalam AR atau VR, saya perlu mengetahui lokasi penonton dan ke mana mereka melihat. Posisi dan orientasi penonton disediakan oleh objek XRViewerPose. Saya
mendapatkan pose penonton dengan memanggil XRFrame.getViewerPose() pada frame
animasi saat ini. Saya meneruskan ruang referensi yang saya peroleh saat menyiapkan sesi. Nilai yang ditampilkan oleh objek ini selalu relatif terhadap ruang referensi yang saya minta saat saya memasuki sesi saat ini. Seperti yang mungkin Anda ingat, saya harus meneruskan ruang referensi saat ini saat meminta pose.
function onXRFrame(hrTime, xrFrame) {
let xrSession = xrFrame.session;
xrSession.requestAnimationFrame(onXRFrame);
let xrViewerPose = xrFrame.getViewerPose(xrRefSpace);
if (xrViewerPose) {
// Render based on the pose.
}
}
Ada satu pose penonton yang merepresentasikan posisi keseluruhan pengguna, yang berarti
kepala penonton atau kamera ponsel.
Pose memberi tahu aplikasi Anda tentang lokasi penonton. Rendering gambar sebenarnya menggunakan objek XRView, yang akan saya bahas sebentar lagi.
Sebelum melanjutkan, saya menguji apakah pose penonton ditampilkan jika sistem kehilangan pelacakan atau memblokir pose karena alasan privasi. Pelacakan adalah kemampuan perangkat XR untuk mengetahui lokasi perangkat dan perangkat inputnya relatif terhadap lingkungan. Pelacakan dapat terhenti dalam beberapa cara, dan bervariasi bergantung pada metode yang digunakan untuk pelacakan. Misalnya, jika kamera di headset atau ponsel digunakan untuk melacak perangkat, perangkat mungkin kehilangan kemampuannya untuk menentukan lokasinya dalam situasi dengan cahaya rendah atau tanpa cahaya, atau jika kamera tertutup.
Contoh pemblokiran postur karena alasan privasi adalah jika headset menampilkan
dialog keamanan seperti perintah izin, browser dapat berhenti memberikan
postur ke aplikasi saat hal ini terjadi. Namun, saya telah memanggil
XRSession.requestAnimationFrame() sehingga jika sistem dapat dipulihkan, loop
frame akan berlanjut. Jika tidak, agen pengguna akan mengakhiri sesi dan memanggil handler peristiwa
end.
Berhenti sebentar
Langkah berikutnya memerlukan objek yang dibuat selama
penyiapan sesi.
Ingat bahwa
saya membuat kanvas dan menginstruksikannya untuk membuat konteks
rendering Web GL yang kompatibel dengan XR, yang saya dapatkan dengan memanggil canvas.getContext(). Semua gambar dibuat menggunakan
WebGL API, WebGL2 API, atau framework berbasis WebGL seperti Three.js. Konteks
ini diteruskan ke objek sesi dengan updateRenderState(), selain
instance baru XRWebGLLayer.
let canvas = document.createElement('canvas');
// The rendering context must be based on WebGL or WebGL2
let webGLRenContext = canvas.getContext('webgl', { xrCompatible: true });
xrSession.updateRenderState({
baseLayer: new XRWebGLLayer(xrSession, webGLRenContext)
});
Teruskan ('bind') WebGLFramebuffer
XRWebGLLayer menyediakan framebuffer untuk WebGLRenderingContext
yang disediakan khusus untuk digunakan dengan WebXR dan menggantikan framebuffer default konteks rendering. Hal ini disebut 'binding' dalam bahasa WebGL.
function onXRFrame(hrTime, xrFrame) {
let xrSession = xrFrame.session;
xrSession.requestAnimationFrame(onXRFrame);
let xrViewerPose = xrFrame.getViewerPose(xrRefSpace);
if (xrViewerPose) {
let glLayer = xrSession.renderState.baseLayer;
webGLRenContext.bindFramebuffer(webGLRenContext.FRAMEBUFFER, glLayer.framebuffer);
// Iterate over the views
}
}
Lakukan iterasi pada setiap objek XRView
Setelah mendapatkan pose dan mengikat framebuffer, saatnya mendapatkan
viewport. XRViewerPose berisi array antarmuka XRView yang masing-masing merepresentasikan layar atau sebagian layar. Informasi ini berisi informasi yang diperlukan untuk merender konten yang diposisikan dengan benar untuk perangkat dan penonton, seperti bidang pandang, offset mata, dan properti optik lainnya.
Karena saya menggambar untuk dua mata, saya memiliki dua tampilan, yang saya lakukan perulangan dan menggambar gambar terpisah untuk masing-masing mata.
Saat menerapkan augmented reality berbasis ponsel, saya hanya akan memiliki satu tampilan, tetapi saya tetap akan menggunakan loop. Meskipun iterasi melalui satu tampilan mungkin tampak tidak ada gunanya, tindakan ini memungkinkan Anda memiliki jalur rendering tunggal untuk spektrum pengalaman imersif. Ini adalah perbedaan penting antara WebXR dan sistem imersif lainnya.
function onXRFrame(hrTime, xrFrame) {
let xrSession = xrFrame.session;
xrSession.requestAnimationFrame(onXRFrame);
let xrViewerPose = xrFrame.getViewerPose(xrRefSpace);
if (xrViewerPose) {
let glLayer = xrSession.renderState.baseLayer;
webGLRenContext.bindFramebuffer(webGLRenContext.FRAMEBUFFER, glLayer.framebuffer);
for (let xrView of xrViewerPose.views) {
// Pass viewports to the context
}
}
}
Teruskan objek XRViewport ke WebGLRenderingContext
Objek XRView mengacu pada apa yang dapat diamati di layar. Namun, untuk menggambar tampilan tersebut, saya memerlukan koordinat dan dimensi yang khusus untuk perangkat saya. Seperti framebuffer, saya memintanya dari XRWebGLLayer dan meneruskannya ke
WebGLRenderingContext.
function onXRFrame(hrTime, xrFrame) {
let xrSession = xrFrame.session;
xrSession.requestAnimationFrame(onXRFrame);
let xrViewerPose = xrFrame.getViewerPose(xrRefSpace);
if (xrViewerPose) {
let glLayer = xrSession.renderState.baseLayer;
webGLRenContext.bindFramebuffer(webGLRenContext.FRAMEBUFFER, glLayer.framebuffer);
for (let xrView of xrViewerPose.views) {
let viewport = glLayer.getViewport(xrView);
webGLRenContext.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
// Draw something to the framebuffer
}
}
}
webGLRenContext
Dalam penulisan, saya berdebat dengan beberapa rekan kerja mengenai penamaan objek webGLRenContext. Skrip contoh dan sebagian besar kode WebXR
memanggil variabel ini gl. Saat berupaya memahami sampel, saya terus
lupa apa yang dirujuk oleh gl. Saya menyebutnya webGLRenContext untuk mengingatkan Anda
selama Anda mempelajari bahwa ini adalah instance WebGLRenderingContext.
Alasannya adalah penggunaan gl memungkinkan nama metode terlihat seperti rekanannya di OpenGL ES 2.0 API, yang digunakan untuk membuat VR dalam bahasa yang dikompilasi. Fakta ini jelas jika Anda telah menulis aplikasi VR menggunakan OpenGL, tetapi membingungkan jika Anda benar-benar baru mengenal teknologi ini.
Menggambar sesuatu ke framebuffer
Jika Anda merasa sangat ambisius, Anda dapat menggunakan WebGL secara langsung, tetapi saya tidak merekomendasikannya. Akan jauh lebih mudah untuk menggunakan salah satu framework yang tercantum di bagian atas.
Kesimpulan
Ini bukan akhir dari update atau artikel WebXR. Anda dapat menemukan referensi untuk semua antarmuka dan anggota WebXR di MDN. Untuk mengetahui peningkatan mendatang pada antarmuka itu sendiri, ikuti setiap fitur di Chrome Status.