概要

今回はいろんな方向から物体に光を当ててみる。

@CretedDate 2013/08/18
@Versions Three.js r59

光を当てていない状態

まずは光を当てない状態で物体Xを表示してみる。

<!DOCTYPE html>
<html>

<head>
  <meta charset='utf-8'> 
  <script src="three.js"></script>
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
  <script>
    $(document).ready( function() {
      // Rendererを用意
      var renderer = new THREE.WebGLRenderer( { 'canvas' : $('#canvas')[0] } );

      // Cameraを用意
      var camera = new THREE.PerspectiveCamera();
      camera.position.z = 500;

      // 物体の用意
      var geometry = new THREE.CubeGeometry(200, 200, 200);
      var material = new THREE.MeshLambertMaterial( { color: 0x00ff88 } )
      var mesh = new THREE.Mesh( geometry, material );
      mesh.rotation.x = 0.5;
      mesh.rotation.y = 0.5;

      // Sceneを用意
      var scene = new THREE.Scene();
      scene.add( mesh );

      // render
      renderer.render( scene, camera );      
    } );
  </script>
</head>

<body>
  <canvas id="canvas" style="width:300px; height: 300px; border:solid 1px; margin: 30px;">
    CanvasはHTML5の標準機能です。しばし普及に時間がかかりましたが、今や巻き返しの時です。グラフィカルなWebサイトはお好き? 結構。ではますます好きになりますよ。さあ、どうぞ。WebGLを使った3D画像です。んあぁ、おっしゃらないで。IE8で見れない。でも、IEなんて9以前は使えたもんじゃない。どうぞChromeで見てください。余裕の3Dだ。レンダリング能力が違いますよ。
  </canvas>
</body>

</html>

実行結果はこんな感じ。MeshLambertMaterialはnon-shiny surfacesなので、色指定しても光を当てないと黒くなる。

光の当たってない状態

DirectionalLight

方向を決めて光を当てる、DirectionalLightを当ててみる。

// 光を用意
var light = new THREE.DirectionalLight(0xffffff);
light.position.set(1, 1, 1).normalize();
scene.add( light );

1, 1, 1の方向から、0xffffffの光を当ててます。

光の当たった状態

1, 0, 0の方向から当てると、X軸の方向から光が当たることになるので、上と横はには光は当たらない。

// 光を用意
var light = new THREE.DirectionalLight(0xffffff);
light.position.set(1, 0, 0).normalize();
scene.add( light );

光の当たった状態

逆に0, 1, 0から当てると、上だけ光が当たる。

次に光の色を変えてみる。表面が緑なので黄色(FFFF00)を当てれば黄緑になるはず。

// 光を用意
var light = new THREE.DirectionalLight(0xffff00);
light.position.set(1, 1, 1).normalize();
scene.add( light );

光の当たった状態

若干、黄色が増した。もっとわかりやすいように、白い物体に黄色い光を当ててみる。

// 物体を用意
var geometry = new THREE.CubeGeometry(200, 200, 200);
var material = new THREE.MeshLambertMaterial( { color: 0xffffff } )
var mesh = new THREE.Mesh( geometry, material );
mesh.rotation.x = 0.5;
mesh.rotation.y = 0.5;

// Sceneを用意
var scene = new THREE.Scene();
scene.add( mesh );

// 光を用意
var light = new THREE.DirectionalLight(0xffff00);
light.position.set(1, 1, 1).normalize();
scene.add( light );

光の当たった状態

DirectionalLightの第2引数は光の強さを取る。デフォルトは1なので、これを0.5くらいの弱い光にしてみる。

// 光を用意
var light = new THREE.DirectionalLight(0xffff00, 0.5);
light.position.set(1, 1, 1).normalize();
scene.add( light );

光の当たった状態

0.5くらいの光だとだいぶ弱いらしい。2.0の強い光を当ててみると、下図のようにだいぶ黄色くなる。

光の当たった状態

小さな球を50個ほどランダムに配置して、光を当ててみる。

      // 丸をいくつか用意
      for( var i = 0; i < 50; i++ ) {
        var geometry = new THREE.SphereGeometry(10);
        var material = new THREE.MeshLambertMaterial( { color: 0xffffff } )
        var mesh = new THREE.Mesh( geometry, material ); 
        mesh.position.set( Math.random()*500-250, Math.random()*500-250, Math.random()*500-250 );
        scene.add( mesh );
      }

      // 黄色い光
      var light = new THREE.DirectionalLight(0xffff00);
      light.position.set(1, 1, 1).normalize();
      scene.add( light );

どちらから光が当たっているかよく分かる。

光の当たった状態

AmbientLight

Ambientは取り巻くとか包囲するという意味。DirectionalLightは一方向から光を当てたけど、AmbientLightは自然光のように全体的に光を当てる。

試しに白い物体(FFFFFF)に、赤いAmbientLight(FF0000)を当ててみる。

// 白いCube
var geometry = new THREE.CubeGeometry(200, 200, 200);
var material = new THREE.MeshLambertMaterial( { color: 0xffffff } )
var mesh = new THREE.Mesh( geometry, material );
mesh.rotation.x = 0.5;
mesh.rotation.y = 0.5;

// Sceneを用意
var scene = new THREE.Scene();
scene.add( mesh );

// 赤い光
var light = new THREE.AmbientLight(0xff0000);
scene.add( light );

なんか面白みのない赤になった。

光の当たった状態

赤いAmbientLightに加えて、青いDirectionalLightを当ててみる。

// 赤い光
var light1 = new THREE.AmbientLight(0xff0000);
scene.add( light1 );

// 青い光
var light2 = new THREE.DirectionalLight(0x0000ff);
light2.position.set(1, 1, 1).normalize();
scene.add( light2 );

紫っぽくなった。

光の当たった状態

SpotLight

SpotLightは一部だけ光を当てたりできる。試しに白い球を100個ほど描画して、そこに黄色いSpotLightを当ててみる。

      // 丸をいくつか用意
      for( var i = 0; i < 100; i++ ) {
        var geometry = new THREE.SphereGeometry(10);
        var material = new THREE.MeshLambertMaterial( { color: 0xffffff } )
        var mesh = new THREE.Mesh( geometry, material ); 
        mesh.position.set( Math.random()*500-250, Math.random()*500-250, Math.random()*500-250 );
        scene.add( mesh );
      }

      // 黄色い光
      var light = new THREE.SpotLight(0xffff00);
      light.position = camera.position;
      scene.add( light );

Lightの光源はカメラの場所(0, 0, 500に設定してある)に置いている。見ての通り、中央付近は明るく周囲は暗い光の当たり方をしている。

光の当たった状態

distanceを設定して、もう少し光の範囲を狭めてみる。

      var light = new THREE.SpotLight(0xffff00);
      light.position = camera.position;
      light.distance = 1500;
      scene.add( light );

distance=1500で照射。distanceを設定すると徐々に光が減退するので、だいぶ暗くなる。

光の当たった状態

次にtargetを指定して、光の向きを変えてみる。

      // 緑の光
      var light = new THREE.SpotLight(0x00ff00);
      light.position = camera.position;
      light.target.position.set( 0, 250, 0 );
      scene.add( light );

気分を変えて光を緑にして、cameraのある0,0,500の位置から、targetで指定した0,250,0(斜め上)に光を向けた。

光の当たった状態

SpotLightは影も落とせる。試しに影の設定をせずに、平面の上に球が浮いていて、上から青い光が当たる光景を作ってみる。

      // 球を作る
      var sphere = new THREE.SphereGeometry(100);
      var material = new THREE.MeshLambertMaterial( { color: 0xffffff } )
      var mesh = new THREE.Mesh( sphere, material );
      mesh.position.set( 0, 100, 0 );
      scene.add( mesh );

      // 地面を作る
      var plane = new THREE.PlaneGeometry(300, 300, 10, 10);
      var mesh = new THREE.Mesh( plane, material );
      mesh.rotation.x = -1;
      mesh.position.set( 0, -50, 0 );
      scene.add( mesh );

      // 斜め上から青い光
      var light = new THREE.SpotLight(0x33ccff);
      light.position.set( 0, 500, 500 );
      light.target.position.set( 0, 0, 0 );
      scene.add( light );

影の設定をしていないので、この状態では地面にも等しく光が当たっている。

光の当たった状態

影の設定をして、球の影が地面に描画されるようにします。影を出すには、RendererのshadowMapEnabledをtrueにして、影を作る側の物体のcastShadowをtrueにして、影を受ける側の物体のreceiveShadowをtrueにして、光のcastShadowをtrueにします。

      // Rendererを用意
      renderer = new THREE.WebGLRenderer( { 'canvas' : $('#canvas')[0] } );
      renderer.setSize(300, 300);
      renderer.shadowMapEnabled = true;

      // Cameraを用意
      camera = new THREE.PerspectiveCamera();
      camera.position.z = 500;

      // Sceneを用意
      scene = new THREE.Scene();
      scene.add( camera );

      // 球を作る
      var sphere = new THREE.SphereGeometry(100);
      var material = new THREE.MeshLambertMaterial( { color: 0xffffff } )
      var mesh = new THREE.Mesh( sphere, material );
      mesh.position.set( 0, 100, 0 );
      mesh.castShadow = true;
      scene.add( mesh );

      // 地面を作る
      var plane = new THREE.PlaneGeometry(300, 300, 10, 10);
      var mesh = new THREE.Mesh( plane, material );
      mesh.rotation.x = -1;
      mesh.position.set( 0, -50, 0 );
      mesh.receiveShadow = true;
      scene.add( mesh );

      // 青い光
      var light = new THREE.SpotLight(0x33ccff);
      light.position.set( 0, 500, 500 );
      light.target.position.set( 0, 0, 0 );
      light.shadowCameraVisible = true;
      light.castShadow = true;
      scene.add( light );

      renderer.render(scene, camera);

これで地面に影が差しました。

光の当たった状態

余談ですが、RendererのshadowMapEnabledをtrueにする場合、ちゃんとsetSizeをしてないと描画がされなくなるようです。ハマった。。。