Three.js:初心者がWebGLプログラミングを0から学ぶ[第一回]
どーも、中野区でホームページ作成やってます。@PNRAです!
プログラミングずぶの素人がWebGLプログラミングをマスターしてリッチな表現をマスターしてドヤ顔するための不定期連載を始めたいと思います。時間を見つけて更新していくので、適宜チェックしていただけると幸いです。
まずはWebGLとはなんぞや?という準備の準備から始めていきたいと思います。
WebGLとは?
WebGLとは、ブラウザ上で3DCGプログラミングを表示させるための技術のこと。Javascriptの拡張技術として提供されており、HTML Canvasタグで定義されたページ上の領域にリッチコンテンツを表現することができるとして、近年注目が集まってきている技術。
立体コンテンツを表現するためのものなのか〜ぐらいの認識からスタートしていますが、実際にサイトでの活用例も多くでてきているので、まずはいくつかピックアップ
The Carp and The Seagull
Genealogy of Nike Free
G Star RAW
フラットデザインとかマテリアルデザインとか、色々とウェブデザイン論はあると思いますが、そういうの抜きにして、かっこいいです。
従来のウェブデザインでは、ブラウザという平面上をどう色付けするか、数多くのブラウザに対応するために、デザイン的な制約を多く受けた上だったため、同じ平面のデザインでも「紙面デザイン」>「ウェブデザイン」のようなところが会ったように思いましたが、ここにきてウェブデザインが独自の表現を持ち始めたといっていいかもしれません。
紙面では表現できない動的な表現とテレビでは実現できないインタラクティブ性、これらを備えたウェブという語り部が生まれたように思います。
少し脱線してしまいましたが、最終的にはこれらのサイトのような動きを実装するところまではマスターしたいと思います。
最初にしては少しハードルが高い例を出してしまったので、次はもうちょっと手前によせていきたいと思います。
WebGL用のJavascriptライブラリーとして最も人気のある「three.js」のサンプルデモを見てみましょう。
WebGL shaders vector
css3d youtube
canvas lines sphere
これでも充分、いままでの表現とは一線を画した見せ方ができそうですね!
さらっと「three.js」に触れましたが、もう少し詳しくみようと思います。
どのように実装する?
WebGLそのものはJava scriptの拡張技術のため、基本的な記載はJava script言語をベースにおこないます。ただし、偉そうに説明している私自身Java scriptに関しては全くの素人。何から手をつけていいかも分かりません。そこで比較的簡単にWebGLを実装するために用意されたJava scriptライブラリーである「three.js」「Away3D.js」「Babylon.js」を用いてなんとかしていきたいと思います。
特にWebGL用のJavascriptライブラリーとしては、「three.js」が最も人気がある(コンポーネントも揃っている)ので、こちらをベースに作成していきたいと思います。
早速three.jsをダウンロードする
「three.js」は公式サイト( http://threejs.org/ )からダウンロードすることができます。
上記URLから公式サイトに飛び、「download」をクリックしてZIPファイルを取得します。
ZIPファイル解凍すると、複数のファイルが格納されているのが確認できます。
この中で必要なファイルを取り出して、実装の準備をしていきたいと思います。
必要なファイルは「three.min.js」のみなので、構成ファイル内の buildフォルダから「three.min.js」をコピー&ペーストで自身のサーバーに移しましょう。
これでやっとスタートラインに立った気がします。
理解を深めていくためには、どういった指示をどのように与えてどうやってリッチコンテンツを描写していくのか?といった内容をみていかなければなりません。
が、まずはそれをすっ飛ばして実例を元にいじくって理解を進めようという横暴なやり方をとりたいと思う初心者的発想の持ち主なので、最初に紹介した「canvas lines sphere」をとりあえずまるっとコピーして考えてみましょう。
canvas lines sphere DEMO
//script内容
<script src="build/three.min.js"></script>
<script>
var SCREEN_WIDTH = window.innerWidth,
SCREEN_HEIGHT = window.innerHeight,
mouseX = 0, mouseY = 0,
windowHalfX = window.innerWidth / 2,
windowHalfY = window.innerHeight / 2,
SEPARATION = 200,
AMOUNTX = 10,
AMOUNTY = 10,
camera, scene, renderer;
init();
animate();
function init() {
var container, separation = 100, amountX = 50, amountY = 50,
particles, particle;
container = document.createElement('div');
document.body.appendChild(container);
camera = new THREE.PerspectiveCamera( 75, SCREEN_WIDTH / SCREEN_HEIGHT, 1, 10000 );
camera.position.z = 500;
scene = new THREE.Scene();
renderer = new THREE.CanvasRenderer();
renderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
container.appendChild( renderer.domElement );
// particles
var PI2 = Math.PI * 2;
var material = new THREE.SpriteCanvasMaterial( {
color: 0xffffff,
program: function ( context ) {
context.beginPath();
context.arc( 0, 0, 0.5, 0, PI2, true );
context.fill();
}
} );
for ( var i = 0; i < 1000; i ++ ) {
particle = new THREE.Sprite( material );
particle.position.x = Math.random() * 2 - 1;
particle.position.y = Math.random() * 2 - 1;
particle.position.z = Math.random() * 2 - 1;
particle.position.normalize();
particle.position.multiplyScalar( Math.random() * 10 + 450 );
particle.scale.multiplyScalar( 1 );
scene.add( particle );
}
// lines
for (var i = 0; i < 300; i++) {
var geometry = new THREE.Geometry();
var vertex = new THREE.Vector3( Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1 );
vertex.normalize();
vertex.multiplyScalar( 250 );//広がり距離
geometry.vertices.push( vertex );
var vertex2 = vertex.clone();
vertex2.multiplyScalar( Math.random() * 0.2 + 1 );//直線長さ
geometry.vertices.push( vertex2 );
var line = new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0xffffff, opacity: Math.random() } ) );
scene.add( line );
}
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
document.addEventListener( 'touchstart', onDocumentTouchStart, false );
document.addEventListener( 'touchmove', onDocumentTouchMove, false );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
//
function onDocumentMouseMove(event) {
mouseX = event.clientX - windowHalfX;
mouseY = event.clientY - windowHalfY;
}
function onDocumentTouchStart( event ) {
if ( event.touches.length > 1 ) {
event.preventDefault();
mouseX = event.touches[ 0 ].pageX - windowHalfX;
mouseY = event.touches[ 0 ].pageY - windowHalfY;
}
}
function onDocumentTouchMove( event ) {
if ( event.touches.length == 1 ) {
event.preventDefault();
mouseX = event.touches[ 0 ].pageX - windowHalfX;
mouseY = event.touches[ 0 ].pageY - windowHalfY;
}
}
//
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
camera.position.x += ( mouseX - camera.position.x ) * .05;
camera.position.y += ( - mouseY + 200 - camera.position.y ) * .05;
camera.lookAt( scene.position );
renderer.render( scene, camera );
}
</script>
ふむ、さっぱり分かりません。(笑)
Javascriptを使っているだけあって、記述の後ろほうに条件式が入ってたりしているので、なんとなく分かりそうな気もしますがいまいち分からないですね。
ということで、次回からはこの内容を読み解いていく作業をしていきたいと思います。
今日はとりあえず、WebGLを使うと表現の幅が飛躍的に広がるな。ということが理解できただけでもよしとしましょう。
Siyabonga ekufundeni kwakho.(訳:最後まで読んでくれてありがとう。 / 注:ズールー語)