Özet
Sanal gerçeklikte resim, tasarım ve heykel yapmak için altı sanatçı davet edildi. Bu, onların oturumlarını kaydetme, verileri dönüştürme ve web tarayıcılarında gerçek zamanlı olarak sunma şeklimizi içerir.
https://g.co/VirtualArtSessions
Yaşamak için ne zaman! Sanal gerçekliğin tüketici ürünü olarak hayatımıza girmesiyle, yeni ve keşfedilmemiş olasılıklar keşfediliyor. HTC Vive'da bulunan bir Google ürünü olan Tilt Brush, üç boyutlu uzayda çizim yapmanıza olanak tanır. Tilt Brush'ı ilk kez denediğimizde, hareketleri izleyen kumandalarla çizim yapma hissi ve"süper güçlere sahip bir odada" olma hissi beraberinde duruyor. Etrafınızdaki boş alanda çizim yapabilmek gibi bir deneyim yok.
Google'daki Veri Sanatları Ekibi'ne, bu deneyimi VR başlığı olmayan, Tilt Brush'ın henüz çalışmadığı web'de ise göstermenin zor olduğu anlaşıldı. Ekip bu amaçla, bir heykeltıraş, çizgi ustası, konsept tasarımcısı, moda sanatçısı, enstalasyon sanatçısı ve sokak sanatçılarını, bu yeni ortamda kendi tarzlarında sanat eserleri yaratmaları için işe aldı.
Sanal Gerçeklikte Çizim Kaydetme
Unity'de yerleşik olarak geliştirilen Tilt Brush yazılımının kendisi, oda ölçeğinde VR'ı kullanarak başınızın konumunu (başa takılan ekran veya HMD) ve her bir elinizdeki kumandaları takip eden bir masaüstü uygulamasıdır. Tilt Brush'ta oluşturulan poster varsayılan olarak .tilt
dosyası olarak dışa aktarılır. Bu deneyimi web'e taşımak için sanat eseri verilerinden
daha fazlasına ihtiyacımız olduğunu fark ettik. Tilt Brush'ı değiştirmek için
Tilt Brush ekibiyle yakın bir çalışma yürüttük. Bu sayede, geri alma/silme işlemlerinin yanı sıra sanatçının
başı ve el konumlarını saniyede 90 kez dışa aktardık.
Tilt Brush, çizim sırasında kumandanızın konumunu ve açısını alır ve zaman içinde birden fazla noktayı "fırça"ya dönüştürür. Burada bir örnek görebilirsiniz. Bu çizgileri çıkaran ve ham JSON dosyası olarak veren eklentiler yazdık.
{
"metadata": {
"BrushIndex": [
"d229d335-c334-495a-a801-660ac8a87360"
]
},
"actions": [
{
"type": "STROKE",
"time": 12854,
"data": {
"id": 0,
"brush": 0,
"b_size": 0.081906750798225,
"color": [
0.69848710298538,
0.39136275649071,
0.211316883564
],
"points": [
[
{
"t": 12854,
"p": 0.25791856646538,
"pos": [
[
1.9832634925842,
17.915264129639,
8.6014995574951
],
[
-0.32014992833138,
0.82291424274445,
-0.41208130121231,
-0.22473378479481
]
]
}, ...many more points
]
]
}
}, ... many more actions
]
}
Yukarıdaki snippet, taslak JSON biçiminin biçimini gösterir.
Burada her çizgi, "STROKE" türüyle bir işlem olarak kaydedilir. Çizgi işlemlerine ek olarak, bir sanatçıyı çizim sırasında hata yaparken ve düşüncelerini değiştirirken göstermek istedik. Bu nedenle, bir fırçanın tamamı için silme veya geri alma işlemleri görevi gören "SİL" işlemlerini kaydetmek çok önemliydi.
Her fırçayla ilgili temel bilgiler kaydedilir. Böylece fırça türü, fırça boyutu ve renk rgb'i toplanır.
Son olarak, çizginin her bir köşe noktası kaydedilir. Bu köşe; konum, açı, zaman ve kumandanın tetikleyici basıncı gücünü (her bir nokta içinde p
olarak belirtilir) içerir.
Rotasyonun 4 bileşenli bir dörtlü olduğuna dikkat edin. Bu, daha sonra gimbal kilitlerini önlemek için fırçaları dışarı oluşturduğumuzda önemlidir.
Skeçleri WebGL ile Oynatma
Çizimleri bir web tarayıcısında göstermek için THREE.js kitaplığını kullandık ve Tilt Brush'ın arka planda yaptığını taklit eden geometri oluşturma kodu yazdık.
Tilt Brush kullanıcının el hareketine göre gerçek zamanlı olarak üçgen şeritler oluştururken çizimin tamamı, biz web'de gösterdiğimiz zamana kadar "tamamlanmış" durumda. Bu, gerçek zamanlı hesaplamanın büyük bir kısmını atlamamıza ve yükte geometriyi oluşturmamıza olanak tanır.
Bir çizgideki her bir köşe çifti, bir yön vektörü (yukarıda gösterildiği gibi her bir noktayı birbirine bağlayan mavi çizgiler, aşağıdaki kod snippet'inde moveVector
) oluşturur.
Her noktanın bir yönü de bulunur. Bu dörtgen, denetleyicinin geçerli açısını temsil eder. Bir üçgen şerit oluşturmak için, bu noktaların her biri üzerinde yineleme yaparak yöne ve denetleyici yönüne dik normaller oluştururuz.
Her bir çizginin üçgen şeridini hesaplama işlemi, Tilt Brush'ta kullanılan kodla neredeyse aynıdır:
const V_UP = new THREE.Vector3( 0, 1, 0 );
const V_FORWARD = new THREE.Vector3( 0, 0, 1 );
function computeSurfaceFrame( previousRight, moveVector, orientation ){
const pointerF = V_FORWARD.clone().applyQuaternion( orientation );
const pointerU = V_UP.clone().applyQuaternion( orientation );
const crossF = pointerF.clone().cross( moveVector );
const crossU = pointerU.clone().cross( moveVector );
const right1 = inDirectionOf( previousRight, crossF );
const right2 = inDirectionOf( previousRight, crossU );
right2.multiplyScalar( Math.abs( pointerF.dot( moveVector ) ) );
const newRight = ( right1.clone().add( right2 ) ).normalize();
const normal = moveVector.clone().cross( newRight );
return { newRight, normal };
}
function inDirectionOf( desired, v ){
return v.dot( desired ) >= 0 ? v.clone() : v.clone().multiplyScalar(-1);
}
Fırça yönü ve yönü kendi başlarına birleştirildiğinde matematiksel olarak belirsiz sonuçlar ortaya çıkar. Birden çok normal türetilebilir ve bunlar genellikle geometride bir "büküm"e neden olur.
Bir çizginin noktaları üzerinde yineleme yaparken "tercih edilen sağ" vektörü korur ve bunu computeSurfaceFrame()
fonksiyonuna iletiriz. Bu fonksiyon, çizginin doğru yönüne (son noktadan şu anki noktaya) ve denetleyicinin yönüne (bir dördün) bağlı olarak, dörtlü şeritte bir dörtlü elde edebileceğimiz bir normallik verir. Daha da önemlisi, bir sonraki hesaplama grubu için yeni bir "tercih edilen sağ" vektörü de döndürür.
Her vuruşun kontrol noktalarına göre dörtlüler oluşturduktan sonra, köşelerini bir dörtlüden diğerine doğru interpolasyon yaparak birleştiririz.
function fuseQuads( lastVerts, nextVerts) {
const vTopPos = lastVerts[1].clone().add( nextVerts[0] ).multiplyScalar( 0.5
);
const vBottomPos = lastVerts[5].clone().add( nextVerts[2] ).multiplyScalar(
0.5 );
lastVerts[1].copy( vTopPos );
lastVerts[4].copy( vTopPos );
lastVerts[5].copy( vBottomPos );
nextVerts[0].copy( vTopPos );
nextVerts[2].copy( vBottomPos );
nextVerts[3].copy( vBottomPos );
}
Her dörtlü, bir sonraki adım olarak üretilen UV'ler de içerir. Bazı fırçalarda, her fırça darbesinin farklı bir boya fırçası gibi göründüğü izlenimi vermek için çeşitli fırça darbeleri kullanılır. Bu, her fırça dokusunun olası tüm varyasyonları içerdiği _doku atlama kullanılarak gerçekleştirilir. Fırçanın UV değerleri değiştirilerek doğru doku seçilir.
function updateUVsForSegment( quadVerts, quadUVs, quadLengths, useAtlas,
atlasIndex ) {
let fYStart = 0.0;
let fYEnd = 1.0;
if( useAtlas ){
const fYWidth = 1.0 / TEXTURES_IN_ATLAS;
fYStart = fYWidth * atlasIndex;
fYEnd = fYWidth * (atlasIndex + 1.0);
}
//get length of current segment
const totalLength = quadLengths.reduce( function( total, length ){
return total + length;
}, 0 );
//then, run back through the last segment and update our UVs
let currentLength = 0.0;
quadUVs.forEach( function( uvs, index ){
const segmentLength = quadLengths[ index ];
const fXStart = currentLength / totalLength;
const fXEnd = ( currentLength + segmentLength ) / totalLength;
currentLength += segmentLength;
uvs[ 0 ].set( fXStart, fYStart );
uvs[ 1 ].set( fXEnd, fYStart );
uvs[ 2 ].set( fXStart, fYEnd );
uvs[ 3 ].set( fXStart, fYEnd );
uvs[ 4 ].set( fXEnd, fYStart );
uvs[ 5 ].set( fXEnd, fYEnd );
});
}
Her çizimin fırça sayısı sınırsız olduğundan ve fırçaların çalışma zamanında değiştirilmesine gerek kalmayacağından, çizgi geometrisini önceden hesaplar ve bunları tek bir ağda birleştiririz. Her yeni fırça türünün kendi malzemesi olsa da, çizim çağrımızı fırça başına bir tane olacak şekilde azaltır.
Sisteme stres testi uygulamak için alanı mümkün olduğunca çok köşeyle doldurmak üzere 20 dakika süren bir eskiz oluşturduk. Ortaya çıkan eskiz, WebGL'de 60 fps'de oynatılmaya devam eder.
Bir çizginin orijinal köşelerinin her biri zaman da içerdiğinden, verileri kolayca oynatabiliriz. Kare başına çizgi sayısını yeniden hesaplamak çok yavaş olacaktır. Bu nedenle, yük üzerinde çizimin tamamını önceden hesapladık ve gerektiğinde her dörtlüyü tek tek ortaya çıkardık.
Dörtlüyü gizlemek, köşelerinin 0,0,0 noktasına daraltılması anlamına geliyordu. Dörtgenin görüneceği zamana geldiğinde köşeleri tekrar yerine yerleştiririz.
İyileştirilecek bir alan, köşelerin tamamen GPU'da gölgelendiricilerle değiştirilmesidir. Mevcut uygulama, bunları geçerli zaman damgasına göre köşe dizisinde döngüye alarak, hangi köşe noktalarının gösterilmesi gerektiğini kontrol edip ardından geometriyi güncelleyerek bunları yerleştirir. Bu da CPU'ya çok fazla yük bindirerek fanın dönmesine ve pil ömrünün boşa gitmesine neden olur.
Sanatçıları Kaydetme
Çizimlerin tek başına yeterli olmayacağını düşündük. Sanatçılara her fırça darbesini boyayıp eskizlerinin içini göstermek istedik.
Sanatçıları çekmek için Microsoft Kinect kameraları kullanarak sanatçıların uzayda vücudunun derinlik verilerini kaydettik. Bu bize, üç boyutlu şekillerin çizimlerin göründüğü yerde gösterilmesini mümkün kılıyor.
Sanatçının vücudu kendini gizleyerek arkasında ne olduğunu görmemizi engelleyeceğinden ikisi de odanın karşıt tarafında merkezi gösteren bir çift Kinect sistemi kullandık.
Derinlik bilgilerine ek olarak, standart DSLR kameralarla sahnenin renk bilgilerini de yakaladık. Derinlik kamerası ile renkli kamera görüntülerini kalibre etmek ve birleştirmek için mükemmel DepthKit yazılımını kullandık. Kinect renk kaydı yapabiliyor ama pozlama ayarlarını kontrol edebildiğimiz, kaliteli ve güzel lensler kullanabildiğimiz ve yüksek çözünürlüklü kayıt yapabildiğimiz için DSLR'leri kullanmayı tercih ettik.
Görüntüleri kaydetmek için HTC Vive'a, sanatçıya ve kameraya ait özel bir oda oluşturduk. Tüm yüzeyler, bize daha temiz bir nokta bulutu (duvarlarda yumuşak, yerde ise kauçuk kaplama) sağlamak için kızılötesi ışığı emen malzemeyle kaplandı. Nokta bulut çekiminde malzemenin görünmesi ihtimaline karşı, beyaz bir şey kadar dikkat dağıtıcı olmaması için siyah malzemeyi seçtik.
Ortaya çıkan video kayıtları, bir parçacık sistemini yansıtmamız için bize yeterli bilgi sağladı. Görüntüleri daha da netleştirmek, özellikle de zeminleri, duvarları ve tavanı kaldırmak için openFrameworks'te bazı ek araçlar hazırladık.
Sanatçıları göstermenin yanı sıra HMD'yi ve denetleyicileri 3D olarak oluşturmak istedik. Bu, yalnızca HMD'nin nihai çıktıda açıkça gösterilmesi açısından önemliydi (HTC Vive'ın yansıtıcı lensleri Kinect'in IR ölçümlerini sapmaktaydı), ayrıca parçacık çıkışını ayıklamak ve videoları eskizle hizalamak için bize temas noktaları sağladı.
Bu işlem, Tilt Brush'a HMD'nin ve her karenin denetleyicilerinin konumlarını çıkaran özel bir eklenti yazılarak yapıldı. Tilt Brush 90 fps'de çalıştığı için tonlarca veri akışı oluyor ve bir çizimin giriş verisi sıkıştırılmamış olarak 20 MB'tan fazla çıkıyordu. Bu tekniği, sanatçının araç panelinde bir seçenek belirlemesi ve ayna widget'ının konumu gibi tipik Tilt Brush kaydetme dosyasında kaydedilmeyen etkinlikleri yakalamak için de kullandık.
Topladığımız 4 TB'lık veriyi işlerken karşılaşılan en büyük zorluklardan biri tüm farklı görsel/veri kaynaklarını uyumlu hale getirmekti. Piksellerin hem uzayda hem de zaman içinde hizalanması için bir DSLR kameradan alınan her videonun karşılık gelen Kinect ile hizalanması gerekir. Daha sonra, bu iki kamera düzeneğinden alınan görüntülerin tek bir sanatçı oluşturmak için birbirine hizalanması gerekiyordu. Sonra, 3D sanatçımızı, çizimlerinden alınan verilerle uyumlu hale getirmemiz gerekiyordu. Bora Bu görevlerin çoğuna yardımcı olması için tarayıcı tabanlı araçlar yazdık. Bunları buradan kendiniz deneyebilirsiniz.