Kare döngüsü hakkında
Kısa süre önce, WebXR Device API'nin temel kavramlarını tanıtan Sanal gerçeklik web'e geliyor başlıklı bir makale yayınladım. Ayrıca, XR oturumu isteğinde bulunma, oturuma girme ve oturumu sonlandırmayla ilgili talimatlar da sağladım.
Bu makalede, içeriğin ekrana tekrar tekrar çizildiği, kullanıcı aracısı tarafından kontrol edilen sonsuz bir döngü olan kare döngüsü açıklanmaktadır. İçerik, kare adı verilen ayrı bloklar halinde çizilir. Karelerin art arda gelmesi hareket yanılsaması oluşturur.
Bu makale ne değildir?
WebGL ve WebGL2, bir WebXR uygulamasında kare döngüsü sırasında içerik oluşturmanın tek yoludur. Neyse ki birçok çerçeve, WebGL ve WebGL2'nin üzerine bir soyutlama katmanı sağlar. Bu çerçeveler three.js, babylonjs ve PlayCanvas içerir. A-Frame ve React 360 ise WebXR ile etkileşim için tasarlanmıştır.
Bu makale bir WebGL veya çerçeve eğitimi değildir. Bu makalede, Immersive Web Çalışma Grubu'nun Immersive VR oturumu örneği (demo, kaynak) kullanılarak kare döngüsünün temelleri açıklanmaktadır. WebGL veya çerçevelerden birini incelemek isterseniz internet, giderek büyüyen bir makale listesi sunar.
Oyuncular ve oyun
Kare döngüsünü anlamaya çalışırken ayrıntılarda kayboldum. Oyunda çok sayıda nesne vardır ve bunların bazıları yalnızca diğer nesnelerdeki referans özelliklerle adlandırılmıştır. Konuyu daha net anlayabilmeniz için "oyuncu" olarak adlandırdığım nesneleri açıklayacağım. Ardından, bu öğelerin nasıl etkileşimde bulunduğunu açıklayacağım. Buna "oyun" diyorum.
Oyuncular
XRViewerPose
Poz, bir nesnenin 3D uzaydaki konumu ve yönüdür. Hem izleyiciler hem de giriş cihazları bir duruşa sahiptir. Ancak burada izleyicinin duruşu önemlidir. Hem izleyici hem de giriş cihazı duruşlarının, konumunu bir vektör olarak ve orijine göre yönelimini bir dört boyutlu vektör olarak tanımlayan bir transform
özelliği vardır. XRSession.requestReferenceSpace()
çağrılırken kaynak, istenen referans alanı türüne göre belirtilir.
Referans alanları biraz açıklama gerektirir. Bunları artırılmış gerçeklik ile ayrıntılı olarak anlatıyorum. Bu makalenin temeli olarak kullandığım örnekte 'local'
referans alanı kullanılmaktadır. Bu, orijinin, oturum oluşturulduğu sırada izleyicinin konumunda olduğu, iyi tanımlanmış bir zemin olmadığı ve tam konumunun platforma göre değişebileceği anlamına gelir.
XRView
Görünüm, sanal sahneyi görüntüleyen bir kameraya karşılık gelir. Görünümlerin, vektör olarak konumunu ve yönünü açıklayan bir transform
özelliği de vardır.
Bunlar hem vektör/dört boyutlu vektör çifti hem de eşdeğer matris olarak sağlanır. Kodunuza en uygun olan temsili kullanabilirsiniz. Her görüntüleme, görüntüyü izleyiciye sunmak için bir cihaz tarafından kullanılan bir ekrana veya ekranın bir bölümüne karşılık gelir. XRView
nesneleri, XRViewerPose
nesnesinden bir dizgede döndürülür. Dizideki görüntüleme sayısı değişiklik gösterir. Mobil cihazlarda AR sahnesinin tek bir görünümü vardır. Bu görünüm, cihaz ekranını kaplayabilir veya kaplamayabilir.
Kulaklıklar genellikle her göz için iki görünüme sahiptir.
XRWebGLLayer
Katmanlar, bit eşlem resimleri için bir kaynak ve bu resimlerin cihazda nasıl oluşturulacağına ilişkin açıklamalar sağlar. Bu açıklama, oynatıcının işlevini tam olarak yansıtmıyor. Bunu bir cihaz ile WebGLRenderingContext
arasında aracı olarak düşünmeye başladım. MDN de bu konuda benzer bir bakış açısına sahip olup bu iki öğe arasında "bağlantı sağladığını" belirtir. Bu nedenle, diğer oyunculara erişim sağlar.
Genel olarak WebGL nesneleri, 2D ve 3D grafikleri oluşturmak için durum bilgilerini depolar.
WebGLFramebuffer
Bir çerçeve arabelleği, WebGLRenderingContext
görüntü verilerini sağlar. XRWebGLLayer
bölümünden aldıktan sonra mevcut WebGLRenderingContext
öğesine aktarmanız yeterlidir. bindFramebuffer()
'ü çağırmanın dışında (bu konu hakkında daha fazla bilgiyi aşağıda bulabilirsiniz) bu nesneye hiçbir zaman doğrudan erişmezsiniz. Yalnızca XRWebGLLayer
'ten WebGLRenderingContext'e iletmeniz yeterlidir.
XRViewport
Görüntü alanı, WebGLFramebuffer
içindeki dikdörtgen bir bölgenin koordinatlarını ve boyutlarını sağlar.
WebGLRenderingContext
Oluşturma bağlamı, bir kanvas (üzerinde çizim yaptığımız alan) için programatik bir erişim noktasıdır. Bunun için hem WebGLFramebuffer
hem de XRViewport gerekir.
XRWebGLLayer
ile WebGLRenderingContext
arasındaki ilişkiye dikkat edin. Biri görüntüleyenin cihazına, diğeri ise web sayfasına karşılık gelir.
WebGLFramebuffer
ve XRViewport
, ilkinden ikincisine aktarılır.
Maç
Oyuncuların kim olduğunu öğrendiğimize göre, oynadıkları oyuna bakalım. Bu her karede baştan başlayan bir oyun. Karelerin, temel donanıma bağlı bir hızda gerçekleşen bir kare döngüsünün parçası olduğunu unutmayın. VR uygulamaları için saniyedeki kare sayısı 60 ila 144 arasında olabilir. Android için AR, saniyede 30 kare hızında çalışır. Kodunuz belirli bir kare hızını varsaymamalıdır.
Kare döngüsü için temel süreç şu şekildedir:
XRSession.requestAnimationFrame()
Hizmetleri İçin Arayın. Kullanıcı aracısı, yanıt olarak sizin tanımladığınızXRFrameRequestCallback
öğesini çağırır.- Geri çağırma işlevinizin içinde:
XRSession.requestAnimationFrame()
adlı kişiyi tekrar arayın.- İzleyicinin pozunu alın.
WebGLFramebuffer
öğesiniXRWebGLLayer
'danWebGLRenderingContext
'ye iletin ("bağlayın").- Her
XRView
nesnesini iterleyerekXRViewport
değeriniXRWebGLLayer
'den alıpWebGLRenderingContext
'a aktarın. - Çerçeve arabelleğine bir şey çizin.
Önceki makalede 1. ve 2a. adımlar ele alındığı için 2b adımından başlayacağım.
İzleyicinin duruşunu alın
Bunu söylemeye gerek yok. Artırılmış gerçeklik (AR) veya sanal gerçeklikte (VR) bir şey çizmek için
izleyicinin nerede olduğunu ve nereye baktığını bilmem gerekiyor. İzleyicinin konumu ve yönü bir XRViewerPose nesnesi tarafından sağlanır. Geçerli animasyon karesindeki XRFrame.getViewerPose()
işlevini çağırarak izleyicinin duruşunu alıyorum. Oturum oluştururken edindiğim referans alanı iletirim. Bu nesne tarafından döndürülen değerler, her zaman mevcut oturuma girdiğimde istediğim referans alanına göre olur. Hatırlayacağınız üzere, pozu isterken mevcut referans alanını geçmem gerekiyor.
function onXRFrame(hrTime, xrFrame) {
let xrSession = xrFrame.session;
xrSession.requestAnimationFrame(onXRFrame);
let xrViewerPose = xrFrame.getViewerPose(xrRefSpace);
if (xrViewerPose) {
// Render based on the pose.
}
}
Kullanıcının genel konumunu, yani akıllı telefonda kullanıcının kafasını veya telefon kamerasını temsil eden bir izleyici duruşu vardır.
Poz, uygulamanıza izleyicinin nerede olduğunu söyler. Gerçek resim oluşturma işleminde
XRView
nesne kullanılır. Buna birazdan değineceğim.
Devam etmeden önce, sistemin izlemeyi kaybetmesi veya gizlilik nedeniyle pozu engellemesi ihtimaline karşı izleyicinin pozunun döndürülüp döndürülmediğini test ederim. Takip, XR cihazın kendisinin ve/veya giriş cihazlarının çevreye göre nerede olduğunu bilme özelliğidir. İzleme, çeşitli şekillerde kaybedilebilir ve izleme için kullanılan yönteme göre değişiklik gösterir. Örneğin, mikrofonlu kulaklıktaki veya telefondaki kameralar cihazı takip etmek için kullanılırsa ışığın az olduğu, hiç olmadığı ya da kameraların üstü kapalı olduğu durumlarda cihazın yerini belirleme yeteneğini kaybedebilir.
Gizlilik nedeniyle pozun engellenmesine örnek olarak, kulaklık izin istemi gibi bir güvenlik iletişim kutusu gösteriyorsa tarayıcı bu sırada uygulamaya poz sağlamayı durdurabilir. Ancak sistem kurtarılabilirse kare döngüsü devam etsin diye XRSession.requestAnimationFrame()
numaralı telefonu aradım. Aksi takdirde kullanıcı aracısı oturumu sonlandırır ve end
etkinlik işleyicisini çağırır.
Kısa bir ara
Bir sonraki adım için oturum oluşturma sırasında oluşturulan nesneler gerekir. Bir kanvas oluşturduğumu ve canvas.getContext()
çağrısını yaparak XR uyumlu bir Web GL oluşturma bağlamı oluşturmasını istediğimi hatırlayın. Tüm çizimler WebGL API, WebGL2 API veya Three.js gibi WebGL tabanlı bir çerçeve kullanılarak yapılır. Bu bağlam, yeni bir XRWebGLLayer
örneğiyle birlikte updateRenderState()
aracılığıyla oturum nesnesine aktarıldı.
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)
});
WebGLFramebuffer'ı iletme ("bağlama")
XRWebGLLayer
, WebXR ile kullanılmak üzere özel olarak sağlanan ve oluşturma bağlamlarının varsayılan çerçeve önbelleğini değiştiren WebGLRenderingContext
için bir çerçeve önbelleği sağlar. WebGL dilinde buna "bağlama" denir.
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
}
}
Her XRView nesnesini iterasyonla gezme
Pozisyonu aldıktan ve çerçeve önbelleğini bağladıktan sonra görüntüleme alanlarını almanız gerekir. XRViewerPose
, her biri bir ekranın bir ekranını veya bir kısmını temsil eden bir XRView arayüzleri dizisi içerir. Bu veriler, cihaz ve izleyici için doğru şekilde konumlandırılmış içerikleri oluşturmak için gereken bilgileri (ör. görüş alanı, göz ofseti ve diğer optik özellikler) içerir.
İki göz için çizim yaptığımdan iki görünümüm var. Bu görünümleri döngüden geçirip her biri için ayrı bir resim çiziyorum.
Telefon tabanlı artırılmış gerçeklik için uygulama yaparken yalnızca bir görünümüm olur ancak yine de döngü kullanırım. Tek bir görünümde yineleme yapmak anlamsız görünse de, farklı etkileyici deneyimler için tek bir oluşturma yolunuz olmasını sağlar. Bu, WebXR ile diğer sürükleyici sistemler arasındaki önemli bir farktır.
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
}
}
}
XRViewport nesnesini WebGLRenderingContext'e aktarma
XRView
nesnesi, ekranda gözlemlenebilir olan öğeleri ifade eder. Ancak bu görünümde çizim yapmak için cihazıma özgü koordinatlara ve boyutlara ihtiyacım var. Görüntü çerçevesi gibi, bunları XRWebGLLayer
'ten isteyip WebGLRenderingContext
'a iletirim.
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
Bu makaleyi yazarken webGLRenContext
nesnesinin adlandırması konusunda birkaç iş arkadaşımla tartışma yaşadım. Örnek komut dosyaları ve çoğu WebXR kodu, bu değişkeni gl
olarak adlandırır. Sana gönderdiğim örnekleri anlamaya çalışırken gl
ne anlama geliyordu? Bunun WebGLRenderingContext
örneği olduğunu öğrenirken size hatırlatmak için webGLRenContext
olarak adlandırdım.
Bunun nedeni, gl
kullanıldığında yöntem adlarının derlenmiş dillerde VR oluşturmak için kullanılan OpenGL ES 2.0 API'sindeki benzerlerine benzemesidir. OpenGL kullanarak VR uygulamaları yazdıysanız bu durum açıktır ancak bu teknolojiye tamamen yeniyseniz kafa karıştırıcı olabilir.
Çerçeve arabelleğine bir öğe çizin
Çok azimliyseniz doğrudan WebGL'yi kullanabilirsiniz, ama bunu önermem. En üstte listelenen çerçevelerden birini kullanmak çok daha kolaydır.
Sonuç
WebXR güncellemeleri veya makaleleri burada sona ermiyor. WebXR'ın tüm arayüzleri ve üyelerine dair referansları MDN'de bulabilirsiniz. Arayüzlerde yapılacak yenilikler için Chrome Durumu'nda ilgili özellikleri takip edin.
Fotoğraf: JESHOOTS.COM, Unsplash'te