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

Kare döngüsü hakkında her şey

Joe Medley
Joe Medley

Kısa bir süre önce, WebXR Device API'nin ardındaki temel kavramları tanıtan Sanal gerçeklik web'e geliyor başlıklı bir makaleyi yayınladım. Ayrıca, bir XR oturumunu isteme, girme ve sonlandırmayla ilgili talimatları da sağladım.

Bu makalede, içeriğin sürekli olarak ekrana çizildiği, kullanıcı aracısı kontrollü bir sonsuz döngü olan çerçeve döngüsü açıklanmaktadır. İçerik, çerçeve adı verilen ayrı bloklar halinde çizilir. Karelerin arka arkaya çalışması hareket illüzyonu yaratır.

Bu makale ne değildir?

WebGL ve WebGL2, WebXR uygulamasında bir kare döngüsü sırasında içerik oluşturmak için kullanılan tek yöntemdir. Neyse ki birçok çerçeve, WebGL ve WebGL2'nin üzerine bir soyutlama katmanı sağlamaktadır. Bu çerçeveler arasında three.js, babylonjs ve PlayCanvas yer alır. A-Frame ve React 360 ise, WebXR ile etkileşim için tasarlanmıştır.

Bu makale bir WebGL veya çerçeve eğiticisi değildir. Bu kılavuzda, Immersive Web Working Group'un Immersive VR Oturumu örneği kullanılarak çerçeve döngüsünün temel bilgileri açıklanmaktadır (demo, kaynak). WebGL veya çerçevelerden birini ayrıntılı olarak incelemek isterseniz internette makalelerin sayısı giderek artmaktadır.

Oyuncular ve oyun

Kare döngüsünü anlamaya çalışırken sürekli ayrıntılarda kayboluyordum. Oyunda çok sayıda nesne vardır ve bunların bazıları yalnızca diğer nesnelerdeki referans özelliklerine göre adlandırılır. Düz bir çizgide kalmanıza yardımcı olmak için nesneleri, 'oyuncular' diyorum. Sonra, etkileşim kurduklarını açıklayacağım, buna 'oyun' adını veriyorum.

Oyuncular

XRViewerPose

Poz, bir şeyin 3D uzaydaki konumu ve yönüdür. Hem görüntüleyenlerin hem de giriş cihazlarının bir duruşu var, ancak burada asıl endişemiz görüntüleyen kişi durudur. Hem görüntüleyici hem de giriş cihazı pozisyonlarında, konumunu vektör olarak, yönünü ise kaynağa göre dörtlü olarak açıklayan bir transform özelliği bulunur. Kaynak, XRSession.requestReferenceSpace() çağrılırken istenen referans alanı türüne göre belirtilir.

Referans alanlarının açıklanması biraz zaman alabilir. Artırılmış gerçeklik'te bunları tüm ayrıntılarıyla ele alıyorum. Bu makalenin temeli olarak kullandığım örnekte 'local' referans alanı kullanılıyor. Bu, kaynağın oturum oluşturulurken izleyicinin bulunduğu konumda olduğu ve iyi tanımlanmış bir taban bulunmadığı anlamına gelir. Ayrıca, tam konumu platforma göre değişebilir.

XRView

Görünüm, sanal sahneyi görüntüleyen bir kameraya karşılık gelir. Bir görünüm, konumunu vektör olarak ve yönünü açıklayan transform özelliğine de sahiptir. Bunlar hem vektör/kuterniyon çifti hem de eşdeğer bir matris olarak sağlanır ve kodunuza en uygun olan temsile bağlı olarak her iki temsili de kullanabilirsiniz. Her görünüm, bir cihaz tarafından izleyiciye görüntü sunmak için kullanılan bir ekrana veya ekranın bir bölümüne karşılık gelir. XRViewerPose nesnesinden bir dizide XRView nesne döndürülür. Dizideki görüntüleme sayısı değişiklik gösterir. Mobil cihazlarda bir AR sahnesi, cihazın ekranını kaplayan ya da kaplamayan tek bir görünüme sahiptir. Mikrofonlu kulaklıklarda genellikle her bir göz için bir tane olmak üzere iki görünüm bulunur.

XRWebGLLayer

Katmanlar, bit eşlem resimler için bir kaynak ve bu resimlerin cihazda nasıl oluşturulacağına ilişkin açıklamalar sağlar. Bu açıklama, bu oynatıcının ne yaptığını tam olarak yansıtmıyor. Bunu, bir cihazla WebGLRenderingContext arasındaki aracı gibi düşünüyorum. MDN de hemen hemen aynı görünümü alarak ikisi arasında "bir bağlantı sağladığını" ifade eder. 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

Çerçeve arabelleği, resim verilerini WebGLRenderingContext ürününe sağlar. XRWebGLLayer öğesinden aldıktan sonra, bunu geçerli WebGLRenderingContext öğesine geçirirsiniz. bindFramebuffer() (daha sonra da bahsedilecek) işlevini çağırmak dışında, bu nesneye hiçbir zaman doğrudan erişemezsiniz. Bunu sadece XRWebGLLayer ürününden WebGLRenderingContext'e aktarmanız yeterli.

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ı, tuval (üzerinde çizdiğimiz alan) için programatik erişim noktasıdır. Bunu yapmak için hem WebGLFramebuffer hem de XRViewport gereklidir.

XRWebGLLayer ile WebGLRenderingContext arasındaki ilişkiye dikkat edin. Biri görüntüleyenin cihazına, diğeri web sayfasına karşılık gelir. WebGLFramebuffer ve XRViewport, ilkinden ikincisine aktarılır.

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

Maç

Artık oyuncuların kim olduğunu öğrendiğimize göre oynadıkları oyunlara bakalım. Bu, her karede baştan başlayan bir oyun. Karelerin, temel donanıma bağlı bir hızda gerçekleşen bir çerçeve döngüsünün parçası olduğunu unutmayın. VR uygulamalarında saniyedeki kare sayısı 60 ile 144 arasında bir değer olabilir. Android için AR, saniyede 30 kare hızında çalışır. Kodunuz herhangi bir kare hızı tahmininde bulunmamalıdır.

Çerçeve döngüsüne ilişkin temel süreç şu şekildedir:

  1. XRSession.requestAnimationFrame() Hizmetleri İçin Arayın. Buna yanıt olarak, kullanıcı aracısı sizin tarafınızdan tanımlanan XRFrameRequestCallback yöntemini çağırır.
  2. Geri çağırma işlevinizin içinde:
    1. XRSession.requestAnimationFrame() adlı kişiyi tekrar arayın.
    2. İzleyicinin pozunu çekin.
    3. WebGLFramebuffer öğesini XRWebGLLayer ile WebGLRenderingContext arasında aktarın ("bağlayın").
    4. Her XRView nesnesini yineleme yaparak XRWebGLLayer nesnesinden XRViewport öğesini alın ve WebGLRenderingContext öğesine iletin.
    5. Çerçeve arabelleğine bir şey çizin.

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

İzleyicinin duruşunu çekin

Söylemeye gerek yok. Artırılmış gerçeklik (AR) veya sanal gerçeklikte (VR) bir şey çizmek için görüntüleyenin nerede olduğunu ve nereye baktığını bilmem gerekiyor. Görüntüleyenin konumu ve yönü bir XRViewerPose nesnesi tarafından sağlanır. İzleyicinin pozunu, mevcut animasyon karesinde XRFrame.getViewerPose() çağrısı yaparak istiyorum. Bu e-postayı, oturumu oluştururken edindiğim referans alanına iletiyorum. Bu nesne tarafından döndürülen değerler her zaman geçerli oturumu girdiğimde istediğim referans alanına göre olur. Hatırlayacağınız üzere, 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. Diğer bir deyişle, akıllı telefonda görüntüleyen kişinin başı veya telefonun kamerası gösterilir. Poz, uygulamanıza görüntüleyenin 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şı izleyici pozunun geri döndürülüp döndürülmediğini test ediyorum. İzleme, XR cihazının ortama göre nerede ve/veya giriş cihazlarının nerede olduğunu bilebilmesidir. İzleme, birkaç şekilde kaybolabilir ve izleme için kullanılan yönteme bağlı olarak değişir. Örneğin, mikrofonlu kulaklıktaki veya telefondaki kameralar takip etmek için kullanılırsa cihaz, ışığın az olduğu veya hiç olmadığı durumlarda nerede olduğunu ya da kameraların üzerinin kapalı olup olmadığını belirleyemeyebilir.

Gizlilik nedeniyle pozu engellemeye örnek olarak, başlıkta izin istemi gibi bir güvenlik iletişim kutusu gösteriliyorsa tarayıcı bu işlem gerçekleşirken uygulamaya poz vermeyi durdurabilir. Ama daha önce XRSession.requestAnimationFrame() çağrısı yaptım. Böylece, sistem kurtarılabilirse çerçeve döngüsü devam eder. Aksi takdirde, kullanıcı aracısı oturumu sonlandırır ve end etkinlik işleyicisini çağırır.

Rotadan kısa bir süre tali

Bir sonraki adım, oturum kurulumu sırasında oluşturulmuş nesneleri gerektirir. Hatırlayacağınız üzere bir tuval oluşturdum ve ona XR uyumlu bir Web GL oluşturma bağlamı oluşturma talimatı verdiğimi ve bunu canvas.getContext() çağrısıyla aldığımı hatırlıyorum. Tüm çizimler WebGL API'sı, WebGL2 API'sı veya Three.js gibi WebGL tabanlı bir çerçeve kullanılarak yapılır. Bu bağlam, yeni XRWebGLLayer örneğiyle birlikte updateRenderState() aracılığıyla oturum nesnesine geçirildi.

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 öğesini geçirin ('bind')

XRWebGLLayer, özellikle WebXR ile kullanılması ve oluşturma bağlamının varsayılan çerçeve arabelleğinin değiştirilmesi amacıyla sağlanan WebGLRenderingContext için bir çerçeve arabelleği sağlar. Bu, WebGL'nin dilinde "bağlama" olarak adlandırılı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);
    // Iterate over the views
  }
}

Her XRView nesnesini yinele

Pozu aldıktan ve çerçeve arabelleğini bağladıktan sonra sıra görüntü alanlarını almaya gelir. XRViewerPose, her biri bir ekranı veya ekranın bir kısmını temsil eden bir XRView arayüz dizisi içerir. Bunlar, cihaz ve izleyici için doğru şekilde konumlandırılmış içeriği oluşturmak için gereken bilgileri (ör. görüş alanı, göz uzaklığı ve diğer optik özellikler) içerir. İki göz için çizdiğimden iki görünümüm var. Bunları döngüye alıp her biri için ayrı birer resim çiziyorum.

Telefon tabanlı artırılmış gerçeklik uygularken yalnızca bir görünüm kullanırdım ama yine de döngü kullanırdım. Tek bir görünümde yineleme yapmak anlamsız görünse de, bunu yapmak sürükleyici deneyimlerden oluşan bir yelpazesi için tek bir oluşturma yoluna sahip olmanıza olanak tanır. Bu, WebXR ile diğer kapsamlı 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 geçirme

XRView nesnesi, ekranda gözlemlenebilen öğeleri ifade eder. Ancak, bu görünümü elde edebilmek için cihazıma özgü koordinatlara ve boyutlara ihtiyacım var. Çerçeve arabelleğinde olduğu gibi, bunları da XRWebGLLayer öğesinden istiyorum ve WebGLRenderingContext'e iletiyorum.

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 birkaç iş arkadaşımla webGLRenContext nesnesinin adlandırılması konusunda bir tartışma yaşadım. Örnek komut dosyaları ve çoğu WebXR kodu basitçe bu değişkeni gl olarak adlandırır. Örnekleri anlamaya çalışırken, gl ürününün neden bahsettiğini sürekli unuttum. Bunun bir WebGLRenderingContext örneği olduğunu öğrenirken size hatırlatmak için bu etiketi 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'deki karşılıkları gibi görünmesini sağlamasıdır. Bu, VR uygulamalarını OpenGL kullanarak yazdıysanız, ancak bu teknolojide tamamen yeni olup olmadığınız konusunda kafanızı karıştıran bir gerçektir.

Çerçeve arabelleğine bir şey çizin

Çok azimliyseniz doğrudan WebGL'yi kullanabilirsiniz ancak bunu önermem. En üstte listelenen çerçevelerden birini kullanmak çok daha basittir.

Sonuç

WebXR güncellemelerinin veya makalelerinin sonu değildir. MDN'de tüm WebXR arayüzleri ve üyeleri için referans bulabilirsiniz. Arayüzlerde yapılacak geliştirmeler için Chrome Durumu'ndaki her bir özelliği takip edin.

Fotoğraf: JESHOOTS.COM Unsplash'ta