three.jsで、太陽系をつくってみよう!〜シャドウマッピング編〜

はっちゃん


three.jsで、太陽系をつくってみよう!〜シャドウマッピング編〜

こんにちは、はっちゃんです。
今回も張り切って太陽系を進化させていきましょう!

前回までで、太陽系をランダムに置くところまでやりました。

しかし、通常であれば、星と星が重なったとき、後ろの星には前の星の影が映るはずですよね。前回までの太陽系ではそれが再現できていません。

14955061786808400_31
不自然ですね…。

今回のゴールは、下記のように、土台とそれぞれのメッシュに影が投影されることです。
149551313883918900_26

それでは、メッシュと土台に影を投影する設定(シャドウマッピング)を加えて、さらにリアルにしていきましょう。

シャドウマッピングとは

シャドウマッピングは,ライト方向の深度値を格納したシャドウマップと呼ばれるテクスチャを使用してシーンに影を付加して描画を行う手法です。 シャドウマップの長所としては物体の形によらず影がかけることです。 短所としては,テクスチャを使用するためテクスチャの解像度が低いとジャギーがでてしまい影が汚くなってしまうという点です。

☆PROJECT ASURA☆ [Direct3D 11] 『シャドウマッピングの基本』

簡単に言うと、影の設定ですね。

three.jsでは、

  • レンダリング時、影を使用するかどうか
  • 光源が影を作るかどうか
  • 物体が影を作るかどうか
  • 物体が影を受けるかどうか

などが簡単に設定できます。

renderer.shadowMap.enabled = true; //レンダリング時、影を使用するかどうか
light.castShadow = true; //光源が影を作るかどうか
mesh.castShadow = true; //物体が影を作るかどうか
mesh.receiveShadow = true; //物体が影を受けるかどうか

宇宙空間をまっさらな土台に変更

まず、宇宙空間のサイズのままだと大きすぎて影ができないので、サイズを小さくします。合わせて、影を分かりやすくするため、宇宙空間を単色(グレー)に変更します。

■サイズ変更

universe = planetFactory(textureUniverse, 10000, 20, 20, 'isUniverse');

universe = planetFactory(textureUniverse, 950, 20, 20, 'isUniverse');

 

controls.maxDistance = 9900;   //遠ざかれる距離の最大値

controls.maxDistance = 940;   //遠ざかれる距離の最大値

 

■素材変更

new THREE.MeshLambertMaterial({ // 材質     
  map: texture,
  side: THREE.DoubleSide // 裏からも見えるようにする
})

new THREE.MeshLambertMaterial({ // 材質     
  color: 0x444444,
  side: THREE.DoubleSide // 裏からも見えるようにする
})

シャドウマッピングの設定

レンダラーと光源の設定

まず、レンダラーに下記コードで影を使用する命令を書きます。

renderer.shadowMap.enabled = true;

 

次に、光源から発した光でメッシュに影を投影する設定をします。
デフォルトだと光が当たる範囲がとても狭く設定されているので、その値も変更します。

light.castShadow = true;
light.shadow.camera.left = -200;
light.shadow.camera.right = 200;
light.shadow.camera.top = 200;
light.shadow.camera.bottom = -200;

この2つが、シャドウマッピングの大前提になるので、しっかり設定しておきましょう。

メッシュの設定

■宇宙
宇宙は影を作る必要がないので、receiveShadowのみ設定します。

sphere = new THREE.Mesh(
  new THREE.SphereGeometry(radius, widthSegments, heightSegments), // 形状     
  new THREE.MeshLambertMaterial({ // 材質     
    color: 0x444444,
    side: THREE.DoubleSide // 裏からも見えるようにする
  })
);
sphere.position.set(0, 0, 0);
sphere.receiveShadow = true;

 

■太陽
太陽は光源の上にかぶさっているだけのメッシュなので、影の設定はせずに、MeshBasicMaterialで作るだけでOKです。

sphere = new THREE.Mesh(
  new THREE.SphereGeometry(radius, widthSegments, heightSegments), // 形状     
  new THREE.MeshBasicMaterial({ // 材質     
    map: texture,
    side: THREE.DoubleSide // 裏からも見えるようにする
  })
);
sphere.position.set(0, 0, 0);

 

■その他の惑星
その他の惑星は、影を作る・受ける両方の設定が必要なので、receiveShadowとcastShadowを設定します。

sphere = new THREE.Mesh(
  new THREE.SphereGeometry(radius, widthSegments, heightSegments), // 形状
  new THREE.MeshLambertMaterial({ // 材質     
    map: texture                           
  })
);
sphere.position.set(
  Math.random() * 500 - 250,
  Math.random() * 500 - 250,
  Math.random() * 500 - 250
);
sphere.castShadow = true;
sphere.receiveShadow = true;

これでシャドウマッピングができました。

shadow helperを置いてみる

シャドウマッピングにもhelperが存在します。
点光源のhelperは、なぜか一方向の光の広がりしか可視化できないのですが、光の当たる範囲などが分かるので、参考にして光の強さや範囲を調整しましょう。

shadowHelper = new THREE.CameraHelper(light.shadow.camera);
scene.add(shadowHelper);

こんな見た目になります。
スクリーンショット_2017-05-23_14_57_41

完成

シャドーマッピングが正しく設定できると、以下のようになります(視認性を高めるため、helperはコメントアウトしております)。

See the Pen April Blog Shadow by k_hatsushi (@hatsushi_kazuya) on CodePen.


宇宙空間と各惑星に影が投影されているのが分かるかと思います。

まとめ

いかがでしたでしたか?

影の描画は、リアルな表現をするのに非常に重要なので、ぜひ覚えておきましょう!
次回は土星と天王星に輪を作ろうと思います。

はっちゃん
この記事を書いた人
はっちゃん

フロントエンドエンジニア

関連記事