以前ご紹介した「WebGL」の続きになります。今年こそブラウザ3D事始めんと欲し「書き初め」的に書いた投稿からもうじき半年…。 前回tone.jsと同様、今回も便利なライブラリ「Three.js」を使って一刻も早く体験したく。いざっ!
※目次:
- Three.jsとは
- three.jsの読み込み
- Three.js入門サイトにいざ入門!
- サンプル再現!回る箱!
- イベントリスナ
- 関数init
- サイズを指定
- レンダラーを作成
- シーンを作成
- カメラを作成
- 箱を作成
- ループイベント(アニメ)
- あらためて復習
- まとめ
Three.jsとは
WebGLの定番ライブラリに「three.js」がありんす。名前の由来は3Dの3と思われる。
公式サイトはこちら。サンプルみるだけでオラワク*1ですね!
※参考:three.js – JavaScript 3D library
「生JSとライブラリとフレームワークの理解」で書いたようにライブラリにはあらかじめ便利なメソッドがたくさん定義されています。
そういえばこの記事の後半でもThree.jsのコードを引用していますね。Three.js本体ファイルを読み込むと、このPerspectiveCamera()
、WebGLRenderer()
などの便利メソッドを実行できるわけです。
three.jsの読み込み
さっそく初めてみる。公式サイト の左メニュー「download」からファイル一式がダウンロードできるんだけども、CodePenはあらかじめthree.jsのCDN*2 の設定が用意されております!
- 新規投稿画面(New Pen)。「Setting」をクリック
- 「JavaScript」タブをクリック
- 一番下の「QuickAdd」を開く
- 「Three.js」を選択
- CDNのthree.jsファイルがリンクされます。
- 最後に「Save & Close」をクリック!
どーですか!とっても・カン・タン・だ!って感じでしょう。さー準備は整った。時がきた!それだけだ。あとはやるだけだ!
Three.js入門サイトにいざ入門!
WebGLで3Dを表現するには、2Dで絵を描くのに比べて一筋縄ではいかないことがわかってきました。かい摘んでもざっとこれだけの工程があります。
- モデリング:造形
- マテリアル:色や表面の質感
- ライティング:光の角度、強さ
- カメラ:カメラの位置
- モーション:動き
どんな素敵な造形をしてもカメラやライトを設定しないとまっ暗な倉庫にあるのと同じわけですな。うーむ、こりゃなかなか独学では難しそうです。
そこで!いつもお世話になっているICS MEDIA 内の連載「Three.js入門サイト」に、私も入門をしたく思います!押忍!!
サンプル再現!回る箱!
まずは最初の「入門編」のページから取り組もうと思います。サンプルを開くと…おお!箱が回っているゾ!
※参考:簡単なThree.jsのサンプルを試そう - ICS MEDIA
>まずはHTMLファイルを用意して、次のコードを貼り付けて試してください。
実際に貼ってみた。
おお!ここでも箱が回ってる!!
<canvas id="myCanvas"></canvas>
HTMLの方はたったの1行。canvas
要素があって#myCanvas
というid名がついてますね。
そしてCSSは記述全くなし!すべてはJSに書かれているようです。
JSはこんなです。
// ページの読み込みを待つ window.addEventListener('load', init); function init() { // サイズを指定 const width = 960; const height = 540; // レンダラーを作成 const renderer = new THREE.WebGLRenderer({ canvas: document.querySelector('#myCanvas') }); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(width, height); // シーンを作成 const scene = new THREE.Scene(); // カメラを作成 const camera = new THREE.PerspectiveCamera(45, width / height); camera.position.set(0, 0, +1000); // 箱を作成 const geometry = new THREE.BoxGeometry(400, 400, 400); const material = new THREE.MeshNormalMaterial(); const box = new THREE.Mesh(geometry, material); scene.add(box); tick(); // 毎フレーム時に実行されるループイベントです function tick() { box.rotation.y += 0.01; renderer.render(scene, camera); // レンダリング requestAnimationFrame(tick); } }
…いきなりこれだけ見てもちょっとわかんないですね、はい。分解して見ていきませう。
イベントリスナ
まず最初のところ。コメントには「ページ読み込みを待つ」とある。
// ページの読み込みを待つ window.addEventListener('load', init);
まずwindow
。これは「JSの基本-前編」でも書いたように、ブラウザ全体を表す最上位のオブジェクトです。
その次のaddEventListener()
は「JSの基本-後編」でも書いたイベントリスナ。「第1引数の時に第2引数する」という意味になる。
で、第1引数はload
で「読み込んだ時」、第2引数のinit
は関数名。イベントリスナで読み込む関数名にはカッコが付かないんでしたね。
まとめると「ブラウザを読み込んだ時に、関数init
を実行する!」という意味になります。ページが読み込み終わったあとに実行させたいということですね。
関数init
そのあとには関数init()
の設定が書かれています。外側を見るとこんな感じ。
//関数init function init() { 処理内容1、2、3〜〜〜 }
この処理内容の頭にコメントが付いているのでそれをかい摘むとこんなです。
//関数initの中身 function init() { // サイズを指定 // レンダラーを作成 // シーンを作成 // カメラを作成 // 箱を作成 // 毎フレーム時に実行されるループイベントです }
では、関数initの中身を一つずつ見て行きませう。
サイズを指定
一つ目のコメント「サイズを指定」の中身はこんなです。
// サイズを指定 const width = 960; const height = 540;
const
というのは変数の一種です。お馴染みvar
*3、let*4に続く第3の変数。巻き上げもできなければ、再代入(値上書き)もできません。
「変数width
に960、変数height
に540」という値を入れ、この値は絶対的な固定値になります。
そしてこの値は画面のサイズに違いないですな。きっと…。
レンダラーを作成
次「レンダラーを作成」を見てきましょう。WebGLで書いたように「レンダリング」とはデータ情報を画像や映像などに変換することでしたね。
// レンダラーを作成 const renderer = new THREE.WebGLRenderer({ canvas: document.querySelector('#myCanvas') }); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(width, height);
またconst
で変数renderer
(レンダラー)を定義しています。
//変数rendererを定義 const renderer = 値
new
で予めThree.jsに用意されたTHREE
オブジェクトを複製してます。これによって変数renderer
はTHREE
オブジェクトの「インスタンス」になります。
//THREEオブジェクトのインスタンス作成 const renderer = new THREE.処理〜
※参考:【JS】newとプロトタイプとクラス - クモのようにコツコツと
そこにWebGLRenderer()
というメソッドが続きます。
//THREEオブジェクトのインスタンス作成 const renderer = new THREE.WebGLRenderer(引数)
WebGLRenderer()
メソッドの中には波括弧{}
で連想配列が入ってますね。
//THREEオブジェクトのインスタンス作成 const renderer = new THREE.WebGLRenderer({ //連想配列 プロパティ:値 })
canvas
プロパティの値はDOMのdocument.querySelector(セレクタ)
です。DOMのセレクタはHTMLにある#myCanvas
です。
//THREEオブジェクトのインスタンス作成 const renderer = new THREE.WebGLRenderer({ canvas: document.querySelector('#myCanvas') })
これで、renderer
インスタンスと#myCanvas
セレクタを紐付けているわけです。
その下には インスタンスrenderer
のsetPixelRatio()
、setSize()
の二つのメソッドを実行しています。どちらもTHREE
オブジェクトに予め定義されているメソッドのようです。
//変数rendererのメソッドを実行 renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(width, height);
setPixelRatio()
の引数はwindow.devicePixelRatio
。「WindowプロパティのdevicePixelRatioは現在のディスプレイにおけるCSS解像度と物理解像度の比を返します。」とのこと。
※参考:Window.devicePixelRatio - Web API | MDN
setSize()
は引数が2つありwidth, height
。「サイズを指定」の変数の値(960、540)が入りますね。
シーンを作成
「シーンを作成」のコードを見ていきましょう
// シーンを作成 const scene = new THREE.Scene();
一行ですね。変数scene
をnew
でTHREE
オブジェクトのインスタンスにして、Scene()
(シーン)メソッドが入ります。次っ!
カメラを作成
次は「カメラを作成」です。
// カメラを作成 const camera = new THREE.PerspectiveCamera(45, width / height); camera.position.set(0, 0, +1000);
変数camera
をTHREE
のインスタンス化し、PerspectiveCamera()
メソッドが入ります。第1引数は画角で45
度、第2引数は縦横比でwidth / height
(960 / 540)。
※参考:写真用語集 - 画角 - キヤノンイメージゲートウェイ
その下では、camera
インスタンスのposition
プロパティのset()
メソッド。引数は3つで0, 0, +1000
。カメラを置く位置のようです。ここを打ち変えると位置がかわりそうです。
箱を作成
いよいよ立方体の部分ですね。
// 箱を作成 const geometry = new THREE.BoxGeometry(400, 400, 400);
まず変数geometry
をTHREE
インスタンス化し、BoxGeometry()
メソッドを入れています。このメソッドは箱型のジオメトリ(立体)とのこと。引数は3つで400, 400, 400
で、幅、高さ、奥行きですね。
const material = new THREE.MeshNormalMaterial();
次に変数material
もTHREE
のインスタンス化してMeshNormalMaterial()
メソッドを入れています。MeshNormalMaterial()
は予め用意されたマテリアルで、ライトを設定しなくても形状が見えるそうです。
※参考:Three.jsのさまざまなマテリアル - ICS MEDIA
const box = new THREE.Mesh(geometry, material);
同様に変数box
にTHREE
のインスタンスのMesh()
メソッドが入っています。引数は2つで上のgeometry
とmaterial
が入ります。
scene.add(box);
最後にscene
インスタンスのadd()
メソッドを実行。中の引数にはbox
インスタンスを代入し、シーンに箱を追加しています。
ループイベント(アニメ)
その次に唐突にtick()
メソッドが書かれていますね。このメソッドはすぐ下に定義されています。
tick(); function tick() { //処理内容 }
処理内容の中身はこんなです。
box.rotation.y += 0.01;
box
インスタンスのrotation
プロパティのy
に0.01
を加算。rotation
はCSSにもあるように、回転ですね。「箱が0.01回転する」という意味。
renderer.render(scene, camera); // レンダリング
次にrenderer
インスタンスのrender()
メソッドを実行。引数は2つでscene
とcamera
が入ります。「シーンとカメラをレンダリング」という意味。
requestAnimationFrame(tick);
最後の処理はrequestAnimationFrame()
メソッド。名前の通りフレームアニメーションですね。引数はには関数tick
自体が入ります。
まとめると「フレームごとに箱を0.01回転させて、シーンとカメラにレンダリングする」という意味になります。
これで、箱が回転するアニメーションになったわけですね。
あらためて復習
では、もう一回コードを見ていきませう。
// ページの読み込みを待つ window.addEventListener('load', init); function init() { // サイズを指定 const width = 960; const height = 540; // レンダラーを作成 const renderer = new THREE.WebGLRenderer({ canvas: document.querySelector('#myCanvas') }); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(width, height); // シーンを作成 const scene = new THREE.Scene(); // カメラを作成 const camera = new THREE.PerspectiveCamera(45, width / height); camera.position.set(0, 0, +1000); // 箱を作成 const geometry = new THREE.BoxGeometry(400, 400, 400); const material = new THREE.MeshNormalMaterial(); const box = new THREE.Mesh(geometry, material); scene.add(box); tick(); // 毎フレーム時に実行されるループイベントです function tick() { box.rotation.y += 0.01; renderer.render(scene, camera); // レンダリング requestAnimationFrame(tick); } }
ページの読み込みを待つ
- ページを読み込んだら関数
init
を実行 - 関数
init
を定義
サイズを指定
- サイズを幅
960
、高さ540
に指定
レンダラーを作成
renderer
インスタンスで#myCanvas
をレンダラーにrenderer
インスタンスに画像解像度を返すrenderer
インスタンスにサイズをセット
シーンを作成
scene
インスタンスにシーンを作成
カメラを作成
camera
インスタンスにPerspectiveCamera()
メソッドで画角と縦横比を設定camera
インスタンスの位置をセット
箱を作成
geometry
インスタンスに高さ400、幅400、奥行き400の箱を作成material
インスタンスにMeshNormalMaterial()
でマテリアル設定box
インスタンスにgeometry
とmaterial
の設定値を入れるscene
インスタンスにbox
インスタンス(箱)を入れる
ループイベント(アニメ)
- 関数
tick
を実行 - 関数
tick
を定義 box
インスタンスを0.01回転renderer
インスタンスにシーンとカメラをレンダリングrequestAnimationFrame()
メソッドでフレームごとに関数tick
を実行
どうでしょうか。最初より読めてきたでしょうか。
まとめ
うーむ、箱をシーンに置いて回転させるだけでも随分とコードがあり、パッと見ただけではなかなか内容がイメージできないので、細かく見ていきました。
それでもライブラリを使わないともっともっと膨大なコードになると思われます。無からコードだけで3Dアニメが出現するのはなんとも嬉しい体験です。
3Dの道は一日にしてならず。引き続きよろしくお願いしまっす!