PC内臓カメラからの映像を、Three.jsで3D空間に表示する
完成イメージ
こんな感じで、PC内臓カメラからの映像が3D空間に描画され、360度グリグリと回すことができます。
環境
解説
今回は、DOM要素を3D空間に描写することの延長です。
3D空間にDOM要素を描写するには、CSS3DRendererを使うのですが、ポイントは下記の5つです。
- シーンとレンダラーは1つずつ(sceneとcssScene、rendererとcssRenderer)用意する
- 通常のsceneに平面オブジェクト(planeObject)を描写し、cssSceneにはDOMオブジェクト(cssObject;)を 同じサイズで 描写する
- DOMオブジェクトでビデオタグを描写する
- DOMオブジェクトのrotationは、平面オブジェクトのrotationと同じにする
- レンダリング時に、どちらのレンダラーも動かす
ちなみに、前にYoutubeを同様に3D空間に表示したのですが、仕組みは同じです。iframeタグを描写するだけです。
コード全貌
index.html
<!DOCTYPE html> <html lang="ja"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>3dPcCamera</title> <style rel="stylesheet" href="css/index.css"></style> </head> <body> <script src="js/three.min.js"></script> <script src="js/libs/OrbitControls.js"></script> <script src="js/libs/CSS3DRenderer.js"></script> <script src="js/main.js"></script> </body> </html>
main.js
// WebRTC関連の変数 let localStream = null; // 3D関連の変数 var container; var camera; var scene, renderer; var planeObject; var cssScene, cssRenderer; var element; var cssObject; var orbitControls; var initCameraPositinZ = 1000; var pcWidth = 640; var pcHeight = 480; init(); animate(); function init() { // 3Dを表示するdivを追加 container = document.createElement( 'div' ); document.body.appendChild( container ); // scene scene = new THREE.Scene(); cssScene = new THREE.Scene(); // camera camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 2000 ); camera.position.z = initCameraPositinZ; // light var ambientLight = new THREE.AmbientLight( 0xcccccc, 0.4 ); scene.add( ambientLight ); cssScene.add( ambientLight ); var pointLight = new THREE.PointLight( 0xffffff, 0.8 ); camera.add( pointLight ); scene.add( camera ); // WebGLRenderer renderer = new THREE.WebGLRenderer(); renderer.setPixelRatio( window.devicePixelRatio ); renderer.setSize( window.innerWidth, window.innerHeight ); container.appendChild( renderer.domElement ); // CSS3DRenderer cssRenderer = new THREE.CSS3DRenderer(); cssRenderer.setSize( window.innerWidth, window.innerHeight ); cssRenderer.domElement.style.position = 'absolute'; cssRenderer.domElement.style.top = 0; cssRenderer.domElement.style.margin = 0; cssRenderer.domElement.style.padding = 0; container.appendChild( cssRenderer.domElement ); // Controls orbitControls = new THREE.OrbitControls(camera, cssRenderer.domElement); // planeオブジェクトの追加 var floorGeo = new THREE.PlaneGeometry(pcWidth,pcHeight); var floorMesh = new THREE.MeshBasicMaterial({color:0xc0c0c0}); // var floorMesh = new THREE.MeshBasicMaterial({wireframe:true}); planeObject = new THREE.Mesh( floorGeo, floorMesh ); var objePositionX = 0; var objePositionY = 0; var objePositionZ = 0; planeObject.position.set(objePositionX, objePositionY, objePositionZ); scene.add( planeObject ); // cssObjectの追加 element = document.createElement('video'); element.id = "local_video"; element.autoplay = true; element.muted = false; element.style.width = pcWidth + 'px'; element.style.height = pcHeight + 'px'; var cssObject = new THREE.CSS3DObject( element ); cssObject.position.set(objePositionX, objePositionY, objePositionZ); cssObject.rotation = planeObject.rotation; cssScene.add( cssObject ); startVideo(element); } function animate() { requestAnimationFrame( animate ); render(); } function render() { cssRenderer.render( cssScene, camera ) renderer.render( scene, camera ); } // getUserMediaでカメラ、マイクにアクセス async function startVideo(localVideo) { try{ localStream = await navigator.mediaDevices.getUserMedia({video: true, audio: false}); playVideo(localVideo,localStream); } catch(err){ console.error('mediaDevice.getUserMedia() error:', err); } } // Videoの再生を開始する async function playVideo(element, stream) { element.srcObject = stream; try { await element.play(); } catch(erro) { console.log('error auto play:' + error); } }