वर्चुअल आर्ट सेशन

आर्ट सेशन की जानकारी

खास जानकारी

छह कलाकारों को वीआर में पेंट करने, डिज़ाइन करने, और मूर्ति बनाने के लिए बुलाया गया था. इस प्रोसेस से यह पता चलता है कि हमने उनके सेशन कैसे रिकॉर्ड किए, किस तरह डेटा को बदला, और उसे वेब ब्राउज़र पर रीयल-टाइम में कैसे पेश किया.

https://g.co/VirtualArtSessions

ज़िंदा रहने का कितना समय है! उपभोक्ता प्रॉडक्ट के रूप में वर्चुअल रिएलिटी के आने से, नई और अनसुलझी संभावनाओं की तलाश की जा रही है. HTC Vive पर उपलब्ध Google का एक प्रॉडक्ट Android Brush, आपको 3D में जगह बनाने में मदद करता है. जब हमने पहली बार til Brush की कोशिश की, तो मोशन-ट्रैक वाले कंट्रोलर के साथ ड्रॉ करने का ऐसा एहसास जो "सुपर-पावर वाले कमरे में" आपके साथ बना हुआ था; अपने चारों ओर के खाली जगह में ड्रॉइंग कर पाने जैसा कोई अनुभव नहीं है.

वर्चुअल आर्ट पीस

Google की डेटा आर्ट्स टीम के सामने यह चुनौती पेश की गई थी कि जिन लोगों के पास वीआर हेडसेट नहीं है उन्हें यह अनुभव, वेब पर कहां दिखाया जाए, जहां टील ब्रश अभी तक काम नहीं करता है. इसके लिए, टीम ने मूर्तिकार, इलस्ट्रेटर, कॉन्सेप्ट डिज़ाइनर, फ़ैशन आर्टिस्ट, इंस्टॉलेशन कलाकार, और स्ट्रीट आर्टिस्ट को इस नए मीडियम में अपनी स्टाइल में आर्टवर्क बनाने के लिए बुलाया.

वर्चुअल रिएलिटी में ड्रॉइंग रिकॉर्ड करना

Unity में बना til Brush सॉफ़्टवेयर, अपने-आप में एक डेस्कटॉप ऐप्लिकेशन है. यह आपके सिर की पोज़िशन (हेड माउंटेड डिसप्ले या एचएमडी) और आपके हर एक हाथ के कंट्रोलर को ट्रैक करने के लिए रूम-स्केल वीआर का इस्तेमाल करता है. til Brush में बनाया गया आर्टवर्क, डिफ़ॉल्ट रूप से .tilt फ़ाइल के तौर पर एक्सपोर्ट होता है. इस अनुभव को वेब पर उपलब्ध कराने के लिए, हमें एहसास हुआ कि हमें सिर्फ़ आर्टवर्क डेटा के अलावा भी दूसरी चीज़ की ज़रूरत है. हमने tiff Brush की टीम के साथ मिलकर काम किया, ताकि 'पहले जैसा करें' या 'मिटाएं' कार्रवाइयों के साथ-साथ कलाकार के सिर और हाथ की पोज़िशन को एक सेकंड में 90 गुना कम या ज़्यादा बार एक्सपोर्ट किया जा सके.

ड्रॉइंग करते समय, til Brush आपके कंट्रोलर की स्थिति और ऐंगल और समय के साथ एक से ज़्यादा पॉइंट को "स्ट्रोक" में बदल देता है. इसका उदाहरण यहां देखा जा सकता है. हमने ऐसे प्लगिन लिखे हैं जिन्होंने इन स्ट्रोक को एक्सट्रैक्ट किया है और उन्हें रॉ 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
      ]
    }

ऊपर दिया गया स्निपेट, स्केच JSON फ़ॉर्मैट के फ़ॉर्मैट के बारे में बताता है.

यहां, हर स्ट्रोक को कार्रवाई के तौर पर सेव किया गया है और उसका टाइप "STROKE" है. स्ट्रोक की कार्रवाइयों के अलावा, हम चाहते थे कि कलाकार को गलतियां करते हुए दिखाया जाए और अपने सोच को बदलने के बारे में जाना पड़े. इसलिए, "DELETE" से जुड़ी कार्रवाइयों को सेव करना ज़रूरी था जो किसी पूरे स्ट्रोक के लिए, मिटाने या पहले जैसा करने वाली कार्रवाइयों के तौर पर काम करती हैं.

हर स्ट्रोक की बुनियादी जानकारी सेव की जाती है, इसलिए ब्रश का टाइप, ब्रश का साइज़, रंग आरजीबी की सारी जानकारी इकट्ठा की जाती है.

आखिर में, स्ट्रोक के हर वर्टेक्स को सेव कर लिया जाता है और इसमें पोज़िशन, ऐंगल, समय के साथ-साथ कंट्रोलर के ट्रिगर प्रेशर स्ट्रेंथ (हर पॉइंट के अंदर p के तौर पर नहीं) शामिल होती हैं.

ध्यान दें कि रोटेशन एक 4-कॉम्पोनेंट क्वेटर्नियन है. यह बाद में तब ज़रूरी होता है, जब हम जिंबल लॉक से बचने के लिए स्ट्रोक को रेंडर करते हैं.

WebGL की मदद से बैक स्केच चलाना

एक वेब ब्राउज़र में स्केच दिखाने के लिए, हमने THREE.js का इस्तेमाल किया और ज्यामिति जनरेट करने वाला कोड लिखा. यह कोड वैसा ही है, जैसा कि Android Brush के अंदर होता है.

till Brush उपयोगकर्ता के हाथ के मूवमेंट के आधार पर रीयल टाइम में त्रिभुज वाली पट्टियां बनाता है. जब तक हम उसे वेब पर दिखाते हैं, तब तक उसके पूरे स्केच को "खत्म" कर दिया जाता है. इससे हम रीयल-टाइम में कैलकुलेट करने के तरीकों को बायपास कर पाते हैं और ज्यामिति को लोड करने में मदद कर पाते हैं.

WebGL स्केच

एक स्ट्रोक में मौजूद शीर्षों का हर जोड़ा एक दिशा वेक्टर बनाता है (जैसा कि ऊपर दिखाए गए तरीके से हर पॉइंट को जोड़ने वाली नीली रेखाएं हैं, नीचे दिए गए कोड स्निपेट में moveVector). हर पॉइंट में एक ओरिएंटेशन भी होता है. यह एक क्क्वाटर्नियन होता है, जो कंट्रोलर के मौजूदा ऐंगल को दिखाता है. एक त्रिभुज पट्टी बनाने के लिए, हम इन सभी बिंदुओं पर दोहराव करते हैं और उनके आधार पर स्टैंडर्ड तैयार करते हैं, जो दिशा और कंट्रोलर ओरिएंटेशन के लंबवत होते हैं.

हर स्ट्रोक के लिए ट्रायएंगल स्ट्रिप की गणना करने की प्रक्रिया करीब-करीब उस कोड से मिलती-जुलती है, जो रखने ब्रश में इस्तेमाल किया गया है:

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

स्ट्रोक की दिशा और स्क्रीन की दिशा अपने-आप मिलाने पर, गणित के नज़रिए से साफ़ तौर पर नतीजे मिलते हैं. कई मानक हो सकते हैं और ज्यामिति में अक्सर "ट्विस्ट" हो सकता है.

किसी स्ट्रोक के पॉइंट पर दोहराते समय, हम "पसंदीदा राइट" वेक्टर बनाए रखते हैं और इसे फ़ंक्शन computeSurfaceFrame() में पास करते हैं. इस फ़ंक्शन से हमें एक सामान्य मिलता है, जिससे हम स्ट्रोक (आखिरी बिंदु से मौजूदा बिंदु तक) की दिशा और कंट्रोलर के ओरिएंटेशन (क्वॉडर्नियन) के आधार पर, क्वाड स्ट्रिप में एक क्वाड हासिल कर सकते हैं. इससे भी अहम बात यह है कि यह कंप्यूटेशन के अगले सेट के लिए, एक नया "पसंदीदा राइट" वेक्टर भी दिखाता है.

स्ट्रोक

हर स्ट्रोक के कंट्रोल पॉइंट के आधार पर क्वाड जनरेट करने के बाद, हम उनके कोनों को एक क्वाड से अगले क्वाड तक इंटरपोलेट करके, क्वाड को फ़्यूज़ करते हैं.

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 );
}
फ़्यूज़्ड क्वाड
फ़्यूज़्ड क्वाड.

हर क्वाड में यूवी भी होते हैं, जो अगले चरण के तौर पर जनरेट होते हैं. कुछ ब्रश में कई तरह के स्ट्रोक पैटर्न होते हैं, जिनसे ऐसा लगता है कि हर स्ट्रोक ने पेंट ब्रश के अलग स्ट्रोक की तरह महसूस किया है. ऐसा करने के लिए _texture एटलसिंग का इस्तेमाल किया जाता है. यहां हर ब्रश टेक्सचर में सभी संभावित वैरिएशन मौजूद होते हैं. स्ट्रोक के यूवी वैल्यू में बदलाव करके सही टेक्सचर चुना जाता है.

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

    });

}
ऑयल ब्रश के लिए, टेक्सचर एटलस में चार टेक्सचर
ऑयल ब्रश के लिए टेक्सचर एटलस में चार टेक्सचर
टिल्ट ब्रश में
इन टिल्ट ब्रश
WebGL में
WebGL में

हर स्केच में सीमा से ज़्यादा स्ट्रोक होते हैं और स्ट्रोक को रन-टाइम में बदलने की ज़रूरत नहीं होती. स्ट्रोक की ज्यामिति समय से पहले ही कर लेते हैं और उन्हें एक मेश में मर्ज कर देते हैं. हर नए ब्रश टाइप की अपनी सामग्री होनी चाहिए, लेकिन इससे अब भी हर ब्रश के लिए एक ड्रॉ कॉल कम हो जाता है.

ऊपर दिए गए पूरे स्केच को WebGL में एक ड्रॉ कॉल में परफ़ॉर्म किया गया है
ऊपर दिए गए पूरे स्केच को WebGL में एक ड्रॉ कॉल में परफ़ॉर्म किया गया है

सिस्टम की जांच करने के लिए, हमने एक स्केच बनाया. इसमें 20 मिनट लगे, जिसमें जगह को जितने चाहें उतने वर्टेक्स भरने लगे. हमारा स्केच अब भी WebGL में 60fps (फ़्रेम प्रति सेकंड) पर खेला जाता है.

किसी स्ट्रोक के हर मूल वर्टेक्स में समय भी होता है, इसलिए डेटा को आसानी से देखा जा सकता है. हर फ़्रेम के हिसाब से स्ट्रोक की फिर से गणना करना बहुत धीमा होगा. इसलिए, हमने लोड पर पूरे स्केच की पहले से गणना कर ली है और समय आने पर हमने हर क्वाड को इसके बारे में बताया.

एक क्वाड को छिपाने का मतलब बस उसके शीर्षों को 0,0,0 पॉइंट तक घुसना होता है. जब क्वाड उस पॉइंट पर पहुंच जाता है जहां क्वाड को ज़ाहिर करना है, तो हम वर्टेक्स को वापस अपनी जगह पर रखते हैं.

बेहतर बनाने की एक कोशिश है, जीपीयू के पूरे वर्टेक्स को शेडर की मदद से बदलना. मौजूदा इंप्लिमेंटेशन उन्हें मौजूदा टाइमस्टैंप से वर्टेक्स कलेक्शन से लूप करके, यह जांच करता है कि किन वर्टेक्स को ज़ाहिर करना है. इसके बाद, ज्यामिति को अपडेट किया जाता है. इससे सीपीयू पर बहुत ज़्यादा लोड पड़ता है, जिससे पंखा घूमता है, और बैटरी भी खराब हो जाती है.

वर्चुअल आर्ट पीस

कलाकारों को रिकॉर्ड करना

हमें लगा कि स्केच ही काफ़ी नहीं हैं. हम कलाकारों को उनके स्केच में दिखाना चाहते थे और हर ब्रशस्ट्रोक पेंटिंग को बनाना चाहते थे.

कलाकारों को कैप्चर करने के लिए, हमने अंतरिक्ष में मौजूद कलाकारों के शरीर का डेप्थ डेटा रिकॉर्ड करने के लिए Microsoft Kinect का इस्तेमाल किया. इससे हमें उनके त्रिविमीय आकृतियों को उसी जगह पर दिखाने की क्षमता मिलती है जहां ड्रॉइंग दिखाई देती हैं.

कलाकार का शरीर खुद को ढक जाता था, जिससे हमें उसके पीछे की वजह नहीं दिखाई देती. इसलिए, हमने डबल Kinect सिस्टम का इस्तेमाल किया, जो कमरे के दोनों तरफ़ सेंटर की ओर इशारा करता था.

गहराई की जानकारी के अलावा, हमने मानक DSLR कैमरों से दृश्य के रंग की जानकारी भी कैप्चर की. डेप्थ कैमरे और कलर कैमरों के फ़ुटेज को कैलिब्रेट और मर्ज करने के लिए, हमने बेहतरीन DepthKit सॉफ़्टवेयर का इस्तेमाल किया. The Kinect में रंग को रिकॉर्ड करने की क्षमता है, लेकिन हमने डीएसएलआर का इस्तेमाल किया, क्योंकि हम एक्सपोज़र की सेटिंग कंट्रोल कर सकते थे, बेहतरीन हाई-एंड लेंस इस्तेमाल कर सकते थे, और हाई डेफ़िनिशन में रिकॉर्ड कर सकते थे.

फ़ुटेज रिकॉर्ड करने के लिए, हमने HTC Vive, कलाकार, और कैमरा को रखने के लिए एक खास कमरा बनाया है. सभी प्लैटफ़ॉर्म ऐसी चीज़ों से ढके थे जो इन्फ़्रारेड रोशनी को सोखते थे. इससे हमें साफ़ पॉइंट क्लाउड (दीवारों पर डुवेटाइन, फ़र्श पर रबड़ से बने रबड़) मिलते थे. अगर पॉइंट क्लाउड फ़ुटेज में कॉन्टेंट दिखता है, तो हमने ब्लैक मटीरियल को चुना, ताकि वह सफ़ेद रंग की चीज़ जितना विचलित न करे.

रिकॉर्डिंग कलाकार

नतीजे में मिली वीडियो रिकॉर्डिंग से हमें पार्टिकल सिस्टम को प्रोजेक्ट करने के लिए ज़रूरी जानकारी मिली. हमने फ़ुटेज को और साफ़ करने के लिए, openFrameworks में कुछ और टूल लिखे हैं. इसमें फ़्लोर, दीवारों, और छत को हटाना शामिल है.

किसी रिकॉर्ड किए गए वीडियो सेशन के सभी चार चैनल (ऊपर दो कलर चैनल और नीचे दो कलर चैनल)
रिकॉर्ड किए गए किसी वीडियो सेशन के सभी चार चैनल (दो कलर चैनल ऊपर और दो डेप्थ नीचे)

कलाकारों को दिखाने के अलावा, हम HMD और नियंत्रकों को 3D में भी रेंडर करना चाहते थे. यह आखिरी आउटपुट में HMD को साफ़ तौर पर दिखाने के लिए ही ज़रूरी नहीं था (HTC Vive के रिफ़्लेक्टिव लेंस की वजह से Kinect के आईआर रीडिंग का असर दिख रहा था), इससे हमें पार्टिकल के आउटपुट को डीबग करने और स्केच के साथ वीडियो को लाइन-अप करने के लिए संपर्क करने का मौका मिला.

सिर पर माउंट किया गया डिसप्ले, कंट्रोलर, और पार्टिकल लाइन से
हेड माउंट किया गया डिसप्ले, कंट्रोलर, और पार्टिकल लाइन से जुड़े हुए हैं

ऐसा til Brush में एक कस्टम प्लगिन लिखकर किया गया, जिसने हर फ़्रेम के लिए HMD और कंट्रोलर की पोज़ीशन को निकाला. Tail Brush 90 FPS (फ़्रेम प्रति सेकंड) पर चलता है. इसलिए, बहुत सारा डेटा स्ट्रीम किया गया और एक स्केच का इनपुट डेटा 20 एमबी से ज़्यादा कंप्रेस्ड था. हमने इस तकनीक का इस्तेमाल उन इवेंट को कैप्चर करने के लिए भी किया है जो सामान्य till Brush की सेव की गई फ़ाइल में रिकॉर्ड नहीं किए जाते. जैसे, जब कलाकार टूल पैनल पर कोई विकल्प चुनता है और मिरर विजेट की जगह बताता है.

हमने कैप्चर किए गए 4 टीबी डेटा को प्रोसेस करते समय, सबसे बड़ी चुनौती सभी विज़ुअल/डेटा सोर्स को एक-दूसरे से अलाइन करने की थी. डीएसएलआर कैमरे का हर वीडियो, उससे जुड़े Kinect के साथ अलाइन होना चाहिए, ताकि पिक्सल, समय के साथ-साथ स्पेस में भी अलाइन हो सकें. ऐसे में, एक कलाकार बनाने के लिए इन दोनों कैमरा रिग के फ़ुटेज को एक-दूसरे के साथ अलाइन करना ज़रूरी था. फिर हमें अपने 3D कलाकार को उनकी ड्रॉइंग से कैप्चर किए गए डेटा के साथ अलाइन करना था. वाह! हमने इनमें से ज़्यादातर कामों में मदद के लिए ब्राउज़र पर आधारित टूल लिखे हैं. आप चाहें, तो यहां जाकर इनका इस्तेमाल करें

रिकॉर्डिन आर्टिस्ट

डेटा को अलाइन करने के बाद, हमने NodeJS में लिखी गई कुछ स्क्रिप्ट का इस्तेमाल करके इन स्क्रिप्ट को प्रोसेस किया और एक वीडियो फ़ाइल और JSON फ़ाइलों की सीरीज़ बनाई. इन सभी फ़ाइलों में काट-छांट की गई और उन्हें सिंक किया गया. फ़ाइल का साइज़ कम करने के लिए, हमने तीन काम किए. सबसे पहले, हमने हर फ़्लोटिंग पॉइंट नंबर की सटीक वैल्यू को कम किया, ताकि वह ज़्यादा से ज़्यादा तीन दशमलव संख्या तक सटीक हो. दूसरा, हमने पॉइंट की संख्या को एक तिहाई से 30fps पर सेट किया. साथ ही, क्लाइंट-साइड की पोज़िशन को इंटरपोलेट किया. आखिर में, हमने डेटा को क्रम से लगाया है, ताकि कुंजी/वैल्यू पेयर के साथ सादे JSON का इस्तेमाल करने के बजाय, एचएमडी और कंट्रोलर की पोज़िशन और रोटेशन के लिए वैल्यू का एक ऑर्डर बन जाए. इससे फ़ाइल का साइज़ कम करके 3 एमबी तक कम हो गया. इसे वायर पर डिलीवर किया जा सकता था.

रिकॉर्डिंग कलाकार

वीडियो को HTML5 वीडियो एलिमेंट के तौर पर दिखाया जाता है, जिसे छोटे-छोटे पार्टिकल के तौर पर इस्तेमाल करने के लिए WebGL टेक्सचर में पढ़ा जाता है. इसलिए, वीडियो को बैकग्राउंड में छिपाकर चलाना ज़रूरी था. शेडर, डेप्थ इमेज के रंगों को 3D स्पेस में पोज़िशन में बदल देता है. जेम्स जॉर्ज ने इसका एक बेहतरीन उदाहरण शेयर किया है कि किस तरह सीधे depthKit के ज़रिए बनाए गए फ़ुटेज के साथ किया जा सकता है.

iOS पर इनलाइन वीडियो चलाने की सुविधा सीमित है. हमारा मानना है कि इससे उपयोगकर्ताओं को अपने-आप चलने वाले वेब वीडियो विज्ञापनों से बचाया जा सकता है. हमने वेब पर दूसरे तरीकों से मिलती-जुलती तकनीक का इस्तेमाल किया है. इसमें वीडियो फ़्रेम को कैनवस में कॉपी करना और वीडियो खोजने के समय को हर 1/30 सेकंड में मैन्युअल तरीके से अपडेट करना है.

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

हमारे इस तरीके के खराब असर से iOS फ़्रेमरेट में काफ़ी कमी आई. क्योंकि वीडियो से कैनवस पर पिक्सल बफ़र को कॉपी करने में सीपीयू की रफ़्तार बहुत ज़्यादा होती है. ऐसा करने के लिए, हमने वीडियो के छोटे साइज़ वाले वर्शन पेश किए, जो iPhone 6 पर कम से कम 30 FPS (फ़्रेम प्रति सेकंड) की दर से चलने की अनुमति देते हैं.

नतीजा

2016 तक वीआर सॉफ़्टवेयर डेवलपमेंट के बारे में लोगों ने आम सहमति दी थी कि ज्यामिति और शेडर को आसान रखना चाहिए, ताकि आप एक एचएमडी में 90+fps पर चल सकें. यह WebGL के डेमो के लिए वाकई एक बहुत बढ़िया लक्ष्य बन गया, क्योंकि til Brush मैप में इस्तेमाल की गई तकनीकें, WebGL के लिए बहुत अच्छी तरह से इस्तेमाल की गई हैं.

हालांकि जटिल 3D मेश दिखाने वाले वेब ब्राउज़र अपने-आप में रोमांचक नहीं हैं, लेकिन यह इस सिद्धांत का सबूत है कि वीआर काम और वेब के एक-दूसरे से जुड़े होने की प्रक्रिया पूरी तरह से संभव है.