Sesi Seni Virtual

Detail Sesi Seni

Ringkasan

Enam seniman diundang untuk melukis, mendesain, dan memahat di VR. Ini adalah proses yang digunakan untuk merekam sesi, mengonversi data, dan menyajikannya secara real-time dengan browser web.

https://g.co/VirtualArtSessions

Sungguh waktu yang luar biasa! Dengan diperkenalkannya {i>virtual reality<i} sebagai produk konsumen, berbagai kemungkinan baru dan belum tereksplorasi bisa ditemukan. Tilt Brush, produk Google yang tersedia di HTC Vive, memungkinkan Anda menggambar dalam ruang tiga dimensi. Saat kami mencoba Kuas Virtual untuk pertama kalinya, perasaan menggambar dengan pengontrol yang dilacak gerakan ditambah dengan kehadiran "di dalam ruangan dengan kekuatan super" tetap ada bersama Anda; sebenarnya tidak ada pengalaman yang setara dengan menggambar di ruang kosong di sekitar Anda.

Karya seni virtual

Tim Data Arts di Google diberi tantangan untuk menampilkan pengalaman ini kepada mereka yang tidak menggunakan headset VR, di web tempat Tilt Brush belum beroperasi. Untuk itu, tim membawa pematung, ilustrator, desainer konsep, seniman mode, seniman instalasi, dan seniman jalanan untuk membuat karya seni dengan gaya mereka sendiri dalam media baru ini.

Merekam Gambar dalam Virtual Reality

Dibangun di Unity, software Tilt Brush itu sendiri adalah aplikasi desktop yang menggunakan VR berskala ruangan untuk melacak posisi kepala (head mount display, atau HMD) dan pengontrol di setiap tangan Anda. Poster yang dibuat di Kuas Virtual secara default diekspor sebagai file .tilt. Untuk menghadirkan pengalaman ini ke web, kami menyadari bahwa kami membutuhkan lebih dari sekadar data karya seni. Kami bekerja sama dengan tim Tilt Brush untuk memodifikasi Tilt Brush sehingga dapat mengekspor tindakan urungkan/hapus serta posisi kepala dan tangan artis 90 kali per detik.

Saat menggambar, Kuas Virtual mengambil posisi dan sudut pengontrol Anda serta mengonversi beberapa titik dari waktu ke waktu menjadi "goresan". Anda dapat melihat contohnya di sini. Kami menulis plugin yang mengekstrak goresan ini dan menghasilkannya sebagai JSON mentah.

    {
      "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
      ]
    }

Cuplikan di atas menguraikan format format JSON sketsa.

Di sini, setiap goresan disimpan sebagai tindakan, dengan jenis: "STROKE". Selain tindakan goresan, kami ingin menunjukkan artis yang melakukan kesalahan dan mengubah pikiran di tengah sketsa, jadi sangat penting untuk menyimpan tindakan "DELETE" yang berfungsi sebagai menghapus atau mengurungkan tindakan untuk seluruh goresan.

Informasi dasar untuk setiap goresan disimpan, sehingga jenis kuas, ukuran kuas, warna RGB semuanya dikumpulkan.

Terakhir, setiap verteks goresan disimpan dan mencakup posisi, sudut, waktu, serta kekuatan tekanan pemicu pengontrol (dicatat sebagai p dalam setiap titik).

Perhatikan bahwa rotasi adalah angka empat komponen. Hal ini penting untuk dilakukan saat kita merender goresan untuk menghindari penguncian gimbal.

Memutar Sketsa Kembali dengan WebGL

Untuk menampilkan sketsa di browser web, kami menggunakan THREE.js dan menulis kode pembuatan geometri yang meniru fungsi Kuas Virtual.

Meskipun Kuas Virtual menghasilkan strip segitiga secara real time berdasarkan gerakan tangan pengguna, keseluruhan sketsa sudah "selesai" pada saat kami menampilkannya di web. Dengan demikian, kita dapat mengabaikan banyak perhitungan real-time dan mencatat geometri saat dimuat.

Sketsa WebGL

Setiap pasangan verteks dalam goresan menghasilkan vektor arah (garis biru yang menghubungkan setiap titik seperti yang ditunjukkan di atas, moveVector dalam cuplikan kode di bawah). Setiap titik juga berisi orientasi, yaitu kuaternion yang mewakili sudut pengontrol saat ini. Untuk membuat strip segitiga, kami melakukan iterasi pada setiap titik ini sehingga menghasilkan nilai normal yang tegak lurus dengan arah dan orientasi pengontrol.

Proses penghitungan strip segitiga untuk setiap goresan hampir identik dengan kode yang digunakan di Kuas Virtual:

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);
}

Menggabungkan arah goresan dan orientasi goresan akan menampilkan hasil yang ambigu secara matematis; mungkin ada beberapa normal yang diperoleh dan sering kali akan menghasilkan "putaran" dalam geometri.

Saat melakukan iterasi pada titik-titik goresan, kita akan mempertahankan vektor "kanan pilihan" dan meneruskannya ke fungsi computeSurfaceFrame(). Fungsi ini memberi kita nilai normal untuk mendapatkan quad pada strip quad, berdasarkan arah goresan (dari titik terakhir ke titik saat ini), dan orientasi pengontrol (kuarternion). Yang lebih penting, fungsi ini juga menampilkan vektor "pilihan kanan" baru untuk set komputasi berikutnya.

Garis luar

Setelah menghasilkan segiempat berdasarkan titik kontrol setiap goresan, kita menggabungkan segiempat dengan menginterpolasi sudutnya, dari satu sisi ke sisi berikutnya.

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 );
}
Paha depan menyatu
Kuadran menyatu.

Setiap segi empat juga berisi UV yang dihasilkan sebagai langkah berikutnya. Beberapa kuas berisi berbagai pola goresan untuk memberikan kesan bahwa setiap goresan terasa seperti goresan kuas yang berbeda. Hal ini dilakukan menggunakan _texture atlasing, _di mana setiap tekstur kuas berisi semua kemungkinan variasi. Tekstur yang benar dipilih dengan mengubah nilai UV goresan.

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 );

    });

}
Empat tekstur di atlas tekstur untuk kuas minyak
Empat tekstur di atlas tekstur untuk kuas minyak
Di Kuas Virtual
Di Kuas Virtual
Di WebGL
Di WebGL

Karena setiap sketsa memiliki jumlah goresan yang tidak terbatas, dan goresan tidak perlu dimodifikasi selama runtime, kita akan menghitung geometri goresan terlebih dahulu dan menggabungkannya menjadi satu mesh tunggal. Meskipun setiap jenis kuas baru harus berupa materinya sendiri, hal tersebut tetap mengurangi panggilan gambar menjadi satu per kuas.

Seluruh sketsa di atas dilakukan dalam satu panggilan gambar di WebGL
Seluruh sketsa di atas dilakukan dalam satu panggilan gambar di WebGL

Untuk menguji daya tahan sistem, kami membuat sketsa yang memerlukan waktu 20 menit untuk mengisi ruang dengan sebanyak mungkin titik sudut. Sketsa yang dihasilkan masih diputar pada 60 fps di WebGL.

Karena setiap verteks asli goresan juga berisi waktu, kita dapat dengan mudah memutar data. Menghitung ulang goresan per frame akan sangat lambat. Jadi, kami terlebih dahulu menghitung seluruh sketsa saat pemuatan dan hanya menampilkan setiap segi empat saat tiba waktunya untuk melakukannya.

Menyembunyikan quad berarti menciutkan verteksnya ke titik 0,0,0. Ketika waktu telah mencapai titik saat quad seharusnya diperlihatkan, kita memosisikan ulang verteks kembali ke tempatnya.

Area yang perlu ditingkatkan adalah memanipulasi verteks sepenuhnya pada GPU dengan shader. Implementasi saat ini menempatkannya dengan melakukan loop pada array verteks dari stempel waktu saat ini, memeriksa verteks mana yang perlu ditampilkan, lalu memperbarui geometri. Hal ini akan menimbulkan banyak beban pada CPU yang menyebabkan kipas berputar serta menghabiskan masa pakai baterai.

Karya seni virtual

Merekam Artis

Kami merasa bahwa sketsa itu sendiri tidak akan cukup. Kami ingin menunjukkan kepada para seniman di dalam sketsa, melukis setiap sapuan kuas.

Untuk mengambil gambar artis, kami menggunakan kamera Microsoft Kinect untuk merekam data mendalam tubuh artis di ruang angkasa. Hal ini memberi kita kemampuan untuk menampilkan bentuk tiga dimensi dalam ruang yang sama dengan munculnya gambar.

Karena tubuh artis akan menutupi dirinya sendiri sehingga mencegah kami melihat apa yang ada di belakangnya, kami menggunakan sistem Kinect ganda, keduanya di sisi yang berlawanan dari ruangan yang menunjuk ke tengah.

Selain informasi depth, kami juga mengambil informasi warna dari adegan dengan kamera DSLR standar. Kami menggunakan software DepthKit yang luar biasa untuk mengkalibrasi dan menggabungkan rekaman dari kamera kedalaman dan kamera warna. Kinect mampu merekam warna, tetapi kami memilih untuk menggunakan DSLR karena kami dapat mengontrol setelan eksposur, menggunakan lensa canggih yang indah, dan merekam dalam definisi tinggi.

Untuk merekam rekaman, kami membangun ruangan khusus untuk menampung HTC Vive, seniman, dan kamera. Semua permukaan ditutupi dengan bahan yang menyerap cahaya inframerah untuk memberi kami titik awan yang lebih bersih (bantalan di dinding, matras karet berus di lantai). Jika materi tersebut muncul di rekaman awan titik, kami memilih bahan hitam sehingga tidak akan mengganggu sesuatu yang berwarna putih.

Artis rekaman

Rekaman video yang dihasilkan memberi kami informasi yang cukup untuk memproyeksikan sistem partikel. Kami menulis beberapa alat tambahan di openFrameworks untuk membersihkan rekaman lebih lanjut, terutama melepaskan lantai, dinding, dan plafon.

Keempat saluran sesi video yang direkam (dua saluran warna di atas dan dua
kedalaman di bawah)
Keempat saluran sesi video yang direkam (dua saluran warna di atas dan dua saluran depth di bawah)

Selain menampilkan para seniman, kami juga ingin merender HMD dan pengontrol dalam 3D. Hal ini tidak hanya penting untuk menunjukkan HMD dalam output akhir dengan jelas (lensa reflektif HTC Vive mengurangi pembacaan IR Kiinect), tetapi juga memberi kami titik kontak untuk men-debug output partikel dan menyelaraskan video dengan sketsa.

Layar yang dipasang di kepala, pengontrol, dan partikel yang berjajar
Layar yang dipasang di kepala, pengontrol, dan partikel yang berjajar

Hal ini dilakukan dengan menulis plugin kustom ke dalam Tilt Brush yang mengekstrak posisi HMD dan pengontrol setiap frame. Karena Tilt Brush berjalan pada 90fps, banyak data mengalir keluar dan data input sketsa lebih dari 20 MB tidak dikompresi. Kami juga menggunakan teknik ini untuk menangkap peristiwa yang tidak direkam dalam file simpan Kuas Virtual standar, seperti saat artis memilih opsi di panel alat dan posisi widget pencerminan.

Dalam memproses 4 TB data yang kami kumpulkan, salah satu tantangan terbesar adalah menyelaraskan semua sumber visual/data yang berbeda. Setiap video dari kamera DSLR harus disejajarkan dengan Kinect yang sesuai, sehingga piksel sejajar dalam ruang serta waktu. Lalu, rekaman dari kedua rig kamera ini perlu diselaraskan satu sama lain untuk membentuk satu artis. Kemudian kita perlu menyelaraskan seniman 3D dengan data yang diambil dari gambar. Fiuh! Kami telah menulis alat berbasis browser untuk membantu menyelesaikan sebagian besar tugas ini, dan Anda dapat mencobanya sendiri di sini

Artis rekaman
Setelah data diselaraskan, kami menggunakan beberapa skrip yang ditulis dalam NodeJS untuk memproses semuanya dan menghasilkan file video serta serangkaian file JSON, yang semuanya dipangkas dan disinkronkan. Untuk mengurangi ukuran file, kami melakukan tiga hal. Pertama, kami mengurangi akurasi setiap bilangan floating point sehingga nilainya berada pada presisi maksimum 3 desimal. Kedua, kita memotong jumlah titik sepertiga menjadi 30 fps, dan menginterpolasi posisi di sisi klien. Terakhir, kami melakukan serialisasi data. Jadi, alih-alih menggunakan JSON biasa dengan key-value pair, urutan nilai dibuat untuk posisi dan rotasi HMD dan pengontrol. Hal ini mengurangi ukuran file menjadi hanya sekitar 3 MB yang dapat diterima untuk dikirimkan melalui kabel.
Artis rekaman

Karena video itu sendiri ditayangkan sebagai elemen video HTML5 yang dibaca oleh tekstur WebGL untuk menjadi partikel, video itu sendiri perlu diputar secara tersembunyi di latar belakang. Shader mengonversi warna pada gambar kedalaman menjadi posisi di ruang 3D. James George telah membagikan contoh yang bagus tentang bagaimana Anda dapat menggunakan rekaman langsung dari DepthKit.

iOS memiliki pembatasan pada pemutaran video inline, yang kami asumsikan untuk mencegah pengguna diganggu oleh iklan video web yang diputar otomatis. Kami menggunakan teknik yang mirip dengan solusi lain di web, yaitu menyalin frame video ke kanvas dan memperbarui waktu pencarian video secara manual, setiap 1/30 detik.

videoElement.addEventListener( 'timeupdate', function(){
    videoCanvas.paintFrame( videoElement );
});

function loopCanvas(){

    if( videoElement.readyState === videoElement.HAVE\_ENOUGH\_DATA ){

    const time = Date.now();
    const elapsed = ( time - lastTime ) / 1000;

    if( videoState.playing && elapsed &gt;= ( 1 / 30 ) ){
        videoElement.currentTime = videoElement.currentTime + elapsed;
        lastTime = time;
    }

    }

}

frameLoop.add( loopCanvas );

Pendekatan kami memiliki efek samping yang tidak menguntungkan, yaitu menurunkan kecepatan frame iOS secara signifikan karena penyalinan buffer piksel dari video ke kanvas sangat menguras CPU. Untuk menyiasatinya, kami menyajikan versi video yang sama berukuran lebih kecil yang memungkinkan setidaknya 30 fps di iPhone 6.

Kesimpulan

Konsensus umum untuk pengembangan software VR mulai tahun 2016 adalah menyederhanakan geometri dan shader agar Anda dapat berjalan pada 90+ fps di HMD. Ini ternyata menjadi target yang sangat bagus untuk demo WebGL karena teknik yang digunakan di peta Kuas Virtual dengan sangat baik ke WebGL.

Meskipun browser web yang menampilkan mesh 3D kompleks tidaklah menarik, hal ini merupakan bukti konsep bahwa penyerbukan silang pekerjaan VR dan web sepenuhnya mungkin dilakukan.