Three JS:dat.GUIを使ってTorusKnotGeometryを極めてみる
前回のジオメトリー紹介では複雑なTorusKnotGeometryのプロパティがいまいち理解できませんでした。なので今回はブラウザ上でプロパティの値を動的に変更できるdat.GUIを使用して、理解度を深めようと思います。
前回の記事:Three JS:基本の使い方総まとめ-ジオメトリー後編-
ちなみに現状の各ライブラリのバージョンは以下の通りです。
- three.js: r74
- jQuery:1.9.1
dat.GUIとは
dat.GUIとはJavaScript内の値をブラウザ上のパラメーターで変更することにより、動的に反映させることの出来るJavaScriptライブラリです。Three JS公式サイトのデモページでもよく使われているツールです。
使い方自体はシンプルですのでdat.GUI公式サイトを参考にして下さい。この公式サイトからもダウンロードは出来ますが、Three JSに付属していますのでそちらを使いましょう。
Three JSダウンロード:http://threejs.org/
場所はexamples/js/libs/dat.gui.min.jsです。
簡単に使い方を説明します。
まずはパラメーター用のデータを保持するコンストラクタを作成します。
1 2 3 4 5 6 7 | //GUIパラメータの準備 var hogeCtrl = function(){ this.hoge = 5; this.hogi = 2; this.hogehoge = 80; this.hogihogi = 60; }; |
先ほどのコンストラクタからインスタンスを生成し、操作したいパラメーターのフォルダ(タブ)と紐付けます。
1 2 3 4 5 6 | var gui = new dat.GUI(); hogeObj = new hogeCtrl();//先ほどのコンストラクタからインスタンス生成 var folder = gui.addFolder('フォルダの表示名');//新規フォルダ(タブ)作成 //folder.add( データを保持するインスタンス, 'インスタンスのプロパティ名', 最小値, 最大値 ).onChange( 変更時のイベント); folder.add( hogeObj, 'hoge', 1, 20 ).onChange( setHogeVal); folder.add( hogeObj, 'hogi', 0.1, 10 ).onChange( setHogeVal); |
変更があった時のイベントを作ります。すでに生成したインスタンスにはパラメーターで変更した値が反映されていますので、あとは変更したい値に代入すれば完了です。
1 2 3 4 5 | function setHogeVal(){ //更新処理をここに書きます some.hoge = hogeObj.hoge; some.hogi = hogeObj.hogi; } |
DEMO
何はともあれ、まずはデモをご覧ください。
それでは早速下の画像をクリック!!右上のタブを選ぶとパラメーターが表示されます。それこそがdat.GUIなのです。色々といじって値を変更してみてください。
TorusKnotGeometryを理解する
前回はいまいち掴めなかったTorusKnotGeometryもdat.GUIを使用することにより、視覚的に理解することが出来そうです。
ちなみにTorusKnotGeometryの各プロパティは以下の通りです。
- radius:半径,
- tube:管の太さ,
- radialSegments:円周のセグメント数
- tubularSegments:管周りのセグメント数
- p:周回数
- q:管のねじれ,
- heightScele:高さのスケール
p,qを変化させることにより、とても面白い形状になりました。私のお気に入りはこのデリシャスプレッツェル型!!
ソースコード
念の為、今回使用したソースコードを掲載いたします。使用しているライブラリはjQueryと、Three JSからは以下のファイルを読み込んでいます。
ファイル名 | 場所 | 役割 |
three.min.js | /build/ | Three JSメインファイル |
Detector.js | /examples/js/ | WebGL環境確認 |
OrbitControls.js | /examples/js/controls/ | マウスコントロール用 |
dat.gui.min.js | /examples/js/libs/ | dat.GUIパラメーター用 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | (function($){ var canW = window.innerWidth; //canvas横:任意値 var canH = window.innerHeight; //canvas縦:任意値 var scene,camera,renderer,controls,geoObj,mesh; function init(){ if(!Detector.webgl)Detector.addGetWebGLMessage();//WebGL環境確認 var gui = new dat.GUI(); scene = new THREE.Scene(); // カメラ:透視投影 camera = new THREE.PerspectiveCamera( 60, canW / canH, 0.001, 2000); scene.add(camera); camera.position.set( 0, 20, 40); // レンダラー renderer = new THREE.WebGLRenderer({antialias: true}); renderer.setSize(canW,canH); renderer.setPixelRatio( window.devicePixelRatio); renderer.shadowMap.enabled = true; document.body.appendChild( renderer.domElement ); // ジオメトリー var material = new THREE.MeshLambertMaterial({color: 0xF99845}); mesh = new THREE.Mesh(); mesh.material = material; mesh.position.set(0,10,0); mesh.castShadow = true; scene.add(mesh); // パラメーター設置 geoObj = new geoCtrl(); var folder = gui.addFolder('TorusKnotGeometry') folder.add( geoObj, 'radius', 1, 20 ).onChange( setGeoVal); folder.add( geoObj, 'tube', 0.1, 10 ).onChange( setGeoVal); folder.add( geoObj, 'radialSegments', 3, 160 ).step( 1 ).onChange( setGeoVal); folder.add( geoObj, 'tubularSegments', 3, 80 ).step( 1 ).onChange( setGeoVal); folder.add( geoObj, 'p', 1, 20 ).step( 1 ).onChange( setGeoVal); folder.add( geoObj, 'q', 1, 20 ).step( 1 ).onChange( setGeoVal); folder.add( geoObj, 'heightScale', 1, 20 ).onChange( setGeoVal); setGeoVal(); // 地面 var plane = new THREE.Mesh( new THREE.PlaneGeometry( 100, 100), new THREE.MeshLambertMaterial({color: 0xFFFFFF}) ); plane.rotation.set( -Math.PI/2, 0, 0); plane.position.set( 0, -1, 0); plane.receiveShadow = true; scene.add(plane); // 自然光 var ambientLight = new THREE.AmbientLight( 0xDDDDCC, 0.8); // スポットライト var spotLight = new THREE.SpotLight(0xFFFFFF,1.2,0); spotLight.castShadow = true; spotLight.position.set( 10, 30, 30); scene.add(ambientLight,spotLight); //コントローラー controls = new THREE.OrbitControls(camera); controls.maxDistance = 100; controls.maxPolarAngle = Math.PI * 0.48; //軸 var axis = new THREE.AxisHelper( 50 ); scene.add( axis); resizeSet(); setTimeout(resize, 1); rendering(); } //GUIパラメータの準備 var geoCtrl = function(){ this.radius = 5; this.tube = 2; this.radialSegments = 80; this.tubularSegments = 60; this.p = 3; this.q = 2; this.heightScale = 5; }; function setGeoVal(){ mesh.geometry.dispose(); mesh.geometry = new THREE.TorusKnotGeometry( geoObj.radius, geoObj.tube, geoObj.radialSegments, geoObj.tubularSegments, geoObj.p, geoObj.q, geoObj.heightScale ); } function rendering(){ requestAnimationFrame(rendering); controls.update(); renderer.render( scene, camera); } function resizeSet(){ var queue = null; // キューをストック var wait = 300; // 0.3秒後に実行の場合 window.addEventListener( 'resize', function() { clearTimeout( queue ); queue = setTimeout(function() { resize(); }, wait ); }, false ); }; function resize(){ var width = window.innerWidth; var height = window.innerHeight; camera.aspect = width / height; camera.updateProjectionMatrix(); renderer.setSize(width, height); } $(function(){ init(); }); })(jQuery); |
ハイライト箇所に注意してください。今回はジオメトリーのプロパティを変化させたいのですが、Three JSで用意されているジオメトリー各種のプロパティの値は動的に変化させることは出来ません。なので一度despose();で破棄してから、新しくジオメトリーを生成して、再度メッシュに入れ込んでおります。
まとめ
3Dの描画はとても複雑で、コードだけでは理解できない事も多いです。なので今回紹介したdat.GUIは視覚的に理解できるので開発段階で大変役に立ちます。カメラの位置やライトの設定、マテリアルの色変更などなどどんなプロパティでも紐付けられるのでこれからどんどん使っていこうと思います。
もちろんdat.GUIはThree JS専用のものではないので、色んなライブラリと組み合わせられます。何となくAngular JSを彷彿させますね。