「3Dプログラミング」に挑戦してみました
今回から「3Dプログラミング」に挑戦してみようといろいろと試しているのですが、思ったようにスムーズにはいかず、学ばないといけないこともたくさんあります。
前回作った「シューティングゲーム」を「3D化」したいと思っているのですが、どこまでできるかはわかりませんが、できるところまでチャレンジしてみたいと思います。
「Three.js」が動かない・・・
Javascriptの「3Dプログラミング」では、「Three.js」というライブラリを使うと良いと教えてもらったので、さっそく試してみたのですが、
「Google Chrome」で実行すると、
「Access to image at '画像ファイルパス' from origin 'null' has been blocked by CORS policy: The response is invalid.」
というエラーに遭遇し、途方に暮れていたのですが、いろいろと調べてみると「Cross Origin Resource Sharing(CORS)」という仕組みがあるらしく、セキュリティ上の理由からリソースファイルへのアクセスができない状態となっていました。
が、「Firefox」で実行すると問題なく動いてくれたので、「Firefox」を使うことにしました。
「Google Chrome」を使いたい場合は「WEBサーバー」を利用すると使えるようになるみたいですが、現在のところは「Firefox」で進めていきたいと思います。
「3Dアニメーションプログラミング」の作り方
今回は、「3D」プログラミングに慣れるために、シューティングゲームで利用した画像を使って「四角形のボックスを回す」アプリケーションを作ってみたいと思います。
今回利用するのは、「サトルマーク」と、
「自機キャラクター」と

「敵キャラクター」を

表示したいと思います。
「Three.js」の使い方
一番難しかった点は、「Three.js」のことでは無くて、「オブジェクト指向」という考え方を学ぶことでした。
あと、「Three.js」の公式サイトが全部英語なので、内容を読み解くのもかなり時間がかかって、なかなか前に進まなかったり。
毎日少しずつ理解をして、プログラムを書いての繰り返しで少しずつ前に進んでいる感じです。
今回作ったのは、

のようなボックス3つを表示するアプリケーションです。
一つ一つのボックスの「オブジェクト」を作り、ボックスに貼り付けるテクスチャ等を設定していきます。
そのためのプログラムは下記のようになります。
var texture = new THREE.TextureLoader().load( 'image1.jpg' );
var geometry = new THREE.BoxBufferGeometry( 100, 100, 100 );
var material = new THREE.MeshBasicMaterial( { map: texture } );
cube = new THREE.Mesh( geometry, material );
scene.add( cube );
「new」というキーワードの右に「作成するオブジェクトの名前」を書いて、作成したオブジェクトを変数に代入しています。
var obj = new 作成するオブジェクト名;
ここでは、「THREE」というオブジェクトの「TextureLoader」「BoxBufferGeometry」「MeshBasicMaterial」「Mesh」メソッドの返り値が「texture」「geometry」「material」「cube」にそれぞれ代入されています。
オブジェクト指向ではこのような形のプログラムの書き方に慣れていく必要がありそうです。
最後の「scene.add」は描画内容に四角形を追加するという意味があり、sceneも画面全体を表すオブジェクトが入っています。
「オブジェクト」が相互に連携し合って、プログラムが動いている様子がわかってきたので、この考え方にも日々少しずつ慣れてきているのを感じます。
それにしても「Three.js」はかなり機能が豊富なので、サンプルを見ているだけでもかなりワクワクする内容のものがたくさんありました。
本格的な「3Dアプリケーション」を作るためにはまだまだ勉強が必要ですが、確実に「できることが広がっていく」のを感じることができるので、「やりがい」はかなりありますが、この先にもさまざまな難関がありそうな気もしています。
サンプルアプリケーションの作成
今回作成したプログラムの全体像は下記のようになります。
var drawWidth = 850; // 描画領域(横)
var drawHeight = 450; // 描画領域(縦)
var camera;
var scene
var renderer;
var cube, cube2, cube3;
var controls;;
init();
update();
// 初期化処理
function init() {
// Camera
camera = new THREE.PerspectiveCamera( 70, drawWidth / drawHeight, 1, 10000 );
camera.position.x = 200;
camera.position.y = 0;
camera.position.z = 800;
// Control
controls = new THREE.OrbitControls(camera);
controls.autoRotate = true;
scene = new THREE.Scene();
// Ground
var geometry = new THREE.PlaneBufferGeometry( 2000, 2000, 2000 );
var material = new THREE.MeshBasicMaterial( {color: 0xffffff, side: THREE.DoubleSide} );
var ground = new THREE.Mesh( geometry, material );
ground.rotateX( Math.PI / 2 );
ground.position.x = 300;
ground.position.y = -200;
scene.add( ground );
// Cude1
var texture = new THREE.TextureLoader().load( 'image1.jpg' );
var geometry = new THREE.BoxBufferGeometry( 100, 100, 100 );
var material = new THREE.MeshBasicMaterial( { map: texture } );
cube = new THREE.Mesh( geometry, material );
scene.add( cube );
// Cube2
var texture2 = new THREE.TextureLoader().load( 'image2.jpg' );
var geometry2 = new THREE.BoxBufferGeometry( 100, 100, 100 );
var material2 = new THREE.MeshBasicMaterial( { map: texture2 } );
cube2 = new THREE.Mesh( geometry2, material2 );
cube2.position.x = 300;
scene.add( cube2 );
// Cube3
var texture3 = new THREE.TextureLoader().load( 'image3.jpg' );
var geometry3 = new THREE.BoxBufferGeometry( 100, 100, 100 );
var material3 = new THREE.MeshBasicMaterial( { map: texture3 } );
cube3 = new THREE.Mesh( geometry3, material3 );
cube3.position.x = 600;
scene.add( cube3 );
// Render
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( drawWidth, drawHeight );
document.getElementById("mainFrame").appendChild( renderer.domElement );
}
// 描画更新
function update() {
requestAnimationFrame( update );
// Cube1の回転
cube.rotation.x += 0.005;
cube.rotation.y += 0.01;
// Cube2の回転
cube2.rotation.x += 0.005;
cube2.rotation.y += 0.01;
// Cube3の回転
cube3.rotation.x += 0.005;
cube3.rotation.y += 0.01;
controls.update();
renderer.render( scene, camera );
}
画面に描画する際に必要なのが「THREE.WebGLRenderer」メソッドの返り値の「renderer(レンダラー)」です。
「レンダリング(コンピュータで画像・映像を生成すること)」を実行するものを「「renderer(レンダラー)」というのですが、ブラウザに「2D・3Dグラフィックス」を描くためのJavascript APIが「WEB GL」です。
このあたりの仕組みがよくわからず、毎日いろいろと調べながら少しずつ学んでいきました。
今後の学習
まだ、「3Dアプリケーション」を作るスキルは無いので、どんどん「3Dアプリケーション」に関するスキルを身に付けていって、簡単なものから自分で動かせるアプリケーションを作っていきたいと思います。
あと、プログラミングを学ぶには「英語学習」も必要だなと実感しました。
日本語で解説されている内容ばかりでは無いので、今後は海外のドキュメントを読めるようにならないとおそらく先に進めなくなります。
「プログラミングと英語」はセットで学ばないといけないんだな~と。
また何かプログラムが完成したら公開していきたいと思います。