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

Çerçeve döngüsü hakkında her şey

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 isteme, girme ve sonlandırma talimatlarını da sağladım.

Bu makalede, içeriğin ekrana tekrar tekrar çizildiği, kullanıcı aracısı tarafından kontrol edilen sonsuz döngü olan çerçeve döngüsü açıklanmaktadır. İçerik, kare adı verilen ayrı bloklar halinde çizilir. Karelerin sıralanması hareket yanılsaması yaratır.

Bu makalede yer almayan konular

WebXR uygulamasında kare döngüsü sırasında içerik oluşturmanın tek yolu WebGL ve WebGL2'dir. Neyse ki birçok çerçeve, WebGL ve WebGL2'nin üzerinde bir soyutlama katmanı sağlar. three.js, babylonjs ve PlayCanvas bu tür çerçeveler arasındadır. A-Frame ve React 360 ise WebXR ile etkileşim için tasarlanmıştır.

Bu makalede, Immersive Web Working Group'un Immersive VR Session örneği (demo, kaynak) kullanılarak bir kare döngüsünün temelleri açıklanmaktadır. WebGL veya çerçevelerden birini ayrıntılı olarak incelemek istiyorsanız internette giderek büyüyen bir kaynak listesi bulabilirsiniz.

Oyuncular ve oyun

Çerçeve döngüsünü anlamaya çalışırken ayrıntılarda kayboluyordum. Oyunda birçok nesne var ve bazıları yalnızca diğer nesnelerdeki referans özellikleriyle adlandırılıyor. İşleri kolaylaştırmak için "oyuncular" olarak adlandırdığım nesneleri açıklayacağım. Ardından, etkileşimlerini açıklayacağım. Bu etkileşime "oyun" adını veriyorum.

Oyuncular

XRViewerPose

Poz, bir şeyin 3D uzaydaki konumu ve yönüdür. Hem izleyicilerin hem de giriş cihazlarının pozu vardır ancak burada ilgilendiğimiz izleyicinin pozudur. Hem izleyici hem de giriş cihazı pozları, konumunu vektör olarak ve yönünü başlangıca göre kuaterniyon olarak açıklayan bir transform özelliğine sahiptir. Kaynak, XRSession.requestReferenceSpace() çağrılırken istenen referans alanı türüne göre belirtilir.

Referans alanlarını açıklamak biraz zaman alır. Bu konuları Artırılmış gerçeklik başlıklı bölümde ayrıntılı olarak ele alıyorum. Bu makalenin temelini oluşturan örnekte 'local' referans alanı kullanılıyor. Bu, başlangıç noktasının, oturum oluşturma sırasında izleyicinin bulunduğu konum olduğu ve iyi tanımlanmış bir zemin olmadığı anlamına geliyor. Bu nedenle, kesin konumu platforma göre değişebilir.

XRView

Bir görünüm, sanal sahneyi görüntüleyen bir kameraya karşılık gelir. Görünümde, konumunu vektör ve yön olarak açıklayan bir transform özelliği de bulunur. Bunlar hem vektör/kuaterniyon çifti hem de eşdeğer matris olarak sağlanır. Kodunuza en uygun olan gösterimi kullanabilirsiniz. Her görüntüleme, bir cihazın görüntüleyene görüntü sunmak için kullandığı bir ekrana veya ekranın bir bölümüne karşılık gelir. XRView nesnesi, XRViewerPose nesnesinden bir dizide döndürülür. Dizideki görünüm sayısı değişir. Mobil cihazlarda AR sahneleri, cihaz ekranını kaplayabilen veya kaplamayan tek bir görünüme sahiptir. Başlıklar genellikle her göz için bir olmak üzere iki görünüme sahiptir.

XRWebGLLayer

Katmanlar, bit eşlem resimlerinin kaynağını ve bu resimlerin cihazda nasıl oluşturulacağını açıklar. Bu açıklama, oynatıcının işlevini tam olarak yansıtmıyor. Bunu bir cihaz ile WebGLRenderingContext arasındaki aracı olarak görmeye başladım. MDN de benzer bir görüşü benimseyerek ikisi arasında "bir bağlantı sağladığını" belirtir. Bu nedenle, diğer oyunculara erişim sağlar.

Genel olarak WebGL nesneleri, 2D ve 3D grafiklerin oluşturulması için durum bilgilerini depolar.

WebGLFramebuffer

Bir çerçeve arabelleği, WebGLRenderingContext için görüntü verileri sağlar. XRWebGLLayer öğesinden aldıktan sonra geçerli WebGLRenderingContext öğesine iletirsiniz. bindFramebuffer() numaralı telefonu aramak dışında (bu konuyla ilgili daha fazla bilgiyi sonraki bölümlerde bulabilirsiniz) bu nesneye doğrudan erişemezsiniz. Bu değeri yalnızca XRWebGLLayer öğesinden WebGLRenderingContext'e aktarırsınız.

XRViewport

Görüntü alanı, WebGLFramebuffer içindeki dikdörtgen bir bölgenin koordinatlarını ve boyutlarını sağlar.

WebGLRenderingContext

Çizim bağlamı, tuval için programatik bir erişim noktasıdır (üzerine çizim yaptığımız alan). 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, birinciden ikincisine aktarılır.

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

Oyun

Oyuncuların kim olduğunu öğrendiğimize göre şimdi de 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 kare döngüsünün parçası olduğunu unutmayın. VR uygulamalarında 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.

Çerçeve döngüsünün temel süreci şu şekildedir:

  1. XRSession.requestAnimationFrame() Hizmetleri İçin Arayın. Buna karşılık kullanıcı aracısı, sizin tanımladığınız XRFrameRequestCallback işlevini ç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 öğesinden WebGLRenderingContext öğesine aktarın.
    4. Her XRView nesnesini yineleyin, XRWebGLLayer öğesinden XRViewport özelliğini alın ve WebGLRenderingContext öğesine iletin.
    5. Çerçeve arabelleğine bir şeyler çizin.

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

İzleyicinin pozunu alma

Söylemeye gerek var mı bilmiyoruz. 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ü, XRViewerPose nesnesi tarafından sağlanır. Mevcut animasyon karesinde XRFrame.getViewerPose() çağırarak izleyicinin pozunu alıyorum. Oturumu ayarlarken edindiğim referans alanını iletiyorum. Bu nesnenin döndürdüğü değerler her zaman geçerli oturuma girdiğimde istediğim referans alanına göre belirlenir. Hatırlayacağınız gibi, pozu 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 başı veya telefon kamerası anlamına gelir. Poz, uygulamanıza izleyicinin nerede olduğunu bildirir. Gerçek görüntü oluşturma işleminde, birazdan bahsedeceğim XRView nesneleri kullanılır.

Devam etmeden önce, sistem izlemeyi kaybetmesi veya gizlilik nedeniyle pozu engellemesi durumunda izleyici pozunun döndürülüp döndürülmediğini test ediyorum. Takip, XR cihazın ve giriş cihazlarının çevreye göre nerede olduğunu bilme özelliğidir. İzleme, çeşitli şekillerde kaybolabilir ve izleme için kullanılan yönteme bağlı olarak değişir. Örneğin, cihazı takip etmek için kulaklıktaki veya telefondaki kameralar kullanılıyorsa cihaz, az ışıklı veya ışıksız ortamlarda ya da kameralar kapalıyken bulunduğu yeri belirleme özelliğini kaybedebilir.

Pozun gizlilik nedeniyle engellenmesine örnek olarak, başlıkta izin istemi gibi bir güvenlik iletişim kutusu gösteriliyorsa bu sırada tarayıcının uygulamaya poz sağlamayı durdurması verilebilir. Ancak sistem kurtarılabilirse çerçeve döngüsünün devam etmesi için XRSession.requestAnimationFrame()'ı aradım. Aksi takdirde kullanıcı aracısı oturumu sonlandırır ve end etkinlik işleyicisini çağırır.

Kısa bir tali yol

Bir sonraki adımda, oturum kurulumu sırasında oluşturulan nesneler gerekir. Bir tuval oluşturduğumu ve canvas.getContext() işlevini çağırarak elde ettiğim 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 öğesinin yeni bir örneğinin yanı sıra updateRenderState() ile 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 (bağlayın)

XRWebGLLayer, özellikle WebXR ile kullanılmak üzere sağlanan ve oluşturma bağlamlarının varsayılan çerçeve arabelleğini değiştiren WebGLRenderingContext için bir çerçeve arabelleği sağlar. Bu işleme WebGL dilinde "bağlama" adı verilir.

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 yineleyin.

Pozu aldıktan ve çerçeve arabelleğini bağladıktan sonra görünüm alanlarını almanız gerekir. XRViewerPose, her biri bir ekranı veya ekranın bir bölümünü temsil eden bir XRView arayüzleri dizisi içerir. Bunlar, içeriğin cihaz ve izleyici için doğru şekilde konumlandırılmış olarak oluşturulması 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. Bunları döngüye alıp 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 olurdu ancak yine de döngü kullanırdım. Tek bir görünümde yineleme yapmak anlamsız gibi görünse de bu sayede bir dizi sürükleyici deneyim için tek bir oluşturma yolu elde edebilirsiniz. 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 aktarın.

XRView nesnesi, ekranda görülebilenleri ifade eder. Ancak bu görünümü çizmek için cihazıma özel koordinatlara ve boyutlara ihtiyacım var. Framebuffer'da olduğu gibi, bunları XRWebGLLayer'dan isteyip WebGLRenderingContext'ye 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
    }
  }
}

The webGLRenContext

Yazarken birkaç meslektaşımla webGLRenContext nesnesinin adlandırılması konusunda tartışmıştık. Örnek komut dosyaları ve çoğu WebXR kodu bu değişkene gl adını verir. Örnekleri anlamaya çalışırken gl simgesinin neyi ifade ettiğini sürekli unutuyordum. Öğrenme sürecinde WebGLRenderingContext örneği olduğunu 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'deki 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 şeyler çizme

Kendinize çok güveniyorsanız WebGL'yi doğrudan kullanabilirsiniz ancak bunu önermem. En üstte listelenen çerçevelerden birini kullanmak çok daha basittir.

Sonuç

WebXR güncellemeleri veya makaleleri devam edecek. MDN'de WebXR'ın tüm arayüzleri ve üyeleriyle ilgili bir referans bulabilirsiniz. Arayüzlerde yapılacak iyileştirmeler için Chrome Status'taki özellikleri takip edin.