ここからはGLSLを使ったパーツの紹介です。GLSLとはシェーダーを書くための言語の事です。GLSLを初めて知ったという方は記事「GLSLを使ってワンランク上の表現を!
Three.jsでのぷるぷるシェーダーの作り方
」で詳しく解説しているので参考ください。

内側グロー

inglow

球のMeshにGLSLからグラーデーションをつけ内側グローを表現します。頂点法線ベクトルとカメラの位置ベクトル向きが近くなればなるほど透過が増していく仕組みです。そのためには頂点法線ベクトルとカメラの位置ベクトルの内積を計算しその値を透明度として利用します。

以下のコードは内側グローで重要となる頂点シェーダーのコードです。

/**
 * 頂点シェーダー
 */
uniform vec3 viewVector;    // カメラ位置ベクトル
varying float opacity;      // フラグメントシェーダーに渡す透明度
void main()
{
  // 単位ベクトル化した頂点法線ベクトル
  vec3 nNomal = normalize(normal);
  // 単位ベクトル化したカメラの位置ベクトル
  vec3 nViewVec = normalize(viewVector);

  // 2つのベクトルの内積(これが透明度となる)
  opacity = dot(nNomal, nViewVec);
  // 反転
  opacity = 1 - opacity; 

  // 3次元上頂点座標を画面上の2次元座標に変換(お決まり)
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}

内積を求めるため、JavaScriptから渡されたカメラの位置ベクトルと頂点ベクトルを単位ベクトルへ変換します

// 単位ベクトル化した頂点法線ベクトル
vec3 nNomal = normalize(normal);
// 単位ベクトル化したカメラの位置ベクトル
vec3 nViewVec = normalize(viewVector);

このままだと球の中心が色が濃く周りが薄い状態になってしまうため透明度を反転させます

// 2つのベクトルの内積
opacity = dot(nNomal, nViewVec);
// 反転させる
opacity = 1 - opacity;

求めたopacityをフラグメントシェーダーへ渡し、そのまま透明度として使用すれば内側グローの完成です。

/**
 * フラグメントシェーダー
 */
uniform vec3 glowColor; // グローの色
varying float opacity;  // 頂点シェーダーから受け取る透明度
void main()
{
  gl_FragColor = vec4(glowColor, opacity);
}

フレア

flare_2

フレアはドーナツ上の単一フレアの集合により表現できます。フレアも内側グローと同様にGLSLで作成します。

上の画像のようにドーナツ状のMeshにUVアニメーションと縁の透過グラデーションを加えます。縁の透過グラデーションはパキッとした不自然な見た目になることを防ぐためです。

今回はフラグメントシェーダーでの処理が非常に重要になります。以下のコードは単一フレアを表現するためのフラグメントシェーダーです。

uniform sampler2D map;      // テクスチャーデータ
uniform float opacity;      // 透明度
uniform float topRadius;    // 外径
uniform float bottomRadius; // 内径
varying vec2 vUv;           // UV座標
varying float radius;       // 頂点位置から中心までの距離
const float PI = 3.1415926; // 円周率

void main() {
  // uvの位置情報からテクスチャーの色を取得
  vec4 tColor = texture2D(map, vUv);
  // ドーナツの辺の幅 = 円柱上面の半径 - 円柱下面の半径 
  float width = topRadius - bottomRadius;
  // 描画位置がドーナツの幅の何割の位置になるか
  float ratio = (radius - bottomRadius) / width;
  float opacity = opacity * sin(PI * ratio);
  // ベースカラー
  vec4 baseColor = (tColor + vec4(0.0, 0.0, 0.3, 1.0));
  // 透明度を反映させる
  gl_FragColor = baseColor * vec4(1.0, 1.0, 1.0, opacity);
}

頂点シェーダーから渡されるUV座標の値vUvを使い座標に対応する色をテクスチャーから取得します。

// uvの位置情報からテクスチャーの色を取得
vec4 tColor = texture2D(map, vUv);

描画位置がドーナツの辺の幅の何割の位置に相当するかを算出します。

// ドーナツの辺の幅 = 円柱上面の半径 - 円柱下面の半径 
float width = topRadius - bottomRadius;
// 描画位置がドーナツの幅のどの割合かを計算
float ratio = (radius - bottomRadius) / width;

割合をラジアン角としてsinを使って透明度を計算します。これにより端に行くほど値が0になり中央に近づけば値は1に近づくグラデーションができます。

// 割合をラジアン角としsinで透明度を計算する
float opacity = opacity * sin(PI * ratio);
// 透明度を反映させる
gl_FragColor = baseColor * vec4(1.0, 1.0, 1.0, opacity);

あとは頂点シェーダーでUV座標をずらす処理を加えてUVアニメーションをさせれば単一フレアの完成です。UVアニメーションの方法は頂点シェーダーで座標をインクリメントしてずらす簡単な手法です。詳しくはソースコードを参考ください。

スパークと同じように単一フレアを複数作成し、ランダムな回転を持たせることで以下のようなフレアの表現ができます。

flare

まとめ

本記事ではEffekseerとは全く違うアプローチであるThree.jsで同一の3D表現を実装しました。異なる方法でも作り方の大事なポイントは同じであり、得られるヒントがとても多いことに気付いてもらえたのではないかと思います。「Effeckseerを扱う機会がないから…」と記事をスルーしていたかもしれませんが、3Dエフェクトの実装を学ぶ上で得られる知識はたくさんあります。WebGL向けの3Dエフェクトを学びたい方は「エフェクト作成入門講座 Effekseer編」のシリーズもぜひ参考にしてください。