THETA S で全天球映像を配信するまで(3) 映像を貼り付けて表示してみました


こんにちは、インフォコム技術企画室のがねこです。

来る2016年2月16日、17日に行われる、WebRTC Conference Japan 2016RICOH THETA S を使ったリアルタイム全天球配信のデモを展示する予定です。前回はTHETA S から取得できるライブ映像をWebGLの球面上に貼り付ける準備をしたので、今回は実際に表示してみます。

video要素に表示した映像をWebGLで作ったモデルの表面に貼り付けることができます。録画済の動画ファイルでも、Webカメラの映像でも、WebRTCで通信している離れた場所の映像でも、やり方は同じです。おおざっぱに言うと次の図のような流れになります。

webgl_render_video.png

映像の描画

全天球の描画の基本的な処理は、Three.jsの公式サンプルを参考にしました。関連する部分を抜き出すと、次の通りです。

 // 動画をテクスチャに変換するための準備
 var videElement = document.getElementById('video_element'); // 動画を表示するvideo要素
 var videoImage = document.getElementById('canvas_element'); // 画像に変換するために利用するcanvas要素
 var videoImageContext = videoImage .getContext('2d');   
 var videoTexture = new THREE.Texture( videoImage );

 // WebGL表示のための準備
 var renderer = new THREE.WebGLRenderer({ alpha: true });
 var camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 100);
 var light = new THREE.AmbientLight(0xffffff);
 light.position.set(0, 0, 0).normalize();
 scene = new THREE.Scene();
 scene.add(light);

 // ... 省略 ...

 // 描画開始
 animate();

 function animate() {
   requestAnimationFrame( animate );
   update();
 }

 function update() {
   // video to image
   videoImageContext.drawImage( videElement, 0, 0, videoImage.width, videoImage.height );
   if ( videoTexture ) { 
     videoTexture.needsUpdate = true;
   }

   renderer.render( scene, camera );
 }

※完全なソースは、Three.jsのサンプルや、Githubをご覧ください

マウス操作とタッチ操作

Three.jsの公式サンプルには、マウスのドラッグで視点を変えたり、ホイールでズーム(画角)を制御する部分も含まれています。

同様にスマートフォンやタブレットのタッチ操作に対応するコードは、次のように追加しました。

  container = document.getElementById( 'container' );
  container.addEventListener( 'touchstart', onDocumentTouchStart, false );
  container.addEventListener( 'touchend', onDocumentTouchEnd, false );
  container.addEventListener( 'touchcancel', onDocumentTouchEnd, false );
  container.addEventListener( 'touchmove', onDocumentTouchMove, false );

    var isPinching = false;
    var pinchStartDistance = 0;
    var FOV_MIN = 20;
    var FOV_MAX = 140;

    function onDocumentTouchStart( event ) {
      event.preventDefault();
  
      isUserInteracting = true;
  
      var touches = event.touches;
      var l = touches.length;
      if (l == 1) {
        var touch = touches[0];
        onPointerDownPointerX = touch.clientX;
        onPointerDownPointerY = touch.clientY;
        onPointerDownLon = lon;
        onPointerDownLat = lat;
      }
      else if (l == 2) {
        isPinching = true;
  
        // distance
        var touch1 = touches[0];
        var touch2 = touches[1];
        var dx = touch1.clientX - touch2.clientX;
        var dy = touch1.clientY - touch2.clientY;
        pinchStartDistance = Math.sqrt(dx*dx + dy*dy);
      }
    }

    function onDocumentTouchEnd( event ) {
      isUserInteracting = false;
  
      if (isPinching) {
        isPinching = false;
      }
    }

    function onDocumentTouchMove( event ) {
      if ( isUserInteracting === true ) {
        var touches = event.touches;
        var l = touches.length;
        if (l == 1) {
          var touch = touches[0];
          lon = ( onPointerDownPointerX - touch.clientX ) * 0.1 + onPointerDownLon;
          lat = ( touch.clientY - onPointerDownPointerY ) * 0.1 + onPointerDownLat; 
        }
        else if (l == 2) {
          // distance
          var touch1 = touches[0];
          var touch2 = touches[1];
          var dx = touch1.clientX - touch2.clientX;
          var dy = touch1.clientY - touch2.clientY;
          var distance = Math.sqrt(dx*dx + dy*dy);
  
          camera.fov -= (distance -  pinchStartDistance)*0.02;
          if (camera.fov > FOV_MAX) {
            camera.fov = FOV_MAX;
          }
          if (camera.fov < FOV_MIN) {
            camera.fov = FOV_MIN;
          }
          camera.updateProjectionMatrix();
        }
      }
    }

これでPCのブラウザ(Chrome, Firefox)だけでなく、Androidのブラウザ(Chrome, Firefox)でも自由に360度映像を見ることができます。

ライブラリの公開

今回調べた内容を私たちのデモから切り離し、自由にお使いいただける形でgithubに公開しています。ご興味のある方は、ぜひお試しください。

参考

他にもTHETA Sの映像をWebGLで表示する方法が公開されています。一部ではとてもホットな話題になっています。

UVマッピング方式

THETA S のUSBライブストリーミングをブラウザで球面マップする

THETAのDualfisheye動画をThree.jsで表示してみた

シェーダー方式

https://github.com/gtk2k/gtk2k.github.io/tree/master/aframe_theta_s_live_preview

補足

twitterのコメントで、テクスチャーはCanvasを経由せずに、直接video要素から生成できると教えていただきました。次のようにコードが処理をシンプルにすることができました。

 // 動画をテクスチャに変換するための準備
 var videElement = document.getElementById('video_element'); // 動画を表示するvideo要素
 var videoTexture = new THREE.Texture( videElement );

function update() {
   // video to image
   // videoImageContext.drawImage( videElement, 0, 0, videoImage.width, videoImage.height ); <-- これは不要
   if ( videoTexture ) { 
     videoTexture.needsUpdate = true;
   }

   renderer.render( scene, camera );
 }

今回の用途ではCanvasの2d context で左右反転させているため、CanvasとdrawImage()の利用を継続しています。

THETA S で全天球映像を配信するまで(3) 映像を貼り付けて表示してみました


こんにちは、インフォコム技術企画室のがねこです。

来る2016年2月16日、17日に行われる、WebRTC Conference Japan 2016RICOH THETA S を使ったリアルタイム全天球配信のデモを展示する予定です。前回はTHETA S から取得できるライブ映像をWebGLの球面上に貼り付ける準備をしたので、今回は実際に表示してみます。

video要素に表示した映像をWebGLで作ったモデルの表面に貼り付けることができます。録画済の動画ファイルでも、Webカメラの映像でも、WebRTCで通信している離れた場所の映像でも、やり方は同じです。おおざっぱに言うと次の図のような流れになります。

webgl_render_video.png

映像の描画

全天球の描画の基本的な処理は、Three.jsの公式サンプルを参考にしました。関連する部分を抜き出すと、次の通りです。

 // 動画をテクスチャに変換するための準備
 var videElement = document.getElementById('video_element'); // 動画を表示するvideo要素
 var videoImage = document.getElementById('canvas_element'); // 画像に変換するために利用するcanvas要素
 var videoImageContext = videoImage .getContext('2d');   
 var videoTexture = new THREE.Texture( videoImage );

 // WebGL表示のための準備
 var renderer = new THREE.WebGLRenderer({ alpha: true });
 var camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 100);
 var light = new THREE.AmbientLight(0xffffff);
 light.position.set(0, 0, 0).normalize();
 scene = new THREE.Scene();
 scene.add(light);

 // ... 省略 ...

 // 描画開始
 animate();

 function animate() {
   requestAnimationFrame( animate );
   update();
 }

 function update() {
   // video to image
   videoImageContext.drawImage( videElement, 0, 0, videoImage.width, videoImage.height );
   if ( videoTexture ) { 
     videoTexture.needsUpdate = true;
   }

   renderer.render( scene, camera );
 }

※完全なソースは、Three.jsのサンプルや、Githubをご覧ください

マウス操作とタッチ操作

Three.jsの公式サンプルには、マウスのドラッグで視点を変えたり、ホイールでズーム(画角)を制御する部分も含まれています。

同様にスマートフォンやタブレットのタッチ操作に対応するコードは、次のように追加しました。

  container = document.getElementById( 'container' );
  container.addEventListener( 'touchstart', onDocumentTouchStart, false );
  container.addEventListener( 'touchend', onDocumentTouchEnd, false );
  container.addEventListener( 'touchcancel', onDocumentTouchEnd, false );
  container.addEventListener( 'touchmove', onDocumentTouchMove, false );

    var isPinching = false;
    var pinchStartDistance = 0;
    var FOV_MIN = 20;
    var FOV_MAX = 140;

    function onDocumentTouchStart( event ) {
      event.preventDefault();
  
      isUserInteracting = true;
  
      var touches = event.touches;
      var l = touches.length;
      if (l == 1) {
        var touch = touches[0];
        onPointerDownPointerX = touch.clientX;
        onPointerDownPointerY = touch.clientY;
        onPointerDownLon = lon;
        onPointerDownLat = lat;
      }
      else if (l == 2) {
        isPinching = true;
  
        // distance
        var touch1 = touches[0];
        var touch2 = touches[1];
        var dx = touch1.clientX - touch2.clientX;
        var dy = touch1.clientY - touch2.clientY;
        pinchStartDistance = Math.sqrt(dx*dx + dy*dy);
      }
    }

    function onDocumentTouchEnd( event ) {
      isUserInteracting = false;
  
      if (isPinching) {
        isPinching = false;
      }
    }

    function onDocumentTouchMove( event ) {
      if ( isUserInteracting === true ) {
        var touches = event.touches;
        var l = touches.length;
        if (l == 1) {
          var touch = touches[0];
          lon = ( onPointerDownPointerX - touch.clientX ) * 0.1 + onPointerDownLon;
          lat = ( touch.clientY - onPointerDownPointerY ) * 0.1 + onPointerDownLat; 
        }
        else if (l == 2) {
          // distance
          var touch1 = touches[0];
          var touch2 = touches[1];
          var dx = touch1.clientX - touch2.clientX;
          var dy = touch1.clientY - touch2.clientY;
          var distance = Math.sqrt(dx*dx + dy*dy);
  
          camera.fov -= (distance -  pinchStartDistance)*0.02;
          if (camera.fov > FOV_MAX) {
            camera.fov = FOV_MAX;
          }
          if (camera.fov < FOV_MIN) {
            camera.fov = FOV_MIN;
          }
          camera.updateProjectionMatrix();
        }
      }
    }

これでPCのブラウザ(Chrome, Firefox)だけでなく、Androidのブラウザ(Chrome, Firefox)でも自由に360度映像を見ることができます。

ライブラリの公開

今回調べた内容を私たちのデモから切り離し、自由にお使いいただける形でgithubに公開しています。ご興味のある方は、ぜひお試しください。

参考

他にもTHETA Sの映像をWebGLで表示する方法が公開されています。一部ではとてもホットな話題になっています。

UVマッピング方式

THETA S のUSBライブストリーミングをブラウザで球面マップする

THETAのDualfisheye動画をThree.jsで表示してみた

シェーダー方式

https://github.com/gtk2k/gtk2k.github.io/tree/master/aframe_theta_s_live_preview

補足

twitterのコメントで、テクスチャーはCanvasを経由せずに、直接video要素から生成できると教えていただきました。次のようにコードが処理をシンプルにすることができました。

 // 動画をテクスチャに変換するための準備
 var videElement = document.getElementById('video_element'); // 動画を表示するvideo要素
 var videoTexture = new THREE.Texture( videElement );

function update() {
   // video to image
   // videoImageContext.drawImage( videElement, 0, 0, videoImage.width, videoImage.height ); <-- これは不要
   if ( videoTexture ) { 
     videoTexture.needsUpdate = true;
   }

   renderer.render( scene, camera );
 }

今回の用途ではCanvasの2d context で左右反転させているため、CanvasとdrawImage()の利用を継続しています。

コメントする