WebGLはウェブページに3D表現を組み込むための技術です。そのWebGLを扱いやすくしたJSライブラリの「Three.js」。Theee.jsとシェーダー言語GLSLを組み合わせてプリンが揺れるデモを作成しました。本記事では、Three.jsでシェーダー言語を利用する手順を解説します。
使用技術について
GLSLとはOpenGL Shading Languageの略でその名の通り、OpenGL(WebGL)で使用できるシェーディング言語です。WebGLではライブラリを使用しない場合は、このシェーディング言語を使用してシェーダー(3D描画のための一連の計算セット)を自力で作成しなければなりません。WebGLのシェーダーは2種類あり、バーテックスシェーダーでは頂点の情報を画面上に反映し、フラグメントシェーダーではピクセル単位での描画を行います。今回はバーテックスシェーダーで頂点の位置をずらしてプリンの揺れを表現します。
Three.jsはJavaScriptで扱える3DのJavaScriptライブラリです。Three.js自体の基本的な使い方自体は公式サイト「Three.js - documentation」にあり、説明自体は英語ですがソース部分をコピーペーストするだけで簡単に3D空間が作成できます。
デモ
※画面内をクリックすると、プリンが揺れます
シェーダーのロード
シェーダーはテキストで記述するためJSに直接記入することが可能です。ですがファイルが分かれているとソースが綺麗に保てるのでメインのJavaScriptファイルとは別にしています。シェーダーの読み込みのために「Shader Loader」というライブラリを使用してみました。また、Shader LoaderはjQueryを必要とするのでjQueryも使用しています。
▼ index.html
▼ main.js
シェーダーファイルの読み込み
function preload() {
// シェーダーファイルのプリロード
SHADER_LOADER.load(
// ロード完了後のコールバック関数
function(data) {
// 'myShader'は'index.html,scriptタグのdata-name'で指定したもの
// バーテックスシェーダー
var vs = data.myShader.vertex;
// フラグメントシェーダー
var fs = data.myShader.fragment;
// Three.jsの初期化処理を行う
setUp(vs, fs);
}
);
}
バーテックスシェーダー
バーテックスシェーダーでは3D空間上に配置された頂点が画面上のどの位置にあるかを計算します。このシェーダー上では、頂点の情報を参照・変更することが可能です。
プリンを揺らす前の計算
バーテックスシェーダーの必要最低限の計算は以下となります。頂点位置(position
)をモデル行列(modelViewMatrix
)でワールド座標に変換し、ビュー行列(projectionMatrix
)でスクリーン座標に変換します。この計算を基準としてプリンを揺らす処理を記述します。
void main(){
gl_Position = projectionMatrix * modelViewMatrix * position;
}
プリンの揺らし方
頂点位置に当たるposition
を調整します。時間の経過によって揺れを表現しますが、シェーダー内ではコンテンツの経過時間のような外部の変数を保持することができないため、各フレーム毎にその値を送る必要があります。外部から取り込むシェーダー内の共通の値はuniform
という識別子をつけてThree.js経由で値を送ります。今回はframe
(時間の経過)、modelHeight
(3Dモデルの高さ)、swingVec
(揺れの方向)、swingStrength
(揺れの大きさ)をThree.js経由で送っています。
上記図を見ながら揺れの計算を説明します。
- まず、プリン型の3Dモデルの
{x:0,y:0,z:0}
の位置は中央のため底の位置を0として合わせるためにmodelHeight/2
(3Dモデルの高さの半分)」分ずらします。また、頂点位置の情報を0
〜1.0
の値へ合わせると後ほど計算しやすいので「modelHeihgt」分で割ります。 - 揺れの大きさを決めます。
swingStrength
に1.で決めた高さの0〜1.0
で合わせた値で掛けます。そうすると、底部分が0の為どんな値を掛けても0のまま固定され、上部になるほど揺れが大きくなるといった動きを表現できます。 - 揺れの早さの指定を行っています。
sin()
関数を使用し、波の形で揺れの形を指定しています。frame
の値が早く大きくなるほど揺れの振動が早くなります。具体的にはsin()
の周期が-1π〜1π
で1周期なので2π
分時間が経つと揺れが一番上まで到達します。図の3では1π
分上に移動しています。 - 3Dモデル内の揺れの個数の計算をしています。揺れの個数とは3Dモデル内で何度振動があるかのことです。
positionNormalized(高さ0〜1.0の値) * waveNum(揺れの個数) * PI * 2
と計算しています。この値をsin()
関数に入れているので波の形はsin(0〜2π)
で表せられるカーブの形になっています。waveNum
を1
にし最後に掛け合わせるstrength
の値を10
と大きめに設定すると下記図5のような形になります。waveNumは大きめの値を入れるとぐにゃぐにゃと揺れて楽しいです。
- 最後に揺れの方向を決定します。
swingVec
というvec3
形式の値を今までの計算で得た値に掛けあわせます。この方向を変えると揺れる形も変わっていきます。たとえばデモで一番最初に落ちてくるときのswingVec
は縦方向に揺らすため{x:0,y:1,z:0}
になっています。
▼ vertex.js
// Three.jsから送られた値
uniform float frame;
uniform float modelHeight;
uniform vec3 swingVec;
uniform float swingStrength;
void main(){
//PIは定義されていないようなので自分で定義する
const float PI = 3.14159265359;
float waveNum = 2.;
// 1.位置を0〜1.0の位置に合わせる
float fit0Position = position.y + modelHeight/2.0;
float positionNormalized = fit0Position / modelHeight;
// 2.揺れ幅の調整を行う
float strength = swingStrength * positionNormalized;
// 3.揺れの早さ(frame) 4.3Dモデル内の揺れの個数を指定する(positionNormalized * waveNum * PI )
float wave = sin(frame + positionNormalized * waveNum*PI) * strength; //
// 5.新しい頂点位置の生成
vec3 newPosition = position + (swingVec * wave);
...
// 頂点位置の出力
gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
}
3Dモデルにシェーダーを適用する
3DモデルへはShaderMaterial
というマテリアルを指定することで自作したシェーダーを適用することができます。Three.jsのShaderMaterial
経由でシェーダーのテキスト、uniform
等を指定します。
var uniformsPudding = {
frame: {
type: "f", // float型
value: 0.0
},
modelHeight: {
type: "f", // float型
value: 5
},
swingVec: {
type: "v3", // Vector3型
value: swingVec
},
swingStrength: {
type: "f", // float型
value: swingStrength
}
};
function setUp(vs,fs) {
// ...
// ShaderMaterialの作成
shaderMaterialPudding = new THREE.ShaderMaterial({
uniforms:uniformsPudding,
// シェーダーを割り当てる
fragmentShader: fs,
vertexShader: vs,
});
メッシュ作成時にシェーダーマテリアルを指定します。
function createPudding() {
cylinderGeometry = new THREE.CylinderGeometry(
8.4,
11.2,
11.2,
100,
30
);
cylinder = new THREE.Mesh(
cylinderGeometry,
shaderMaterialPudding
);
cylinder.position.set(0, 21, 0);
return cylinder;
}
おわりに
今回の記事ではバーテックスシェーダーで3Dモデルの各頂点の位置をずらすことで揺れを表現してみました。この方法を用いると3Dモデルをさまざまな形に変形させることができます。みなさんも一緒にGLSLを使って表現力アップしてみませんか?