ホーム > ブログ > プログラミングTips > threejsでオブジェクトに影をつける

threejsでオブジェクトに影をつける

2016.04.29

こんちゃ、銀ねこです。

ブログはまとめて書くタイプです。今回日曜日だというのに家に引きこもってthreejsを勉強したので、それについてまとめます。

影をつけるときは古いリファレンスに要注意。

実は影をつける方法が最新のリビジョン(バージョンみたいなもの)で変わってます。

リビジョンとは、ソフトウェアやハードウェアに対する細かな修正のことである。 リビジョン(revision)とは元々「修正」「改訂」といった意味がある。 製品のバージョンを示す番号にピリオドを付けて示されることが多い。 リビジョンはバグの修正や些細な性能改善が行われた場合に更新される。

参考 : IT用語辞典バイナリ - リビジョンとは

実は最初、ドットインストールで無料の動画コンテンツがありそれで勉強してたんですが影のつけ方でつまづきました。
あれこれ探し回って、結局英語だけどきちんと本家のリファレンス見た方がいいというところに行き着きました。ちなみに今はr(リビジョン)76です。

rendererにshadowMapを使うよーという設定をしないといけないのですが書き方が以下の通り変わってます。

  1. // 新
  2. renderer.shadowMap.enabled = true;
  3.  
  4. // 旧
  5. renderer.shadowMapEnabled = true;

もし、旧を使うと開発者ツールで見ると以下のようにshadowMap.enabledになってるので変えてねーという感じでエラーメッセージが出ます。

スクリーンショット 2016-04-25 8.04.56.png

とりあえず影を出力してみる

では影を実際に出力しみます。
影をつけるためには光源、光を受けるオブジェクト、影を受ける地面にそれぞれ設定をしてやる必要があります。

  1. // 光源
  2. light.castShadow = true;
  3.  
  4. // オブジェクト
  5. obj.castShadow = true;
  6.  
  7. // 地面
  8. plane.receiveShadow = true;

オブジェクトに対して全くサイズの合ってない残念な影が出来上がりました。。。。

スクリーンショット-2016-04-25-8.08.41.jpg

影には細かい設定が必要

影をいい感じで出力するためには光源に以下のような必要です。

  • shadow.camera.near…影の最短距離 影を発生させる光源からの最短距離。デフォ0.5
  • shadow.camera.far…影の最長距離 影を発生させる光源からの最長距離。デフォ500
  • shadow.camera.top…影を作る範囲の上辺 影を発生させる範囲を表す四角柱の上辺の位置。デフォ5
  • shadow.camera.bottom…影を作る範囲の下辺 影を発生させる範囲を表す四角柱の下辺の位置。デフォ-5
  • shadow.camera.left…影を作る範囲の左辺 影を発生させる範囲を表す四角柱の左辺の位置。デフォ-5
  • shadow.camera.right…影を作る範囲の右辺 影を発生させる範囲を表す四角柱の右辺の位置。デフォ5
  • shadow.mapSize.width、shadow.mapSize.height…影の荒さというか細かさ。数値は少なくなればなるほど荒くなり大きくなると細かくなります。基本的には両方同値。デフォ512。

shadow.mapSizeに関しては、100だと荒くなり、1024にすると以下のような感じで細かくなります。

スクリーンショット-2016-04-25-8.08.41.jpg

heplerを使おう

数値を設定できることはわかりましたが、手探りで設定するのは時間的にも労力的にも無駄なので、helperを使って確認するといいです。

DirectionalLightHelperで光の入る方向を確認できます。
以下のコードでDirectionalLightHelperを出力します。

  1. var directionalLightHelper = new THREE.DirectionalLightHelper(light);
  2. scene.add( directionalLightHelper);

すると、以下のように光の方向にラインが入ります。
真上から光が当たるように設定したので真上から光が当たってるのがわかりますね!スクリーンショット 2016-04-25 8.39.58.png

次にTHREE.CameraHelperで光源の影の入る角度や広さなどを確認しましょう。

  1. var directionalLightShadowHelper = new THREE.CameraHelper( light.shadow.camera);
  2. scene.add( directionalLightShadowHelper);

明らかにオブジェクトに合ってない影が設定されていることがわかります。

これはデフォ値なので仕方ないので、先ほどの影の設定を変えていきます。スクリーンショット-2016-04-25-8.46.01.jpg

  1. light.shadow.camera.near = 100;
  2. light.shadow.camera.far = 450;
  3. light.shadow.camera.top = 40;
  4. light.shadow.camera.bottom = -40;
  5. light.shadow.camera.left = 40;
  6. light.shadow.camera.right = -40;
  7. light.shadow.mapSize.width = 200;
  8. light.shadow.mapSize.height = 200;

以下のように影がいい感じのサイズになりました!

スクリーンショット-2016-04-25-8.50.26.jpg

サンプルはこちらです。

アニメーションさせてもばっちり影が動いているのがわかると思います。

  1. <!DOCTYPE html>
  2. <html lang="ja">
  3. <head>
  4. <title>threejsの練習</title>
  5. <meta charset="utf-8">
  6. <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
  7.  
  8. </head>
  9. <body>
  10.  
  11. <script src="js/three.min.js"></script>
  12. <script src="js/OrbitControls.js"></script>
  13. <button id="showHelper"></button>
  14. var camera, scene, light, renderer, material, geometry, mesh, startTime;
  15. var mouseX = 0, mouseY = 0;
  16. var flug = false;
  17.  
  18. function init() {
  19.  
  20. //scene
  21. scene = new THREE.Scene();
  22.  
  23. // object
  24. material = new THREE.MeshLambertMaterial( { color : 0xd556b0 } );
  25. geometry = new THREE.BoxGeometry( 50, 50, 50 );
  26. obj = new THREE.Mesh( geometry, material );
  27. obj.position.y = 70;
  28. obj.castShadow = true;
  29. scene.add( obj );
  30.  
  31. // light
  32. light = new THREE.DirectionalLight( 0xffffff,0.5 );
  33. light.position.set(0, 300, 0 );
  34. light.castShadow = true;
  35. light.shadow.camera.near = 100;
  36. light.shadow.camera.far = 450;
  37. light.shadow.camera.top = 40;
  38. light.shadow.camera.bottom = -40;
  39. light.shadow.camera.left = 40;
  40. light.shadow.camera.right = -40;
  41. light.shadow.mapSize.width = 200;
  42. light.shadow.mapSize.height = 200;
  43. scene.add( light );
  44.  
  45. // 調整光
  46. ambient = new THREE.AmbientLight( 0xffffff ,0.3);
  47. scene.add( ambient);
  48.  
  49. // helper
  50. var directionalLightHelper = new THREE.DirectionalLightHelper(light);
  51. scene.add( directionalLightHelper);
  52.  
  53. var directionalLightShadowHelper = new THREE.CameraHelper( light.shadow.camera);
  54. scene.add( directionalLightShadowHelper);
  55.  
  56. // plane
  57. var plane = new THREE.Mesh(
  58. new THREE.PlaneGeometry(500, 500, 1, 1),
  59. new THREE.MeshLambertMaterial( { color: 0xa0adaf } ) );
  60. plane.rotation.x = - Math.PI / 2;
  61. plane.receiveShadow = true;
  62. scene.add(plane);
  63.  
  64. // render
  65. renderer = new THREE.WebGLRenderer({antialias: true});
  66. renderer.setPixelRatio( window.devicePixelRatio );
  67. renderer.setSize( window.innerWidth, window.innerHeight );
  68. renderer.setClearColor(0xffffff);
  69. renderer.shadowMap.enabled = true;
  70. document.body.appendChild( renderer.domElement );
  71.  
  72. // camera
  73. camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 1000 );
  74. camera.position.set( 0, 100, 0);
  75.  
  76.  
  77. // event
  78. window.addEventListener( 'resize', onWindowResize, false );
  79. document.addEventListener( 'mousemove', onDocumentMouseMove, false );
  80. document.addEventListener( 'mouseover', onDocumentMouseMove, false );
  81.  
  82. // controls
  83. var controls = new THREE.OrbitControls( camera, renderer.domElement );
  84. controls.target.set( 0, 1, 0 );
  85. controls.update();
  86.  
  87. startTime = Date.now();
  88.  
  89. }
  90.  
  91. // 自動でカメラ位置をリサイズ
  92. function onWindowResize() {
  93. camera.aspect = window.innerWidth / window.innerHeight;
  94. camera.updateProjectionMatrix();
  95.  
  96. renderer.setSize( window.innerWidth, window.innerHeight );
  97.  
  98. }
  99.  
  100.  
  101. //マウスオーバーでアニメーション
  102. function onDocumentMouseMove( e ) {
  103.  
  104. var windowHalfX = window.innerWidth / 2;
  105. var windowHalfY = window.innerHeight / 2;
  106. mouseX = ( e.clientX - windowHalfX );
  107. mouseY = ( e.clientY - windowHalfY );
  108.  
  109. }
  110.  
  111. //カメラの位置を変えるアニメーション
  112. function animate() {
  113. var currentTime = Date.now(),
  114. time = ( currentTime - startTime ) / 1000;
  115.  
  116. requestAnimationFrame( animate );
  117.  
  118. camera.position.x += ( mouseX - camera.position.x ) ;
  119. camera.position.y += ( mouseY - camera.position.y ) ;
  120. camera.lookAt( scene.position );
  121.  
  122. obj.rotation.x = time * 0.5;
  123. obj.rotation.y = time * 0.2;
  124.  
  125. renderer.render( scene, camera );
  126.  
  127. }
  128.  
  129. init();
  130. animate();
  131.  
  132. </script>
  133.  
  134. </body>
  135. </html>

この記事のタグ