Semua tentang loop frame
Baru-baru ini, saya memublikasikan Virtual reality comes to the 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, yaitu loop tanpa batas yang dikontrol agen pengguna tempat konten berulang kali digambar ke layar. Konten digambar dalam blok terpisah yang disebut frame. Urutan frame menciptakan ilusi gerakan.
Yang tidak termasuk 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 di atas WebGL dan WebGL2. Framework tersebut mencakup three.js, babylonjs, dan PlayCanvas, sedangkan A-Frame dan React 360 dirancang untuk berinteraksi dengan WebXR.
Artikel ini bukan tutorial framework atau WebGL. Panduan ini menjelaskan dasar-dasar loop frame menggunakan contoh Sesi VR Immersive dari Immersive Web Working Group (demo, sumber). Jika Anda ingin mempelajari WebGL atau salah satu framework, internet menyediakan daftar artikel yang terus bertambah.
Para pemain dan game
Saat mencoba memahami loop frame, saya terus tersesat dalam detail. Ada banyak objek yang digunakan, dan beberapa di antaranya hanya diberi nama oleh 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'.
Pemain
XRViewerPose
Pose adalah posisi dan orientasi sesuatu dalam ruang 3D. Penonton
dan perangkat input memiliki pose, tetapi pose penontonlah yang kita bahas
di sini. Baik penampil maupun pose perangkat input memiliki atribut transform
yang menjelaskan
posisinya sebagai vektor dan orientasinya sebagai kuarternion yang terkait dengan
asal. Asal ditentukan berdasarkan jenis ruang referensi yang diminta saat
memanggil XRSession.requestReferenceSpace()
.
Ruang referensi memerlukan sedikit penjelasan. Saya membahasnya secara mendalam di Augmented
reality. Contoh yang saya gunakan sebagai dasar artikel ini menggunakan ruang referensi 'local'
yang berarti tempat asalnya berada pada posisi pelihat pada saat pembuatan sesi tanpa lantai yang jelas, dan posisi presisinya dapat bervariasi berdasarkan platform.
XRView
Tampilan sesuai dengan kamera yang melihat tampilan virtual. Tampilan juga memiliki
atribut transform
yang menjelaskan posisinya sebagai vektor dan orientasinya.
Ini disediakan sebagai pasangan vektor/kuaternion dan sebagai matriks yang setara,
Anda dapat menggunakan representasi mana pun, bergantung pada mana yang paling sesuai dengan kode Anda. Setiap
tampilan sesuai dengan tampilan atau bagian tampilan yang digunakan oleh perangkat untuk
menampilkan gambar kepada penampil. Objek XRView
ditampilkan dalam array dari
objek XRViewerPose
. Jumlah tampilan dalam array bervariasi. Di perangkat
seluler, tampilan AR memiliki satu tampilan, yang mungkin atau mungkin tidak mencakup 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 hampir sama, yang menyatakan bahwa 'menyediakan
penautan' antara keduanya. Dengan demikian, pemain lain dapat mengaksesnya.
Secara umum, objek WebGL menyimpan informasi status untuk merender grafik 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 itu
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, aplikasi memerlukan WebGLFramebuffer
dan XRViewport.
Perhatikan hubungan antara XRWebGLLayer
dan WebGLRenderingContext
. Satu
sesuai dengan perangkat penampil dan yang lainnya sesuai dengan halaman web.
WebGLFramebuffer
dan XRViewport
diteruskan dari yang pertama ke yang kedua.
Pertandingan
Setelah mengetahui siapa pemainnya, mari kita lihat game yang mereka mainkan. Ini adalah permainan yang dimulai dari awal di 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 dari 60 hingga 144. AR untuk Android berjalan pada kecepatan 30 frame per detik. Kode Anda tidak boleh mengasumsikan kecepatan frame 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 Anda:
- Telepon
XRSession.requestAnimationFrame()
lagi. - Dapatkan pose penampil.
- Teruskan ('bind')
WebGLFramebuffer
dariXRWebGLLayer
keWebGLRenderingContext
. - 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.
Dapatkan pose penonton
Bisa dibilang sudah cukup. Untuk menggambar apa pun dalam AR atau VR, saya perlu mengetahui
lokasi penonton dan tempat mereka melihat. Posisi dan
orientasi penampil disediakan oleh objek
XRViewerPose. Saya
mendapatkan pose penampil dengan memanggil XRFrame.getViewerPose()
pada frame
animasi saat ini. Saya meneruskan ruang referensi yang saya peroleh
ketika 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 mewakili posisi keseluruhan pengguna, yang berarti
kepala penonton atau kamera ponsel jika menggunakan smartphone.
Pose memberi tahu aplikasi Anda lokasi penampil. Rendering gambar yang sebenarnya menggunakan
objek XRView
, yang akan saya bahas sebentar lagi.
Sebelum melanjutkan, saya menguji apakah pose penampil ditampilkan jika sistem kehilangan pelacakan atau memblokir pose karena alasan privasi. Pelacakan adalah kemampuan perangkat XR untuk mengetahui di mana dan/atau perangkat inputnya relatif terhadap lingkungan. Pelacakan dapat hilang dengan beberapa cara, dan bervariasi bergantung pada metode yang digunakan untuk pelacakan. Misalnya, jika kamera di headset atau ponsel digunakan untuk melacak, perangkat mungkin kehilangan kemampuannya untuk menentukan lokasinya dalam situasi dengan cahaya redup atau tidak ada cahaya, atau jika kamera tertutup.
Contoh pemblokiran pose karena alasan privasi adalah jika headset menampilkan
dialog keamanan seperti permintaan izin, browser dapat berhenti memberikan
pose ke aplikasi saat hal ini terjadi. Namun, saya telah memanggil
XRSession.requestAnimationFrame()
sehingga jika sistem dapat pulih, loop
frame akan berlanjut. Jika tidak, agen pengguna akan mengakhiri sesi dan memanggil pengendali peristiwa end
.
Pengalihan 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 gambar dilakukan menggunakan
WebGL API, WebGL2 API, atau framework berbasis WebGL seperti Three.js. Konteks
ini diteruskan ke objek sesi melalui updateRenderState()
, beserta
instance XRWebGLLayer
baru.
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
}
}
Melakukan iterasi pada setiap objek XRView
Setelah mendapatkan pose dan mengikat framebuffer, sekarang saatnya mendapatkan
area pandang. XRViewerPose
berisi array antarmuka XRView yang masing-masing
mewakili layar atau bagian layar. Elemen 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 loop dan menggambar
gambar terpisah untuk masing-masing.
Saat menerapkan untuk augmented reality berbasis ponsel, saya hanya akan memiliki satu tampilan, tetapi saya tetap akan menggunakan loop. Meskipun mungkin tampak tidak ada gunanya melakukan iterasi melalui satu tampilan, hal ini 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
}
}
}
Meneruskan objek XRViewport ke WebGLRenderingContext
Objek XRView
mengacu pada apa yang dapat diamati di layar. Namun, untuk menggambar ke tampilan tersebut,
saya memerlukan koordinat dan dimensi yang spesifik 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 menulis artikel ini, saya berdebat dengan beberapa kolega tentang penamaan
objek webGLRenContext
. Skrip contoh dan sebagian besar kode WebXR hanya
memanggil variabel ini gl
. Saat mencoba memahami sampel, saya terus
lupa apa yang dimaksud dengan gl
. Saya menamainya webGLRenContext
untuk mengingatkan Anda
saat Anda mempelajari bahwa ini adalah instance WebGLRenderingContext
.
Alasannya adalah karena menggunakan gl
memungkinkan nama metode terlihat seperti
nama metode 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 menyarankan hal itu. Jauh lebih mudah untuk 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 setiap fitur di Status Chrome.
Foto oleh JESHOOTS.COM di Unsplash