View on GitHub

lectures

Three.js(lec01)

three_js/Home


演習

base10.html のコピー

Visual Studio Code 上でThreeJS-master/lec01/base10.htmlをクリックし、Ctrlキーを押しながらCキーを押して、離す。続けてCtrlキーを押しながらVキーを押すと、ファイルがコピーされる。

copy_01.png

右クリックで「名前を変更」を選択する。

copy_02.png

ファイル名をwork11.htmlとし、Enterキーを押して確定する。

copy_03.png

以降の演習でも各レクチャーのbase**.htmlファイルを同様の手順でコピーし必要なコードを足すこと。

work11 JavaScript の基本

前項で説明した内容を確認する。実行結果の表示は主にtaDebugTextを使うが、JavaScript に慣れている場合はconsole.log()と Chrome ブラウザの JavaScript デバッガを使っても構わない。

変数、定数、配列、連想配列

/* ↓↓↓work11~work12 の追記場所↓↓↓ */
const NUM = 4;
const C_STRING = "Hello:";
for (let i = 0; i < NUM; i++) {
  taDebugText.value += C_STRING + i + "\n";
}
let array = [3, 2, 1, 2, 3];
taDebugText.value += "array.length:" + array.length + "\n";
for (let v of array) {
  taDebugText.value += "v:" + v + "\n";
}
taDebugText.value += "array.indexOf(2):" + array.indexOf(2) + "\n";
taDebugText.value += "array.lastIndexOf(2):" + array.lastIndexOf(2) + "\n";
taDebugText.value += "array.indexOf(100):" + array.indexOf(100) + "\n";
array.splice(2, 1);
array.push(99);
array.splice(1, 0, 55);
for (let v of array) {
  taDebugText.value += "v:" + v + "\n";
}
let dict = { x: 85, y: 12, z: 33 };
taDebugText.value += "dict.x:" + dict.x + "\n";
dict.y -= 8.5;
taDebugText.value += "dict.y:" + dict.y + "\n";
taDebugText.value += "dict.w ?:" + dict.w + "\n";
for (let k in dict) {
  taDebugText.value += k + ":" + dict[k] + "\n";
}
dict["p"] = 128;
dict.q = 255;
delete dict.z;
taDebugText.value += "-------\n";
for (let k in dict) {
  taDebugText.value += k + ":" + dict[k] + "\n";
}
/* ↑↑↑work11~work12 の追記場所↑↑↑ */

work11_01.png

if文、関数

/* ↓↓↓work11~work12 の追記場所↓↓↓ */
let x = 8;
if (x > 5 && x < 10) {
  taDebugText.value += "x > 5 && x < 10 は true\n";
} else {
  taDebugText.value += "x > 5 && x < 10 は false\n";
}
if (x % 2 == 0) {
  taDebugText.value += "xは偶数\n";
} else {
  taDebugText.value += "xは奇数\n";
}
function myAdd(arg1, arg2) {
  let ans = arg1 + arg2;
  return ans;
}
const mySub = function (arg1, arg2) {
  return arg1 - arg2;
};
taDebugText.value += "myAdd(5, 2):" + myAdd(5, 2) + "\n";
taDebugText.value += "mySub(6, 10):" + mySub(6, 10) + "\n";
function makeArray(arg1, arg2, arg3) {
  return [arg1, arg2, arg3];
}
function makeDict(key1, key2, value1, value2) {
  const result = {};
  result[key1] = value1;
  result[key2] = value2;
  return result;
}
array = makeArray(3, 4, 2);
taDebugText.value += "array.length:" + array.length + "\n";
for (let v of array) {
  taDebugText.value += "v:" + v + "\n";
}
dict = makeDict("u", "v", 25, 30);
for (let k in dict) {
  taDebugText.value += k + ":" + dict[k] + "\n";
}
/* ↑↑↑work11~work12 の追記場所↑↑↑ */

work11_02.png

コードの整形

プログラムに適度なインデント(字下げ)があると非常に読みやすくなる。Visual Studio Code にはプログラム全体を自動でインデントする機能がある。

format.png

work12 JavaScript の基本(クラスの定義)

/* ↓↓↓work11~work12 の追記場所↓↓↓ */
class Person {
  constructor(name, age, height, weight) {
    this.name = name;
    this.age = age;
    this.height = height;
    this.weight = weight;
  }

  getDesc() {
    let desc = this.name + ":" + this.age + "才、身長:" + this.height + "、体重:" + this.weight;
    desc += "、BMI:" + this.getBMI().toFixed(2); // toFixed で小数部分の表示桁数を変えられる。
    return desc;
  }

  getBMI() {
    return this.weight / Math.pow(this.height, 2);
  }
}
const p1 = new Person("太郎", 19, 1.6, 56);
taDebugText.value += p1.getDesc();
/* ↑↑↑work11~work12 の追記場所↑↑↑ */

work12.png

work13 CG 物体の移動

ここから、Three.js を使った 3DCG のプログラムに入る。すべての実行結果はマウス操作(Android 端末の場合はピンチや指先をスクリーンに触れてスライドさせる)によりカメラの位置を適切にコントロールした結果の画像である。実行結果の確認時は自分でカメラ位置を調節すること。

Three.js においてTHREE.Meshクラスは三次元物体の形状や材質を保持している。その位置や姿勢はpositionrotationの値を変更することでコントロールできる。

/* ↓↓↓work13 の追記場所↓↓↓ */
sphere.position.set(10, 0, 0);  // 物体の位置( position )に x, y, z の値をセットするコード。
//sphere.position.x = 10; // 軸ごとの指定もできる。
//sphere.position.y = 0;
/* ↑↑↑work13 の追記場所↑↑↑ */

work13.png

work14 物体の回転

/* ↓↓↓work14 の追記場所↓↓↓ */
// 物体のローカル座標軸を表示できる。
cube.add(new THREE.AxesHelper(5));
// 回転はすべてオブジェクトローカル座標系で行われる。単位はラジアンなので、360度単位からラジアンに変換した値を渡す。
cube.rotation.set(THREE.Math.degToRad(45), THREE.Math.degToRad(45), 0); // 物体の姿勢( rotation )に x, y, z の値をセットするコード。
/* ↑↑↑work14 の追記場所↑↑↑ */

work14.png

work15 カメラ

カメラも他の物体と同じように位置や姿勢を持っている。位置はpositionの値を変更すれば良い。work15.htmlの最初の方、下記プログラムで初期位置を 5,5,5 にしていることが分かる。

/* THREE.js の初期化 */
const [scene, camera, renderer, clock, axes] = mylib2020.initThreeInElement(divGlView);
camera.position.set(5, 5, 5);

姿勢についてはカメラの場合は「どの物体をレンズ中心にとらえるか」をlookAtメソッドにより指定する。

/* ↓↓↓work14~work15 の追記場所↓↓↓ */
camera.position.set(-5, 5, 0);
sphere.position.set(0, 3, 5);
/* ↑↑↑work14~work15 の追記場所↑↑↑ */
/* ↓↓↓work15 の追記場所↓↓↓ */
camera.lookAt(sphere.position);
/* ↑↑↑work15 の追記場所↑↑↑ */

work15.png

work16 光源

光源も他の物体と同じように位置や姿勢を持っている。位置はpositionの値を変更すれば良い。光源の種類の一例を示す。

ライトの場合、種類によってはどの位置に向けて光を照射するかを指定することで向きを変えることができる。work16.htmlの次の箇所で平行光源を生成し、初期位置を 5,5,5 にして、座標 0,0,0 に向けて光を放っていることが分かる。

/* 平行光源の生成 */
/* ↓↓↓work16 の修正場所↓↓↓ */
const directionalLight = new THREE.DirectionalLight(0xFFFFFF, 1.0); /* 色は白、強さは 1.0 */
directionalLight.castShadow = true; /* 他の物体に影を落とす */
directionalLight.position.set(5, 5, 5); /* 座標(5,5,5)から */
directionalLight.target.position.set(0, 0, 0); /* 座標(0,0,0)に照射 */
scene.add(directionalLight); /* ライト本体と */
scene.add(directionalLight.target); /* 照射ターゲットもシーンに追加する必要がある */
/* ↑↑↑work16 の修正場所↑↑↑ */
const directionalLight = new THREE.DirectionalLight(0xFF0000, 0.5);

Three.js では色を指定する際に 16 進数を使う。RGB それぞれの輝度を 256 段階(00〜FF:小文字でも良い)で指定している。したがって、0xFF0000 は赤の輝度が最大、緑、青は最低ということである。
THREE.DirectionalLightの第二引数は光の強さである。1 以上も指定できる。

work16_01.png

/* ↓↓↓work16 の修正場所↓↓↓ */
const spotLight = new THREE.SpotLight(0xFFFFFF, 1); /* 色は白、強さは 1.0 */
spotLight.angle = THREE.Math.degToRad(45); /* 光源位置から画角90度の円錐状に光を発する */
spotLight.penumbra = 0; /* 半影をどの程度生じさせるか。 */
spotLight.castShadow = true; /* 他の物体に影を落とす */
spotLight.position.set(10, 1, 10); /* 座標(10,1,10)から */
spotLight.target.position.set(0, 0, 0); /* 座標(0,0,0)に照射 */
scene.add(spotLight); /* ライト本体と */
scene.add(spotLight.target); /* 照射ターゲットもシーンに追加する必要がある */
/* ↑↑↑work16 の修正場所↑↑↑ */

work16_02.png

SpotLight のサンプル

Three.js 公式ページのSpotLight のサンプルでは様々なパラメータを試すことができる。公式ページのサンプルでパラメータを調整し、自分のプログラムに反映させるのも一つの方法である。
公式ページにはTHREE.SpotLightの他にもパラメータ調整用のページを持ったクラスもあるので、試してみると良い。

work17 材質と形状

/* ↓↓↓work17 の修正場所↓↓↓ */
const plane = new THREE.Mesh(new THREE.PlaneGeometry(100, 100), new THREE.MeshPhongMaterial({ color: 0xFF00FF }));
/* ↑↑↑work17 の修正場所↑↑↑ */
/* ↓↓↓work17 の修正場所↓↓↓ */
const sphere = new THREE.Mesh(new THREE.SphereGeometry(2.5, 20, 20), new THREE.MeshPhongMaterial({ color: 0x00FF00, wireframe: true }));
/* ↑↑↑work17 の修正場所↑↑↑ */
/* ↓↓↓work17 の追記・修正場所↓↓↓ */
const cubeGeometry = new THREE.BoxGeometry(2, 20, 2); /* Geometry の生成を分けて書くこともできる */
const cubeMaterial = new THREE.MeshPhongMaterial({color: 0x0000FF}); /* Material の生成を分けて書くこともできる */
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
/* ↑↑↑work17 の追記・修正場所↑↑↑ */

work17.png

三次元物体の情報を保持するTHREE.Meshクラスはコンストラクタに 2 つの引数を取る。第一引数は形状を表すgeometry、第二引数は材質を表すmaterialである。

geometryには自分が生成したい形状に対応したクラスのインスタンスを与える。形状を表すクラスの一例を以下に示す。

materialにも様々な種類があるが、本演習では、ハイライトが可能なTHREE.MeshPhongMaterialを用いる。 THREE.MeshPhongMaterialのコンストラクタは一つの引数parametersを取るが、これは連想配列である(「{」と「:(コロン)」に注目)。

連想配列のキーバリューペアを使い、様々な材質の設定ができる。代表的な設定を以下に示す。

THREE.MeshPhongMaterialで様々な材質の設定を試せる。

テクスチャ

/* ↓↓↓work17 の追記・修正場所↓↓↓ */
const cubeGeometry = new THREE.BoxGeometry(3, 3, 3); /* Geometry の生成を分けて書くこともできる */
const loader = new THREE.TextureLoader(); /* テクスチャをロードするための道具 */
const mapTexture = loader.load("../assets/downloads/ReflectingTheLava.png"); /* 指定されたURLからテクスチャをロード */
const cubeMaterial = new THREE.MeshPhongMaterial({ map: mapTexture }); /* map:テクスチャマッピング用画像を指定 */
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.position.set(0, 3, 0);
/* ↑↑↑work17 の追記・修正場所↑↑↑ */

work17_02.png

Three.js でテクスチャを扱う場合THREE.TextureLoaderloadメソッドを使って指定された URL から画像データをロードする。ロードされたテクスチャのインスタンスはTHREE.MeshPhongMaterialのパラメータにキー名mapとしてセットする。

const loader = new THREE.TextureLoader(); /* テクスチャをロードするための道具 */
const mapTexture = loader.load("../assets/downloads/ReflectingTheLava.png");
const bumpTexture = loader.load("../assets/downloads/RockWall_orFloor_height.png"); /* バンプマップ用テクスチャ */
const cubeMaterial = new THREE.MeshPhongMaterial({ map: mapTexture, bumpMap: bumpTexture, bumpScale: 0.2 }); /* bumpMap:バンプテクスチャ、bumpScale:バンプマップの深さ */

work17_03.png

テクスチャをロードするとき、時間がかかることもある。一般的な WEB ページを読込む際に画像データが徐々に表示されることと同じである。従って実際には「テクスチャ画像が完全に読込まれたら材質や物体を生成する」とプログラムすることが正しいが、本演習では省略している。
THREE.TextureLoaderonLoad callbackの部分を参照のこと。

work18 物体のコピー

全く同じ形状、材質を持つ物体をシーン内に複数配置したいとき、Geometry(形状の情報)やTHREE.MeshPhongMaterial(材質の情報)のインスタンスを何個も作成するのはメモリの無駄遣いであり、やってはいけない。THREE.Meshのインスタンスだけを複数生成すればよい。

/* ↓↓↓work18 の追記・修正場所↓↓↓ */
for (let i = 0; i < 5; i++) { // 5 個の立方体を生成する。
  const tmp = new THREE.Mesh(cubeGeometry, cubeMaterial);
  tmp.castShadow = true; /* 他の物体に影を落とす */
  tmp.receiveShadow = true; /* 他の物体から影が落ちる */
  tmp.position.set(i * -4, 3, i * 4); // 各立方体の座標はどのようになるか?
  scene.add(tmp);
}
/* ↑↑↑work18 の追記・修正場所↑↑↑ */

work18.png


three_js/Home