HTML5テクニカルノート
three.js入門 04: マテリアルを使う
- ID: FN1705001
- Technique: HTML5 / JavaScript
- Library: three.js r85
「three.js入門」03「ジオメトリーを使う」は、幾何学的な情報であるジオメトリーをいくつかの基本的な立体でご紹介しました。本稿は立体の表面の見た目を決める素材となるマテリアルについてご説明します。
01 立体をワイヤーフレームで描く
「three.js入門」03のサンプル003「three.js r85: Rotating a icosahedron」に手を加えるかたちで進めます。マテリアルはMeshNormalMaterial
からMeshPhongMaterial
に差し替えます。そうすると、光を当てなければ暗闇で見えません(「three.js入門」02の項01「立方体の素材をフォンマテリアルに差し替える」参照)。そこで、WebGLRenderer()
コンストラクタの引数にオブジェクトを与え、alpha
プロパティはtrue
とします。すると、闇は拭われて立体が姿を現すのです。もっとも、このままでは立体は黒い塊にしかなりません。MeshPhongMaterial()
コンストラクタも引数オブジェクトで、wireframe
プロパティをtrue
にすると、立体がワイヤーフレームで描かれます(図001)。コードはサンプル001としてjsdo.itに掲げました。
function createRenderer(width, height) { var renderer = new THREE.WebGLRenderer({alpha: true}); } function createMesh(radius) { // var material = new THREE.MeshNormalMaterial(); var material = new THREE.MeshPhongMaterial({wireframe: true}); }
-
図001■ワイヤーフレームの立体が回る
-
サンプル001■three.js r85: Rotating a wireframe icosahedron
02 フォンマテリアルをライトで照らす
改めて、光を定めましょう。WebGLRenderer()
コンストラクタは引数なしに戻します。MeshPhongMaterial()
コンストラクタも、ワイヤーフレームはやめて、color
プロパティのカラー値を関数(createMesh())の引数(color)から与えるようにします。加える光源はふたつです。ひとつは、平行光源をDirectionalLight()
コンストラクタでつくります(「three.js入門」02の項02「シーンにライトを加える」参照)。もうひとつは、電球のように光が広がる点光源(point light)です(図002)。遠ざかるほど、明るさは弱まります。コンストラクタはPointLight()
で、第1引数に渡すのはDirectionalLight()
と同じくカラー値です。
function init() { // mesh = createMesh(side); mesh = createMesh(side, 0x0000FF); scene.add(createLight(0xFFFFFF, 10, 0, 25)); scene.add(createPointLight(0xFFFFFF, -50, 50, 50)); } function createRenderer(width, height) { var renderer = new THREE.WebGLRenderer(); // {alpha: true}); } // function createMesh(radius) { function createMesh(radius, color) { var material = new THREE.MeshPhongMaterial({color: color}); // , wireframe: true}); } function createLight(color, x, y, z) { var light = new THREE.DirectionalLight(color); light.position.set(x, y, z); return light; } function createPointLight(color, x, y, z) { var light = new THREE.PointLight(color); light.position.set(x, y, z); return light; }
図002■3次元表現で使われる光源の種類
*Tcpp's fileより引用
これで、正二十面体がふたつの光源に照らされて3次元空間で回ります(図003)。スクリプトは以下のコード001にまとめ、サンプル002をjsdo.itに掲げました。
-
図003■ふたつの光源に照らされて3次元空間で回る正二十面体
-
サンプル002■three.js r85: Rotating a icosahedron with lights
コード001■ふたつの光源で正二十面体を照らして3次元空間で回す
var width = window.innerWidth;
var height = window.innerHeight;
var side = Math.min(width, height) / 50;
var scene;
var camera;
var renderer;
var mesh;
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(30, width / height, 1, 1000);
renderer = createRenderer(width, height);
mesh = createMesh(side, 0x0000FF);
camera.position.z = 100;
scene.add(mesh);
scene.add(createLight(0xFFFFFF, 10, 0, 25));
scene.add(createPointLight(0xFFFFFF, -50, 50, 50));
update();
}
function createRenderer(width, height) {
var renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);
return renderer;
}
function createMesh(radius, color) {
var geometry = new THREE.IcosahedronGeometry(radius);
var material = new THREE.MeshPhongMaterial({color: color});
var mesh = new THREE.Mesh(geometry, material);
return mesh;
}
function createLight(color, x, y, z) {
var light = new THREE.DirectionalLight(color);
light.position.set(x, y, z);
return light;
}
function createPointLight(color, x, y, z) {
var light = new THREE.PointLight(color);
light.position.set(x, y, z);
return light;
}
function update() {
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.01;
requestAnimationFrame(update);
renderer.render(scene, camera);
}
window.addEventListener('DOMContentLoaded', init);
03 鏡面反射光の色を加える
MeshPhongMaterial
の表面は光を反射します。鏡面反射光の色は、MeshPhongMaterial.specular
プロパティで与えられます。デフォルト値は0x111111のかなり暗いグレーです。MeshPhongMaterial()
コンストラクタの引数にオブジェクトを渡して定めることもできます。以下のサンプル003は、前掲コード001をつぎのように書き替えて、明るい緑の鏡面反射光を加えました(図004)。
function createMesh(radius, color) { var material = new THREE.MeshPhongMaterial({color: color, specular: 0x00FF00}); }
-
図004■フォンマテリアルに明るい緑の鏡面反射光を加えた
-
サンプル003■three.js r85: Rotating a icosahedron with lights with specular and high shinyness
04 バンプマップで表面に質感を加える
バンプマップは、表面の凹凸をグレースケールのビットマップ(テクスチャ)で定めて反射の表現に用いる手法です。かたち(ジオメトリー)は変えることなく、立体に質感が加わります。MeshPhongMaterial.bumpMap
プロパティにテクスチャを与えます。今回使うのは、図005のテクスチャです。
図005■表面の凹凸をグレースケールのテクスチャで表す
テクスチャを読み込むために用いるのは、TextureLoader
クラスです。TextureLoader.load()
メソッドで、ロードが行われます。第1引数にテクスチャのURL、第2引数には読み込み終えたときのコールバック関数を渡します。コールバックが引数に受け取るのは、読み込んだTexture
オブジェクトです。
TextureLoaderオブジェクト.load(テクスチャのURL, ロード時のコールバック) function コールバック(テクスチャ) {}
バンプマップのテクスチャは別のドメイン(オリジン)から読み込みます。この場合には、TextureLoader.crossOrigin
プロパティにtrue
を与えなければなりません(デフォルト値undefined
)。そうすると、crossorigin
属性が別オリジンからロードできるように定められます(「HTML5 における CORS について」参照)
前掲コード001は、つぎのように書き替えます。テクスチャが読み込み終わったコールバックで、立体をつくり(createMesh())、シーンに加えてから(Object3D.add()
メソッド)、描画を始めます(update())。MeshPhongMaterial.bumpMap
プロパティに与えるテクスチャは、MeshPhongMaterial()
コンストラクタを呼び出す引数のオブジェクトに加えました。
function init() { var textureLoader = new THREE.TextureLoader(); textureLoader.crossOrigin = true; // mesh = createMesh(side, 0x0000FF); textureLoader.load('https://s3-us-west-2.amazonaws.com/s.cdpn.io/53148/4268-bump.jpg', function(texture) { mesh = createMesh(side, 0x0000FF, texture); scene.add(mesh); update(); }); // scene.add(mesh); // update(); } // function createMesh(radius, color) { function createMesh(radius, color, texture) { var material = new THREE.MeshPhongMaterial({color: color, bumpMap: texture}); }
これでフォンマテリアルにバンプマップが与えられて、立体の表面に凹凸の質感が加わります。スクリプトは以下のコード002にまとめました。jsdo.itのサンプルは別オリジンの読み込みがあると埋め込みできなくなるため、「three.js r85: Rotating a icosahedron with bump map」に掲げました。
図005■フォンマテリアルにバンプマップで立体の表面に質感が加わる
>> jsdo.itへ
コード002■フォンマテリアルにバンプマップで立体の表面の質感を加える
var width = window.innerWidth;
var height = window.innerHeight;
var side = Math.min(width, height) / 50;
var scene;
var camera;
var renderer;
var mesh;
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(30, width / height, 1, 1000);
renderer = createRenderer(width, height);
var textureLoader = new THREE.TextureLoader();
textureLoader.crossOrigin = true;
textureLoader.load('https://s3-us-west-2.amazonaws.com/s.cdpn.io/53148/4268-bump.jpg', function(texture) {
mesh = createMesh(side, 0x0000FF, texture);
scene.add(mesh);
update();
});
camera.position.z = 100;
scene.add(createLight(0xFFFFFF, 10, 0, 25));
scene.add(createPointLight(0xFFFFFF, -50, 50, 50));
}
function createRenderer(width, height) {
var renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);
return renderer;
}
function createMesh(radius, color, texture) {
var geometry = new THREE.IcosahedronGeometry(radius);
var material = new THREE.MeshPhongMaterial({color: color, bumpMap: texture});
var mesh = new THREE.Mesh(geometry, material);
return mesh;
}
function createLight(color, x, y, z) {
var light = new THREE.DirectionalLight(color);
light.position.set(x, y, z);
return light;
}
function createPointLight(color, x, y, z) {
var light = new THREE.PointLight(color);
light.position.set(x, y, z);
return light;
}
function update() {
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.01;
requestAnimationFrame(update);
renderer.render(scene, camera);
}
window.addEventListener('DOMContentLoaded', init);
05 テクスチャマップを球体の表面に貼る
フォンマテリアルにテクスチャを画像として貼りましょう。この場合に用いるのが、MeshPhongMaterial.map
プロパティです。ジオメトリーはSphereGeometry
にして球体をつくりましょう。テクスチャマップとして使うのは、つぎの図006の画像です。HTMLドキュメントと同じ階層にフォルダ(images)をつくって納めます(pens.png')。
図006■球体の表面に貼るテクスチャ
>> pens.png (1024×512px)
テクスチャは、やはりTextureLoader
クラスで読み込みます。ピクセル(テクセル)のカラーがマッピングされますので、マテリアルには白(0xFFFFFF)を与えましょう。MeshPhongMaterial.map
プロパティに定めるテクスチャは、コンストラクタのオブジェクトに引数で加えました。これでフォンマテリアルの球体には、前掲図006のテクスチャがマッピングされます(図007)。
function init() { // textureLoader.load('https://s3-us-west-2.amazonaws.com/s.cdpn.io/53148/4268-bump.jpg', function(texture) { textureLoader.load('images/pens.png', function(texture) { // mesh = createMesh(side, 0x0000FF, texture); mesh = createMesh(side, 0xFFFFFF, texture); }); } function createMesh(radius, color, texture) { // var geometry = new THREE.IcosahedronGeometry(radius); var geometry = new THREE.SphereGeometry(radius, 32, 32); var material = new THREE.MeshPhongMaterial({color: color, map: texture}); // bumpMap: texture}); }
図007■球体の表面にテクスチャがマッピングされた
>> jsdo.itへ
これまでのサンプルは、立体をアニメーションで回しました。ここでは、カメラの位置を動かすことにしましょう。ダウンロードしたthree.jsライブラリの「examples/js/controls/」に含まれているOrbitControls.jsを使えば、マウスで簡単にカメラが操作できます。JavaScriptファイルをライブラリ用のフォルダ(ここではlib)に納めたら、<script>
要素に読み込んで、つぎのようにOrbitControls()
コンストラクタにカメラを渡して呼び出すだけです(詳しくは「three.jsのOrbitControls.jsで簡単にカメラをマウスコントロールする」をお読みください)。
<script src="lib/OrbitControls.js"></script>
var controls; function init() { controls = new THREE.OrbitControls(camera); }
カメラを動かすためのマウス操作は、つぎのとおりです。
- オービット: ドラッグ
- ズーム: ホイール
- パン: 右ボタンドラッグ
さらに、OrbitControls.autoRotate
プロパティにtrue
を与えると、カメラは立体を中心にゆっくりと周回します。この場合、アニメーションの再描画メソッド(update())の中からOrbitControls.update()
メソッドを呼び出してください。なお、立体のx軸周りの回転は、動きがわかりにくくなりそうなので外しました。
function init() { controls = new THREE.OrbitControls(camera); controls.autoRotate = true; } function update() { // mesh.rotation.x += 0.01; mesh.rotation.y += 0.01; controls.update(); }
書き上げたスクリプトは、つぎのコード003にまとめたとおりです。jsdo.itには「three.js r85: Rotating a sphere with texture」として掲げました。
コード003■球体にテクスチャマップを貼ってマウスでカメラ操作する
var width = window.innerWidth;
var height = window.innerHeight;
var side = Math.min(width, height) / 50;
var scene;
var camera;
var renderer;
var mesh;
var controls;
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(30, width / height, 1, 1000);
renderer = createRenderer(width, height);
var textureLoader = new THREE.TextureLoader();
textureLoader.crossOrigin = true;
textureLoader.load('images/pens.png', function(texture) {
mesh = createMesh(side, 0xFFFFFF, texture);
scene.add(mesh);
update();
});
controls = new THREE.OrbitControls(camera);
controls.autoRotate = true;
camera.position.z = 100;
scene.add(createLight(0xFFFFFF, 10, 0, 25));
scene.add(createPointLight(0xFFFFFF, -50, 50, 50));
}
function createRenderer(width, height) {
var renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);
return renderer;
}
function createMesh(radius, color, texture) {
var geometry = new THREE.SphereGeometry(radius, 32, 32);
var material = new THREE.MeshPhongMaterial({color: color, map: texture});
var mesh = new THREE.Mesh(geometry, material);
return mesh;
}
function createLight(color, x, y, z) {
var light = new THREE.DirectionalLight(color);
light.position.set(x, y, z);
return light;
}
function createPointLight(color, x, y, z) {
var light = new THREE.PointLight(color);
light.position.set(x, y, z);
return light;
}
function update() {
mesh.rotation.y += 0.01;
requestAnimationFrame(update);
controls.update();
renderer.render(scene, camera);
}
window.addEventListener('DOMContentLoaded', init);
作成者: 野中文雄
作成日: 2017年5月8日
Copyright © 2001-2016 Fumio Nonaka. All rights reserved.