สรุป
ศิลปิน 6 คนได้รับเชิญให้ไปวาดภาพ ออกแบบ และแกะสลักใน VR นี่คือ กระบวนการบันทึกเซสชัน แปลงข้อมูล และนำเสนอ แบบเรียลไทม์ด้วยเว็บเบราว์เซอร์
https://g.co/VirtualArtSessions
ช่างเป็นช่วงเวลาที่ดีที่จะมีชีวิต! ด้วยการนำเทคโนโลยีความจริงเสมือนมาใช้ในฐานะผู้บริโภค พบผลิตภัณฑ์ ทั้งความเป็นไปได้ใหม่ๆ และสิ่งใหม่ๆ ที่ยังไม่ได้สำรวจ Tilt Brush ผลิตภัณฑ์ Google ที่มีอยู่ใน HTC Vive ทำให้คุณสามารถวาด ในพื้นที่มิติได้ ตอนที่ลองใช้ Tilt Brush เป็นครั้งแรก การวาดภาพโดยใช้ตัวควบคุมที่มีการติดตามการเคลื่อนไหวควบคู่ไปกับการแสดงเป็น ที่มีพลังพิเศษ" อยู่กับคุณ จะไม่ค่อยได้รับประสบการณ์ เหมือนการวาดภาพใน พื้นที่ว่างรอบตัวคุณได้
ทีม Data Arts ของ Google พบกับความท้าทายในการแสดงงานนี้ สำหรับผู้ที่ไม่มีชุดหูฟัง VR บนเว็บที่ Tilt Brush ยังดำเนินงานอยู่ ด้วยเหตุนี้ ทีมจึงได้พบกับประติมากร นักวาดภาพประกอบ คอนเซ็ปต์ดีไซน์เนอร์ ศิลปินด้านแฟชั่น ศิลปินช่างตัดเสื้อ และศิลปินสตรีทอาร์ต เพื่อสร้างงานศิลปะในสไตล์ของตัวเองภายในสื่อใหม่นี้
บันทึกภาพวาดในแบบ Virtual Reality
ซอฟต์แวร์ Tilt Brush สร้างขึ้นใน Unity คือแอปพลิเคชันบนเดสก์ท็อปที่
ใช้ VR สำหรับขนาดห้องเพื่อติดตามตำแหน่งศีรษะของคุณ (จอแสดงผลติดศีรษะหรือ HMD)
และตัวควบคุมไว้ในมือแต่ละข้าง อาร์ตเวิร์กที่สร้างใน Tilt Brush จาก
ส่งออกค่าเริ่มต้นเป็นไฟล์ .tilt
ในการนำประสบการณ์นี้มาสู่เว็บ เรา
ได้ตระหนักว่าเราต้องการมากกว่าแค่ข้อมูลอาร์ตเวิร์ก เราทำงานกับ
ให้ทีม Tilt Brush แก้ไข Tilt Brush เพื่อส่งออกการดำเนินการเลิกทำ/ลบด้วย
โดยวางตำแหน่งศีรษะและมือของศิลปินไว้ที่ 90 ครั้งต่อวินาที
เมื่อวาด Tilt 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" เอาไว้ก่อน ที่เป็น ลบหรือยกเลิกการดำเนินการสำหรับเส้นโครงร่างทั้งเส้น
ระบบจะบันทึกข้อมูลพื้นฐานของแต่ละเส้นไว้ ดังนั้นประเภทแปรง ขนาดแปรง และสี RGB ทั้งหมดรวมกัน
สุดท้าย จุดยอดแต่ละจุดของเส้นโครงร่างจะถูกบันทึก
และรวมถึงตำแหน่ง
มุม เวลา และความแรงของแรงกดของทริกเกอร์ของตัวควบคุม (ระบุเป็น p
ภายในแต่ละจุด)
โปรดทราบว่าการหมุนเป็นควอเทอร์เนียน 4 คอมโพเนนต์ ซึ่งมีความสำคัญในภายหลังเมื่อ เราจะแสดงผลเส้นโครงร่างเพื่อหลีกเลี่ยงการล็อกด้วยกิมบอล
การเล่นภาพสเก็ตช์ด้านหลังด้วย WebGL
ในการแสดงภาพร่างในเว็บเบราว์เซอร์ เราใช้ THREE.js และเขียนโค้ดการสร้างเรขาคณิตที่เลียนแบบ สิ่งที่ Tilt Brush ทำจากภายใน
ขณะที่ Tilt Brush สร้างแถบสามเหลี่ยมแบบเรียลไทม์โดยอิงตามมือของผู้ใช้ แสดงว่าภาพร่างทั้งหมดนั้น "จบแล้ว" อยู่แล้ว ตามเวลาที่เราแสดง บนเว็บ ซึ่งทำให้เราข้ามการคำนวณแบบเรียลไทม์ และทำการ Bake เรขาคณิตขณะโหลด
จุดยอดแต่ละคู่ในเส้นโครงร่างจะสร้างเวกเตอร์ทิศทาง (เส้นสีน้ำเงิน
เชื่อมต่อแต่ละจุดดังที่แสดงด้านบน moveVector
ในข้อมูลโค้ดด้านล่าง)
แต่ละจุดยังมีการวางแนวที่เรียกว่าควอเทอร์เนียน
มุมปัจจุบันของตัวควบคุม ในการสร้างแถบสามเหลี่ยม เราจะทำซ้ำขั้นตอน
จุดเหล่านี้ซึ่งทำให้เกิดค่าปกติที่ตั้งฉากกับทิศทางและ
การวางแนวของตัวควบคุม
กระบวนการในการคำนวณแถบสามเหลี่ยมของแต่ละเส้นนั้นแทบจะเหมือนกันทุกประการ กับโค้ดที่ใช้ใน Tilt 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);
}
รวมทิศทางและการวางแนวของเส้นโครงร่างด้วยตัวเอง ผลลัพธ์ที่กำกวมทางคณิตศาสตร์ อาจได้รับค่าปกติได้หลายแบบ มักจะทำให้เกิด "การหักมุม" ในเรขาคณิต
เมื่อมีการทำซ้ำในจุดของโรคหลอดเลือดสมอง เราจะคงค่า "แนะนำด้านขวา"
เวกเตอร์และส่งไปยังฟังก์ชัน 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 );
}
รูปสี่เหลี่ยมแต่ละรูปยังมี UV ที่สร้างขึ้นในขั้นตอนถัดไปด้วย แปรงบางส่วน มีรูปแบบเส้นโครงร่างที่หลากหลาย ทำให้รู้สึกว่าทุกๆ โรคหลอดเลือดสมอง ให้ความรู้สึกเหมือนกำลังลากพู่กันไปมา สามารถทำได้โดยใช้ _texture atlasing, _ซึ่งพื้นผิวแปรงแต่ละแบบจะมีสิ่งที่เป็นไปได้ทั้งหมด รูปแบบต่างๆ เลือกพื้นผิวที่ถูกต้องโดยการแก้ไขค่า UV ของ เส้น
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 );
});
}
เนื่องจากภาพร่างแต่ละภาพมีจำนวนเส้นที่ไม่มีขอบเขตจำกัด และเส้นโครงร่างไม่ต้อง ที่ถูกแก้ไขในรันไทม์ เราจะคำนวณเรขาคณิตของเส้นโครงร่างล่วงหน้า และผสาน ให้เป็นตาข่ายเดียว แม้ว่าแปรงใหม่แต่ละประเภทจะต้องเป็นของตัวเอง จึงยังคงช่วยลดการดึงลูกค้าให้เหลือ 1 ครั้งต่อ 1 แปรงได้
เราได้สร้างภาพสเก็ตช์ที่ใช้เวลา 20 นาทีเพื่อตรวจวัดความเครียดของระบบ ที่มีจุดยอดให้มากที่สุดเท่าที่จะทำได้ การแสดงภาพร่างสุดท้ายก็ยังคงเล่นอยู่ที่ 60 FPS ใน WebGL
เนื่องจากจุดยอดดั้งเดิมของโรคหลอดเลือดสมองแต่ละจุดมีเวลาด้วย เราจึงสามารถ เล่นข้อมูลได้อย่างง่ายดาย การคำนวณเส้นต่อเฟรมใหม่ ดังนั้น เราจึงประมวลผลภาพร่างทั้งหมดขณะโหลดล่วงหน้า และแสดงเพียง แต่ละรูปสี่เหลี่ยม เมื่อถึงเวลาทำเช่นนั้น
การซ่อนรูปสี่เหลี่ยมหมายถึงการยุบจุดยอดของรูปสี่เหลี่ยมให้เหลือ 0,0,0 จุด เมื่อ ถึงเวลาที่ควรจะเผยให้เห็นสี่เหลี่ยมแล้ว เปลี่ยนตำแหน่งจุดยอดมุมกลับเข้าที่
พื้นที่สำหรับการปรับปรุงกำลังจัดการจุดยอดทั้งหมดใน GPU ด้วย ตัวปรับแสงเงา การติดตั้งใช้งานปัจจุบันจะวางองค์ประกอบโดยวนซ้ำจุดยอดมุม อาร์เรย์จากการประทับเวลาปัจจุบัน ตรวจสอบว่าจุดยอดใดที่ต้องเปิดเผย แล้วอัปเดตเรขาคณิต ซึ่งจะทําให้ CPU ทำงานหนัก ซึ่งส่งผลให้ พัดลมหมุน และทำให้อายุการใช้งานแบตเตอรี่หมดเร็ว
การบันทึกเสียงของศิลปิน
เรารู้สึกว่าการแสดงตลกอาจไม่เพียงพอ เราต้องการแสดง ศิลปินในภาพร่างโดยลงสีพู่กันแต่ละพู่กัน
ในการถ่ายภาพศิลปิน เราใช้กล้อง Microsoft Kinect เพื่อบันทึกความลึก ข้อมูลของศิลปิน ร่างกายในอวกาศ สิ่งนี้ทำให้เราสามารถแสดง รูป 3 มิติในพื้นที่เดียวกันที่ภาพวาดจะปรากฏขึ้น
เนื่องจากเนื้อหาของศิลปินจะฝังตัวอยู่ ทำให้เราไม่เห็น ด้านหลังห้องเราใช้ระบบ Kinect คู่ ทั้ง 2 ฝั่งที่อยู่ฝั่งตรงข้ามกันของห้อง ที่ชี้ไปที่ศูนย์กลาง
นอกจากข้อมูลเชิงลึกแล้ว เรายังได้เก็บข้อมูลสีของ ด้วยกล้อง DSLR มาตรฐาน เราใช้ผลิตภัณฑ์ ซอฟต์แวร์ DepthKit สำหรับปรับเทียบและผสาน จาก กล้องวัดความลึกและกล้องสี Kinect มีความสามารถ สีของการบันทึก แต่เราเลือกใช้กล้อง DSLR เพราะเราสามารถควบคุม การตั้งค่าการรับแสง ใช้เลนส์ระดับไฮเอนด์ที่สวยงาม และบันทึกด้วยความละเอียดสูง
ในการบันทึกฟุตเทจ เราได้สร้างห้องพิเศษเพื่อเป็นที่เก็บ HTC Vive ซึ่งเป็นศิลปิน และกล้อง ทุกพื้นผิวถูกปกคลุมด้วยวัสดุที่ดูดซับอินฟราเรด แสงเพื่อให้เรามีจุดที่ดูสะอาดตามากขึ้น (สิ่งสกปรกบนผนัง ยางที่มีริบบิ้น) ปูที่พื้น) ในกรณีที่วัสดุแสดงขึ้นใน Point Cloud เป็นฟุตเทจ เราเลือกวัสดุสีดำ จะได้ไม่ต้องเสียสมาธิมาก ที่เป็นสีขาว
ผลลัพธ์ของวิดีโอที่บันทึกได้ให้ข้อมูลเพียงพอที่จะแสดงอนุภาค ระบบ เราเขียนเครื่องมือเพิ่มเติมใน openFrameworks เพื่อล้างฟุตเทจเพิ่มเติม โดยเฉพาะการกำจัดพื้น ผนัง และเพดาน
นอกเหนือจากการแสดงเหล่าศิลปินแล้ว เรายังต้องการนำเสนอภาพของ HMD และ ตัวควบคุมแบบ 3 มิติได้ด้วย วิธีนี้ไม่เพียงสำคัญสำหรับการแสดง HMD ใน ผลลัพธ์สุดท้ายอย่างชัดเจน (เลนส์สะท้อนของ HTC Vive หลุดออกไป ค่า IR ที่อ่านได้ของ Kinect) ที่ได้ช่วยให้เรามีการติดต่อในการแก้ไขข้อบกพร่องของอนุภาคดังกล่าว และจัดตำแหน่งวิดีโอให้ตรงกับภาพร่าง
ซึ่งทำได้ด้วยการเขียนปลั๊กอินที่กำหนดเองลงใน Tilt Brush ของ HMD และตัวควบคุมในแต่ละเฟรม เนื่องจาก Tilt Brush ทำงานที่ 90fps ข้อมูลที่สตรีมออกจำนวนมาก และข้อมูลอินพุตของสเก็ตช์มีปริมาณสูงขึ้น 20MB ไม่ได้บีบอัด เรายังใช้เทคนิคนี้เพื่อบันทึกเหตุการณ์ที่ไม่ได้บันทึกไว้ด้วย ในไฟล์บันทึก Tilt Brush ทั่วไป เช่น เมื่อศิลปินเลือกตัวเลือก บนแผงเครื่องมือและตำแหน่งวิดเจ็ตมิเรอร์
ในการประมวลผลข้อมูลขนาด 4 TB ที่เราได้มา ความท้าทายที่สำคัญที่สุดอย่างหนึ่งคือ การปรับแหล่งข้อมูลภาพ/แหล่งข้อมูลต่างๆ ทั้งหมดให้สอดคล้องกัน วิดีโอแต่ละรายการจากกล้อง DSLR ต้องอยู่ในแนวเดียวกับ Kinect ที่เกี่ยวข้อง เพื่อให้พิกเซลอยู่ในแนว และเวลา ฟุตเทจจากกล้อง 2 ตัวนี้จำเป็นต้อง ที่สอดคล้องกัน เพื่อสร้างศิลปินคนเดียว จากนั้นเราต้องปรับแนว 3 มิติ ศิลปินกับข้อมูลที่บันทึกจากภาพวาดของพวกเขา ในที่สุด เราเขียนเบราว์เซอร์ตาม เครื่องมือที่จะช่วยทำงานส่วนใหญ่เหล่านี้ และคุณลองใช้ได้เอง ที่นี่
เมื่อข้อมูลสอดคล้องกันแล้ว เราใช้สคริปต์ที่เขียนใน NodeJS เพื่อประมวลผลข้อมูล ทั้งหมดและส่งออกไฟล์วิดีโอและชุดของไฟล์ JSON ทั้งหมดจะถูกตัดทอนและ ทำให้ข้อมูลตรงกันแล้ว เราได้ทำ 3 อย่างในการลดขนาดไฟล์ อย่างแรก เราลด ความแม่นยำของตัวเลขทศนิยมแต่ละจำนวน เพื่อให้มีค่าไม่เกิน 3 ของความแม่นยำทศนิยม อย่างที่สอง เราลดจำนวนจุดลง 1 ใน 3 ให้เหลือ 30fps แล้วแปลตำแหน่งฝั่งไคลเอ็นต์ สุดท้าย เราได้เรียงอันดับ ดังนั้น แทนที่จะใช้ JSON ธรรมดากับคู่คีย์/ค่า ลำดับของค่าจะเป็น ที่สร้างขึ้นสำหรับตำแหน่งและการหมุน HMD และตัวควบคุม การดำเนินการนี้เป็นการตัดไฟล์ ให้มีขนาดเล็กลงเหลือเพียง 3MB ซึ่งยอมรับได้ว่าจะส่งผ่านสาย
เนื่องจากตัววิดีโอจะมีการแสดงผลเป็นองค์ประกอบวิดีโอ HTML5 ที่อ่านโดย พื้นผิว WebGL เพื่อให้กลายเป็นอนุภาค วิดีโอนั้นเองจึงจะสามารถเล่น พื้นหลัง เฉดสีจะแปลงสีในภาพระดับความชัดลึกเป็นตำแหน่งใน 3 มิติ James George ได้แชร์ตัวอย่างที่ยอดเยี่ยม วิธีใช้ฟุตเทจจาก 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 ลดลงอย่างมาก อัตราเฟรมตั้งแต่การคัดลอกพิกเซลบัฟเฟอร์จากวิดีโอไปยังแคนวาส ใช้ CPU มาก ในการแก้ปัญหานี้ เราเพียงแค่แสดงเวอร์ชันขนาดเล็กของ วิดีโอเดียวกับที่มีอัตราเฟรมอย่างน้อย 30 FPS ใน iPhone 6
บทสรุป
ความเห็นพ้องกันทั่วไปสำหรับการพัฒนาซอฟต์แวร์ VR ในปี 2016 คือ เรขาคณิตและเส้นเฉดสีที่เรียบง่าย เพื่อให้คุณสามารถเรียกใช้ที่ 90+fps ใน HMD ช่วงเวลานี้ กลายเป็นเป้าหมายที่ดีสำหรับการสาธิต WebGL เพราะเทคนิคที่ใช้ ใน Tilt Brush แมปกับ WebGL ได้ดีมากๆ
แม้ว่าเว็บเบราว์เซอร์ที่แสดงโครงข่าย 3 มิติที่ซับซ้อนนั้นไม่ใช่เรื่องน่าตื่นเต้นใน นี่เป็นข้อพิสูจน์ของแนวคิดที่ว่า การผสมเกสรดอกไม้ระหว่างงาน VR และ เว็บเป็นไปได้ทั้งหมด