Semua hal tentang loop frame
Baru-baru ini, saya memublikasikan Virtual reality datang ke web, sebuah artikel yang memperkenalkan konsep dasar di balik WebXR Device API. Saya juga memberikan petunjuk untuk meminta, memasuki, dan mengakhiri sesi XR.
Artikel ini menjelaskan loop frame, yang merupakan loop tanpa batas yang dikontrol agen pengguna, yang memungkinkan konten berulang kali digambar ke layar. Konten digambar dalam blok terpisah yang disebut frame. Urutan bingkai menciptakan ilusi gerakan.
Yang tidak ada dalam artikel ini
WebGL dan WebGL2 adalah satu-satunya cara untuk merender konten selama loop frame di Aplikasi WebXR. Untungnya, banyak framework yang menyediakan lapisan abstraksi selain WebGL dan WebGL2. Framework tersebut mencakup three.js, babylonjs, dan PlayCanvas, sementara A-Frame dan React 360 dirancang untuk berinteraksi dengan WebXR.
Artikel ini bukan tutorial WebGL maupun framework. Panduan ini menjelaskan dasar-dasar loop frame menggunakan contoh Sesi Immersive VR Session Group Web Working Group (demo, sumber). Jika Anda ingin mempelajari WebGL atau salah satu framework, internet menyediakan daftar artikel yang terus bertambah.
Pemain dan game
Ketika mencoba memahami loop frame, saya terus bingung dengan detailnya. Ada banyak objek yang sedang dimainkan, dan beberapa di antaranya hanya diberi nama berdasarkan properti referensi pada objek lain. Untuk membantu Anda tetap lurus, saya akan menjelaskan objek-objek, yang saya sebut 'pemain'. Lalu saya akan menjelaskan cara mereka berinteraksi, yang saya sebut dengan 'game'.
Para pemain
XRViewerPose
Pose adalah posisi dan orientasi sesuatu dalam ruang 3D. Penonton
dan perangkat input memiliki pose, tetapi yang menjadi perhatian penonton
adalah pose mereka. Pose penampil dan perangkat input memiliki atribut transform
yang menjelaskan
posisinya sebagai vektor dan orientasinya sebagai angka kuaternion yang relatif terhadap
asal. Asal ditentukan berdasarkan jenis ruang referensi yang diminta saat
memanggil XRSession.requestReferenceSpace()
.
Ruang referensi perlu waktu untuk dijelaskan. Saya membahasnya secara mendalam di
Augmented Reality. Contoh yang saya gunakan sebagai dasar untuk artikel ini menggunakan ruang referensi 'local'
, yang berarti asalnya berada di posisi pelihat pada saat pembuatan sesi tanpa harga minimum yang ditentukan dengan baik, dan posisi tepatnya dapat bervariasi menurut platform.
XRView
Tampilan sesuai dengan kamera yang sedang melihat adegan virtual. Tampilan juga memiliki
atribut transform
yang menjelaskan posisinya sebagai vektor dan orientasinya.
Keduanya disediakan sebagai pasangan vektor/kuarternion dan sebagai matriks yang setara. Anda dapat menggunakan salah satu representasi bergantung pada mana yang paling sesuai dengan kode Anda. Setiap
tampilan sesuai dengan tampilan atau bagian tampilan yang digunakan perangkat untuk
menyajikan gambar bagi pelihat. Objek XRView
ditampilkan dalam array dari objek XRViewerPose
. Jumlah tampilan dalam array bervariasi. Pada perangkat
seluler, scene AR memiliki satu tampilan, yang mungkin menutupi layar perangkat atau tidak.
Headset biasanya memiliki dua tampilan, satu untuk setiap mata.
XRWebGLLayer
Lapisan menyediakan sumber gambar bitmap dan deskripsi tentang cara gambar tersebut
dirender di perangkat. Deskripsi ini tidak cukup menjelaskan
apa yang dilakukan pemain ini. Saya menganggapnya sebagai perantara antara perangkat dan
WebGLRenderingContext
. MDN mengambil sudut pandang yang sama, yang menyatakan bahwa MDN 'menyediakan
hubungan' di antara keduanya. Dengan demikian, pemain lain akan mendapatkan akses ke pemain lain.
Secara umum, objek WebGL menyimpan informasi status untuk merender grafis 2D dan 3D.
WebGLFramebuffer
Framebuffer menyediakan data gambar ke WebGLRenderingContext
. Setelah
mengambilnya dari XRWebGLLayer
, Anda cukup meneruskannya ke
WebGLRenderingContext
saat ini. Selain memanggil bindFramebuffer()
(selengkapnya tentang hal tersebut
akan dibahas 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 area persegi panjang di
WebGLFramebuffer
.
WebGLRenderingContext
Konteks rendering adalah titik akses terprogram untuk kanvas (ruang yang sedang kita gambar). Untuk melakukannya, diperlukan WebGLFramebuffer
dan XRViewport.
Perhatikan hubungan antara XRWebGLLayer
dan WebGLRenderingContext
. Satu
sesuai dengan perangkat pengguna dan satu lagi sesuai dengan halaman web.
WebGLFramebuffer
dan XRViewport
diteruskan dari yang pertama ke yang kedua.
Pertandingan
Setelah kita tahu siapa pemainnya, mari kita lihat game yang mereka mainkan. Ini adalah game yang dimulai dari setiap {i>frame<i}. Ingat kembali 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 antara 60 hingga 144. AR untuk Android berjalan pada 30 frame per detik. Kode Anda tidak boleh mengasumsikan kecepatan frame tertentu.
Proses dasar untuk loop frame terlihat seperti ini:
- Panggil
XRSession.requestAnimationFrame()
. Sebagai respons, agen pengguna akan memanggilXRFrameRequestCallback
, yang Anda tentukan. - Di dalam fungsi callback:
- Telepon
XRSession.requestAnimationFrame()
lagi. - Lihat pose penonton.
- Teruskan ('ikat')
WebGLFramebuffer
dariXRWebGLLayer
keWebGLRenderingContext
. - Lakukan iterasi pada setiap objek
XRView
, mengambilXRViewport
dariXRWebGLLayer
dan meneruskannya keWebGLRenderingContext
. - Gambar sesuatu ke framebuffer.
- Telepon
Karena langkah 1 dan 2a dibahas di artikel sebelumnya, saya akan mulai dari langkah 2b.
Lihat pose penonton
Mungkin ini sudah jelas. Untuk menggambar apa pun dalam AR atau VR, saya perlu
mengetahui di mana pemirsanya dan di mana mereka melihat. Posisi dan orientasi
penonton disediakan oleh objek
XRViewerPose. Saya
mendapatkan pose pelihat 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 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 pelihat yang merepresentasikan posisi pengguna secara keseluruhan, yaitu
kepala penonton atau kamera ponsel pada smartphone.
Pose ini memberi tahu aplikasi Anda di mana penampil berada. Rendering gambar yang sebenarnya menggunakan
objek XRView
, yang akan saya bahas sebentar lagi.
Sebelum melanjutkan, saya menguji apakah pose pelihat ditampilkan jika sistem kehilangan pelacakan atau memblokir pose karena alasan privasi. Pelacakan adalah kemampuan perangkat XR untuk mengetahui lokasi dan/atau perangkat inputnya relatif terhadap lingkungan. Pelacakan dapat hilang dalam beberapa cara, dan bervariasi bergantung pada metode yang digunakan untuk pelacakan. Misalnya, jika kamera pada headset atau ponsel digunakan untuk melacak perangkat, mungkin tidak dapat lagi menentukan lokasinya dalam situasi dengan cahaya rendah atau tanpa cahaya, atau apakah kamera tertutup.
Contoh pemblokiran pose untuk alasan privasi adalah jika headset menampilkan
dialog keamanan seperti dialog izin, browser dapat berhenti menyediakan
pose ke aplikasi saat hal ini terjadi. Namun, saya sudah memanggil
XRSession.requestAnimationFrame()
sehingga jika sistem dapat pulih, loop
frame akan berlanjut. Jika tidak, agen pengguna akan mengakhiri sesi dan memanggil pengendali peristiwa end
.
Jalan memutar singkat
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 penggambaran dilakukan menggunakan WebGL API, WebGL2 API, atau framework berbasis WebGL seperti Three.js. Konteks
ini diteruskan ke objek sesi melalui updateRenderState()
, bersama dengan
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)
});
Meneruskan ('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
}
}
Melakukan iterasi pada setiap objek XRView
Setelah mendapatkan pose dan mengikat framebuffer, saatnya untuk mendapatkan
area pandang. XRViewerPose
berisi array antarmuka XRView yang masing-masing
mewakili tampilan atau sebagian tampilan. Objek ini berisi informasi
yang diperlukan untuk merender konten yang diposisikan dengan benar untuk perangkat dan
penampil, seperti ruang pandang, offset mata, dan properti optik lainnya.
Karena saya menggambar untuk dua mata, saya memiliki dua tampilan, yang saya lingkari dan menggambar
gambar terpisah untuk masing-masing mata.
Saat menerapkan augmented reality berbasis ponsel, saya hanya memiliki satu tampilan, tetapi masih menggunakan loop. Meskipun mungkin tampak tidak ada gunanya melakukan iterasi melalui satu tampilan, melakukan hal tersebut memungkinkan Anda memiliki satu jalur rendering untuk spektrum pengalaman yang 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. Tetapi untuk menggambar pada tampilan itu,
saya membutuhkan 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
Saat menulis artikel ini, saya berdiskusi dengan beberapa rekan tentang penamaan
objek webGLRenContext
. Skrip contoh dan sebagian besar kode WebXR secara sederhana
memanggil variabel gl
ini. Ketika berusaha memahami contoh, saya terus
melupakan apa yang dirujuk gl
. Saya menyebutnya webGLRenContext
untuk mengingatkan Anda
saat belajar bahwa ini adalah instance dari WebGLRenderingContext
.
Alasannya adalah penggunaan gl
memungkinkan nama metode agar terlihat seperti
padanannya 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 merasa sangat ambisius, Anda dapat menggunakan WebGL secara langsung, tetapi kami tidak menyarankannya. Jauh lebih mudah menggunakan salah satu framework yang tercantum di bagian atas.
Kesimpulan
Ini bukanlah akhir dari pembaruan atau artikel WebXR. Anda dapat menemukan referensi untuk semua antarmuka dan anggota WebXR di MDN. Untuk peningkatan mendatang pada antarmuka itu sendiri, ikuti masing-masing fitur di Status Chrome.
Foto oleh JESHOOTS.COM di Unsplash