Wirtualne sesje artystyczne

Szczegóły sesji artystycznej

Podsumowanie

Sześcioro artystów zostało zaproszonych do malowania, projektowania i rzeźby w rzeczywistości wirtualnej. To jest rejestrowania sesji, konwertowania danych i przedstawiania w czasie rzeczywistym w przeglądarkach.

https://g.co/VirtualArtSessions

Co za wspaniałe życie! Dzięki wprowadzeniu rzeczywistości wirtualnej jako konsument usług, odkrywane są nowe i nieodkryte jeszcze możliwości. przechyl pędzel, Produkt Google dostępny w telefonie HTC Vive umożliwia rysowanie w przestrzeni wymiarowej. Gdy po raz pierwszy wypróbowaliśmy Bullet Brush, za pomocą kontrolerów ze śledzeniem ruchu. pokój z supermocami” jest z Tobą. nie jest łatwo możliwość rysowania w pustej przestrzeni dookoła.

Wirtualne dzieło

Zespół Google ds. sztuki danych dostał wyzwanie polegające na zaprezentowaniu tego użytkownikom bez gogli VR – w internecie, które jeszcze nie działają. W tym celu zespół zatrudnił rzeźbiarza, ilustratora, projektant koncepcyjny, artysta mody, instalacja i artyści uliczni tworzyć dzieła sztuki we własnym stylu w ramach tego nowego medium.

Nagrywanie rysunków w rzeczywistości wirtualnej

To oprogramowanie wbudowane w Unity to komputerowa aplikacja, korzysta z rzeczywistości wirtualnej w skali pomieszczenia do monitorowania pozycji głowy (wyświetlacz montowany na głowie lub HMD) i kontrolery w obu rękach. Grafika stworzona w aplikacji Bullet Brush stworzyła użytkowniczka domyślnie wyeksportowane jako plik .tilt. Aby udostępnić tę funkcję w internecie, zdaliśmy sobie sprawę, że potrzebujemy więcej niż tylko danych dzieł sztuki. Ściśle współpracowaliśmy z Zespół til Brush zmodyfikuje przechyl Brush, aby wyeksportować działania cofania/usuwania ponieważ głowa i ręka wykonawcy unoszą się 90 razy na sekundę.

Podczas rysowania przechyl Brush ustawia pozycję i kąt kontrolera oraz wiele punktów w czasie, tworząc „kreskę”. Oto przykład: tutaj. napisaliśmy wtyczki, które wyodrębniły te pociągi i wyświetliliśmy je w postaci nieprzetworzonego kodu JSON.

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

Powyższy fragment określa format formatu JSON sketch.

W tym przypadku każde pociągnięcie jest zapisywane jako działanie o nazwie „STROKE”. Oprócz działania po udarze mózgu, chcieliśmy pokazać wykonawcę, który popełnia błędy i zmienia swój w środku szkicu, dlatego tak ważne było zapisanie polecenia „DELETE”. działań, które pełnią funkcję albo wykasować lub cofnąć działania dla całego pociągnięcia.

Podstawowe informacje o każdym pociągnięciu są zapisywane, więc typ, rozmiar i kolor pędzla są zbierane dane RGB.

Na koniec zapisywane są wszystkie wierzchołki kreski wraz z położeniem, kąta, czasu, a także siły nacisku wyzwalania przez kontroler (oznaczana jako p) w każdym punkcie).

Pamiętaj, że rotacja składa się z 4 komponentów. Jest to ważne później, gdy renderujemy pociągnięcia, aby uniknąć zablokowania gimbala.

Odtwarzanie szkiców przy użyciu WebGL

Aby wyświetlić szkice w przeglądarce, użyliśmy THREE.js i napisać kod do generowania geometrii, który naśladuje z wykorzystaniem funkcji Tip Brush.

Bullet Brush tworzy trójkątne paski w czasie rzeczywistym na podstawie dłoni użytkownika ruchu, cały szkic jest już „ukończony” przed udostępnieniem w internecie. Dzięki temu możemy ominąć znaczną część obliczeń i pieczenia w czasie rzeczywistym. geometrię po wczytaniu.

Szkice WebGL

Każda para wierzchołków w kresce tworzy wektor kierunkowy (niebieskie linie) przez połączenie poszczególnych punktów, jak pokazano powyżej, moveVector we fragmencie kodu poniżej). Każdy punkt zawiera również orientację, kwartionon reprezentujący pod obecnym kątem kontrolera. Aby utworzyć trójkątny pasek, te punkty wytwarzają normalne, które są prostopadłe do kierunku, orientacji kontrolera.

Proces obliczania pasa trójkąta dla każdego pociągnięcia jest prawie taki sam do kodu używanego w Bullet Brush:

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

Ponowne połączenie kierunku i orientacji powoduje powrót niejednoznaczne wyniki matematyczne; może istnieć wiele norm skutkują niespodziewanym zakrętem. w geometrii.

Podczas iteracji na punktach kreski zachowujemy „preferowane prawe” i przekazać go do funkcji computeSurfaceFrame(). Ta funkcja daje nam normalną wartość, z której można wywnioskować czworokąty z czworokąta, na podstawie kierunek linii (od ostatniego do bieżącego punktu) oraz układ kontrolera (kwartionon). Co ważniejsze, funkcja ta zwraca nowe „preferowane prawo”, wektor dla następnego zbioru obliczeń.

Pociągnięcia

Po wygenerowaniu czworokątów na podstawie punktów kontrolnych każdego pociągnięcia łączamy wartości czworokąty przez interpolację ich narożników od jednego czworokąta do drugiego.

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 );
}
. Scalone czworokąty
Scalone czworokąty.

Każda czworokąt zawiera także promieniowanie UV, które jest generowane w kolejnym kroku. Niektóre pędzle zawierają różne wzory kresek, dając wrażenie, że każde pociągnięcie brzmiało jak inny pociągnięcie pędzla. Jest to możliwe dzięki użyciu _atlasa tekstur, _gdzie każda tekstura pędzla zawiera wszystkie możliwe odmian. Prawidłową teksturę wybiera się przez zmianę wartości UV udar mózgu.

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

    });

}
. Cztery tekstury w atlasie tekstury do pędzla olejnego
4 tekstury w atlasie tekstury do pędzla olejnego
. W Bullet Brush
W przechylaczku
. W WebGL.
W WebGL

Ponieważ każdy szkic ma nieograniczoną liczbę kresek, nie będą one musiały zmodyfikowane w czasie wykonywania, wstępnie obliczamy geometrię kreski z wyprzedzeniem i scalamy je w jedną siatkę. Chociaż każdy nowy typ pędzla musi być osobny co pozwala ograniczyć liczbę wywołań rysowania do jednego na pędzel.

Cały powyższy szkic jest wykonywany podczas jednego rysowania w WebGL.
Cały powyższy szkic jest wykonywany podczas jednego rysowania w WebGL

Aby przetestować system w warunkach skrajnych, stworzyliśmy szkic, którego wypełnienie zajęło 20 minut z jak największą liczbą wierzchołków. Powstały szkic był nadal odtwarzany 60 kl./s w WebGL.

Ponieważ każdy z pierwotnych wierzchołków udaru również zawierał czas, możemy bez trudu odtworzyć dane. Ponowne obliczenie ruchów na klatkę Powoli, więc wstępnie obliczyliśmy cały szkic podczas wczytywania i po prostu ujawniliśmy jak nigdy dotąd.

Ukrycie czworokąta oznaczało zwijanie jego wierzchołków do 0,0,0 punktu. Gdy czas osiągnął punkt, w którym czworokąt ma zostać ujawniony, położenie wierzchołków z powrotem.

Obszarem do poprawy jest całkowite manipulowanie wierzchołkami procesora graficznego za pomocą programy do cieniowania. Obecna implementacja umieszcza je w pętli przez wierzchołek. tablica z bieżącej sygnatury czasowej, sprawdzając, które wierzchołki muszą zostać ujawnione a następnie aktualizować geometrię. Bardzo obciąża to procesor, wentylator się obraca, a bateria traci żywotność.

Wirtualne dzieło

Nagrywanie wykonawców

Uznaliśmy, że same szkice to za mało. Chcieliśmy pokazać w swoich szkicach i malują każdy pociąg pędzlem.

Aby uchwycić wykonawców, skorzystaliśmy z aparatów Microsoft Kinect, dzięki którym zarejestrowaliśmy głębię obrazu. danych wykonawców w kosmosie. Dzięki temu możemy pokazać trójwymiarowe figury w tej samej przestrzeni, w której pojawiają się rysunki.

Ponieważ ciało wykonawcy zasłonięłoby siebie, nie można było zobaczyć Użyliśmy podwójnego systemu Kinect, po obu stronach pokoju, w stronę środkową.

Oprócz informacji o głębi, zarejestrowaliśmy również informacje o kolorach standardowych lustrzanki cyfrowej. Użyliśmy doskonałych oprogramowanie DepthKit do kalibracji i scalania; czyli obraz z kamery do głębi i kolorowych kamer. Kinect może koloru nagrywania, ale zdecydowaliśmy się używać lustrzanek cyfrowych, bo możemy ustawienia ekspozycji, używać pięknych, wysokiej klasy obiektywów i nagrywać w wysokiej rozdzielczości.

Aby nagrać materiał, zbudowaliśmy specjalny pokój, w którym mieszka HTC Vive, i aparatem. Wszystkie powierzchnie były pokryte materiałem pochłaniającym podczerwień aby uzyskać czystszą chmurę punktów (powłoka na ścianach, prążkowane na podłodze). Gdy materiał pojawił się w chmurze punktów Wybraliśmy czarny materiał, aby nie rozpraszał i był biały.

Wykonawca nagrywający

Nagrane w ten sposób nagrania wideo dały nam wystarczającą ilość informacji, aby prognozować cząstki. systemu. Opisaliśmy dodatkowe narzędzia w openFrameworks, aby doprecyzować materiał, zwłaszcza podłogi, ściany i sufit.

Wszystkie 4 kanały zarejestrowanej sesji wideo (2 kanały kolorów powyżej i 2 kanały)
głębokość poniżej)
Wszystkie 4 kanały w nagranej sesji wideo (dwa kanały kolorów powyżej i dwa) poniżej)

Chcieliśmy nie tylko pokazać wykonawców, ale też wyświetlić zasoby HMD i z kontrolerami w 3D. Było to ważne nie tylko ze względu na pokazanie HMD końcowy efekt był wyraźnie widoczny (odblaski soczewek w HTC Vive w telefonie odczytów podczerwieni Kinect), dano nam punkty kontaktu przy debugowaniu cząstek. i wyrównanie filmów ze szkicem.

Wyświetlacz, kontrolery i cząstki ułożone na głowie
Wyświetlacz na głowie, kontrolery i cząsteczki obok siebie

Trzeba było napisać niestandardową wtyczkę do przechyla Brush, która wyodrębniła położenie HMD i kontrolerów w każdej ramce. Bullet Brush działa z prędkością 90 kl./s, strumieniowano tony danych, a dane wejściowe szkicu wzrosły do 20 MB bez kompresji. Zastosowaliśmy tę technikę również do rejestrowania zdarzeń, które nie są rejestrowane. w typowym pliku z informacjami o narzędziu til Brush, na przykład wtedy, gdy wykonawca wybierze opcję, na panelu narzędzi i pozycji widżetu lustrzanego.

Podczas przetwarzania 4 TB zebranych danych jednym z największych wyzwań było z wykorzystaniem różnych źródeł wizualnych/danych. Każdy film z lustrzanki cyfrowej muszą być wyrównane z odpowiednim sygnałem Kinect, zarówno przestrzeń, jak i czas. Później materiał z tych dwóch kamer musiał być powiązane ze sobą, tworząc jednego wykonawcę. Następnie musieliśmy dopasować z danymi ze swoich rysunków. Uff... Stworzyliśmy dokument w oparciu o przeglądarkę pomocne w większości tych zadań. Możesz je wypróbować samodzielnie. tutaj

Gracze

Po wyrównaniu danych wykorzystaliśmy do ich przetworzenia kilka skryptów napisanych w Node.js i wygenerować plik wideo oraz serię plików JSON, . Aby zmniejszyć rozmiar pliku, zrobiliśmy 3 czynności. Po pierwsze, obniżyliśmy dokładność każdej liczby zmiennoprzecinkowej do maksymalnie 3 z precyzją ułamka dziesiętnego. Następnie zmniejszamy liczbę punktów o jedną trzecią, 30 kl./s oraz interpolację położenia po stronie klienta. Na koniec zserializowaliśmy danych, więc zamiast zwykłego kodu JSON z parami klucz-wartość, kolejność wartości jest utworzony na potrzeby pozycjonowania i obracania HMD oraz kontrolerów. Spowoduje to wycięcie pliku minimalny rozmiar to 3 MB, co było akceptowalne w połączeniu z przewodem.

Wykonawcy nagrywający

Sam film jest wyświetlany jako element wideo HTML5 odczytywany przez Tekstura WebGL zmienia się w cząstki, a sam film potrzebny do odtworzenia w tle. Cieniowanie konwertuje kolory na zdjęciach głębi na pozycje Przestrzeń 3D. James George podał doskonały przykład jak wykorzystać materiał bezpośrednio z DepthKit.

W systemie iOS obowiązują ograniczenia dotyczące odtwarzania filmów w treści. Zakładamy, że ma to na celu zapobieganie automatycznie się odtwarzają. Zastosowaliśmy metodę podobnie jak w przypadku innych rozwiązań alternatywnych w internecie, czyli kopiowaniu i ręcznie ustawić czas przewijania – co 1/30 ani sekundy.

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 >= ( 1 / 30 ) ){
        videoElement.currentTime = videoElement.currentTime + elapsed;
        lastTime = time;
    }

    }

}

frameLoop.add( loopCanvas );

Nasze podejście miało nieszczęśliwy efekt uboczny polegający na znacznej obniżeniu iOS. liczby klatek na sekundę, bo kopiowanie bufora pikseli z filmu do obszaru roboczego jest bardzo bardzo obciążające procesor. Aby go obejść, zamieściliśmy po prostu mniejsze wersje przeglądarki te same filmy, które mają co najmniej 30 kl./s na iPhonie 6.

Podsumowanie

Zgodnie z ogólną konsensusem w 2016 r. nad rozwojem oprogramowania do rzeczywistości wirtualnej elementy geometryczne i cieniowanie pozwalają na pracę z prędkością 90+ kl./s w przypadku procesorów HMD. Ten okazało się świetnym celem w demonstracjach WebGL, ponieważ wykorzystywane techniki z mapą przechyl pędzla do WebGL.

Chociaż przeglądarki wyświetlające złożone sieci 3D nie są ekscytujące, Był to dowód koncepcyjny łączący pracę w rzeczywistości wirtualnej w internecie jest całkowicie możliwe.