GLSLを使ってワンランク上の表現を! Three.jsでのぷるぷるシェーダーの作り方

33
30

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入門」にあり、ソース部分をコピーペーストするだけで簡単に3D空間が作成できます。

デモ

※画面内をクリックすると、プリンが揺れます

シェーダーの書き方

シェーダーであるGLSL言語はテキストで記述します。JavaScript内には文字列としてプログラムを記載します。バッククオートを使いテンプレート文字列を利用してプログラムを文字列として記述します。

shaderFragment.js

ソースコードを参照

main.js シェーダーファイルの読み込み

// language=GLSL
export const shaderFragment = `
    // バーテックスシェーダーから送られた値
    varying vec3 vNormal;
    varying vec3 mvPosition;
    varying vec2 vUv;

    // ・・・以下省略
    `:

WebStorm等のIDEであれば、プラグインを追加することでテンプレート文字列内のGLSL言語をシンタックスハイライトがされた状態で扱えます。

バーテックスシェーダー

バーテックスシェーダーでは3D空間上に配置された頂点が画面上のどの位置にあるかを計算します。このシェーダー上では、頂点の情報を参照・変更することが可能です。

プリンを揺らす前の計算

バーテックスシェーダーの必要最低限の計算は以下となります。頂点位置(position)をモデル行列(modelViewMatrix)でワールド座標に変換し、ビュー行列(projectionMatrix)でスクリーン座標に変換します。この計算を基準としてプリンを揺らす処理を記述します。

void main(){
  gl_Position = projectionMatrix * modelViewMatrix * position;
}

プリンの揺らし方

頂点位置に当たるpositionを調整します。時間の経過によって揺れを表現しますが、シェーダー内ではコンテンツの経過時間のような外部の変数を保持することができないため、各フレーム毎にその値を送る必要があります。外部から取り込むシェーダー内の共通の値はuniformという識別子をつけてThree.js経由で値を送ります。今回はframe(時間の経過)、modelHeight(3Dモデルの高さ)、swingVec(揺れの方向)、swingStrength(揺れの大きさ)をThree.js経由で送っています。

141212_プリン説明テキスト

上記図を見ながら揺れの計算を説明します。

【手順1】まず、プリン型の3Dモデルの{x:0,y:0,z:0}の位置は中央のため底の位置を0として合わせるために modelHeight/2(3Dモデルの高さの半分)」分ずらします。また、頂点位置の情報を01.0の値へ合わせると後ほど計算しやすいので「modelHeihgt」分で割ります。

【手順2】揺れの大きさを決めます。swingStrength1.で決めた高さの0〜1.0で合わせた値で掛けます。そうすると、底部分が0の為どんな値を掛けても0のまま固定され、上部になるほど揺れが大きくなるといった動きを表現できます。

【手順3】揺れの早さの指定を行っています。sin()関数を使用し、波の形で揺れの形を指定しています。frameの値が早く大きくなるほど揺れの振動が早くなります。具体的にはsin()の周期が-1π〜1πで1周期なので分時間が経つと揺れが一番上まで到達します。図の3では分上に移動しています。

【手順4】3Dモデル内の揺れの個数の計算をしています。揺れの個数とは3Dモデル内で何度振動があるかのことです。positionNormalized(高さ0〜1.0の値) * waveNum(揺れの個数) * PI * 2と計算しています。この値をsin()関数に入れているので波の形はsin(0〜2π)で表せられるカーブの形になっています。waveNum1にし、最後に掛け合わせるstrengthの値を10と大きめに設定すると下記図5のような形になります。waveNumは大きめの値を入れるとぐにゃぐにゃと揺れて楽しいです。

141212_プリン揺れの回数

【手順5】 最後に揺れの方向を決定します。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モデルへはTHREE.ShaderMaterialというマテリアルを指定することで自作したシェーダーを適用できます。Three.jsのTHREE.ShaderMaterial経由でシェーダーのテキスト、uniform等を指定します。

const 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
  }
  // 一部抜粋
};

const shaderMaterialPudding = new THREE.ShaderMaterial({
  uniforms: uniformsPudding,
  // シェーダーを割り当てる
  vertexShader: shaderVertex,
  fragmentShader: shaderFragment,
});

メッシュ作成時にシェーダーマテリアルを指定します。

const geometry = new THREE.CylinderGeometry(
  8.4, // 天盤
  11.2, // 底面
  11.2, // 高さ
  100, // 円柱状の分割
  30, // 縦方向の分割
);
const cylinder = new THREE.Mesh(geometry, shaderMaterialPudding);

おわりに

今回の記事ではバーテックスシェーダーで3Dモデルの各頂点の位置をずらすことで揺れを表現してみました。この方法を用いると3Dモデルをさまざまな形に変形させることができます。みなさんも一緒にGLSLを使って表現力アップしてみませんか?

※この記事が公開されたのは9年前ですが、1年前の2023年2月に内容をメンテナンスしています。

野原 のぞみ

インタラクティブデベロッパー。好きな生き物はハムスター、好きな食べ物は豚汁です。ツールは使うより作りたい派。

この担当の記事一覧