Sanal gerçeklik web'e geliyor, 2. bölüm

Kare döngüsü hakkında

Joe Medley
Joe Medley

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.

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. three.js, babylonjs ve PlayCanvas bu tür çerçevelere örnek gösterilebilir. A-Frame ve React 360 ise WebXR ile etkileşim kurmak için tasarlanmıştır.

Bu makale, 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'yi veya çerçevelerden birini incelemek istiyorsanız internette giderek büyüyen bir makale listesi bulabilirsiniz.

Oyuncular ve oyun

Çerçeve döngüsünü anlamaya çalışırken ayrıntılar arasında kayboluyordum. Çok sayıda nesne vardır ve bunların bazıları yalnızca diğer nesnelerdeki referans özelliklerine göre adlandırılı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. Bu konuları Artırılmış gerçeklik bölümünde ayrıntılı olarak ele alı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ıklarda genellikle her göz için iki görünüm bulunur.

XRWebGLLayer

Katmanlar, bitmap resimleri ve bu resimlerin cihazda nasıl oluşturulacağına dair 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 ve bu iki öğe arasında "bağlantı sağladığını" belirtiyor. Bu nedenle, diğer oyunculara erişim sağlar.

Genel olarak WebGL nesneleri, 2D ve 3D grafikleri oluşturmayla ilgili durum bilgilerini depolar.

WebGLFramebuffer

Görüntü ara belleği, WebGLRenderingContext'e görüntü verileri sağlar. XRWebGLLayer'ten aldıktan sonra mevcut WebGLRenderingContext'a iletmeniz 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, öncekinden sonrakine aktarılır.

XRWebGLLayer ile WebGLRenderingContext arasındaki ilişki
XRWebGLLayer ile WebGLRenderingContext arasındaki ilişki

Oyun

Oyuncuların kim olduğunu öğrendiğimize göre, oynadıkları oyuna bakalım. Her karede baştan başlayan bir oyundur. 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:

  1. XRSession.requestAnimationFrame() Hizmetleri İçin Arayın. Kullanıcı aracısı, yanıt olarak sizin tanımladığınız XRFrameRequestCallback öğesini çağırır.
  2. Geri çağırma işlevinizin içinde:
    1. XRSession.requestAnimationFrame() numaralı telefonu tekrar arayın.
    2. İzleyicinin pozunu alın.
    3. WebGLFramebuffer öğesini XRWebGLLayer'dan WebGLRenderingContext'ye iletin ("bağlayın").
    4. Her XRView nesnesini iterleyerek XRViewport değerini XRWebGLLayer'den alıp WebGLRenderingContext'a aktarın.
    5. Görüntü çerçevesine bir şey çizin.

1. ve 2a. adımlar önceki makalede ele alındığından 2b. adımdan başlayacağım.

İzleyicinin pozunu alın

Bunu söylemeye gerek yok. Artırılmış gerçeklik veya sanal gerçeklikte bir şey çizmek için izleyicinin nerede olduğunu ve nereye baktığını bilmem gerekir. İ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öredir. Hatırlayacağınız gibi, poz isterken mevcut referans alanını iletmem 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 temsil eden bir izleyici pozu vardır. Bu, izleyicinin kafası veya akıllı telefonlarda telefon kamerası anlamına gelir. Poz, uygulamanıza izleyicinin nerede olduğunu bildirir. Gerçek resim oluşturma işleminde XRView nesneleri kullanılır. Bu konuyu birazdan ele alacağım.

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, kulaklıkta veya telefonda bulunan kameralar takip için kullanılıyorsa cihaz, ışık seviyesi düşük veya hiç ışık olmayan durumlarda ya da kameralar kapalıyken nerede olduğunu belirleme özelliğ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, XRWebGLLayer örneğiyle birlikte updateRenderState() aracılığıyla oturum nesnesine iletildi.

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'ı iletin ("bind")

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 nesnesi üzerinde iterasyon yapın

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ı veya ekranın bir bölümünü temsil eden bir XRView arayüzü dizisi içerir. Bu veriler, cihaz ve izleyici için doğru şekilde konumlandırılmış içerik 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ığım için 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 iterasyon yapmanın anlamsız görünmesi mümkün olsa da bu işlem, çeşitli sürükleyici deneyimler için tek bir oluşturma yoluna sahip olmanızı 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 çağı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.

Görüntü arabelleği üzerine bir şey çizme

Gerçekten iddialıysanız doğrudan WebGL'yi kullanabilirsiniz ancak bunu önermiyorum. 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.

Unsplash'ta JESHOOTS.COM tarafından çekilen fotoğraf