three.js
+
Web Font
+
GPGPU

by Takumi Hasegawa / @tkm0125

index

  • 自己紹介
  • three.jsとは?
  • three.jsはとりあえずこれがあればOK
  • Geometryを自分で作ろう
  • WebFontをテクスチャとして使う
  • Vertex ShaderでGPGPU
  • three.jsでシェーダを使用する

index

  • 自己紹介
  • three.jsとは?
  • three.jsはとりあえずこれがあればOK
  • Geometryを自分で作ろう
  • WebFontをテクスチャとして使う
  • Vertex ShaderでGPGPU
  • three.jsでシェーダを使用する

自己紹介

長谷川 巧 (はせがわ たくみ)

Freelance
Front-end engineer / Technical director

最近 (でもないけど) 作ったもの

Takumi Hasegawa

https://tkmh.me/

水曜日のカンパネラ OFFICIAL WEB SITE

http://www.wed-camp.com/

three.jsは割と得意です。

index

  • 自己紹介
  • three.jsとは?
  • three.jsはとりあえずこれがあればOK
  • Geometryを自分で作ろう
  • WebFontをテクスチャとして使う
  • Vertex ShaderでGPGPU
  • three.jsでシェーダを使用する

three.jsとは

three.js

WebGLを意識しなくてもWebGLコンテンツが作れるJavascriptライブラリ。作った人は天才。

https://threejs.org/

index

  • 自己紹介
  • three.jsとは?
  • three.jsはとりあえずこれがあればOK
  • Geometryを自分で作ろう
  • WebFontをテクスチャとして使う
  • Vertex ShaderでGPGPU
  • three.jsでシェーダを使用する

three.jsはとりあえず
これがあればOK

  • WebGLRenderer
    - WebGLを描画するためのエンジン
  • Scene
    - 3Dの世界
  • (Light)
    - 光源 (今回は使用しません。)
  • Camera
    - Sceneを撮影するカメラ
  • Geometry
    - 物体の形などの情報
  • Material
    - 物体の表面をどのように描画するかの情報
  • Mesh
    - GeometryとMaterialを設定してScene上に配置
    - 3Dオブジェクトの実体 (座標・回転の情報等を保持)

TrackballControlsが便利

カメラをマウスでグリグリ動かせる機能を提供
※ただし、デフォルトでは含まれないので、自分で読み込む必要がある。

// イニシャライズ (カメラを引数に)
var controls = new THREE.TrackballControls(camera);
// 描画ループ内で実行
controls.update();
// リサイズ時に実行
controls.handleResize();

index

  • 自己紹介
  • three.jsとは?
  • three.jsはとりあえずこれがあればOK
  • Geometryを自分で作ろう
  • WebFontをテクスチャとして使う
  • Vertex ShaderでGPGPU
  • three.jsでシェーダを使用する

Geometryを自分で作ろう

BufferGeometryを使用して、
物体の形を自分で定義する。

BufferGeometryとは

attributes、indexの追加など、
わりと素のWebGLのAPIを意識しながら
Geometryを作れるクラス
シェーダと組み合わせるならこれを使う

BufferGeometryを使用して、
正方形 (Plane) を作ってみる

BufferGeometryを拡張した
FloatingCharsGeometry(自作)を使用して
正方形をたくさん持ったGeometryを作りたい。

その前にFloatingCharsクラスについて説明。

FloatingCharsクラス

THREE.Meshクラスを拡張したクラス。
完成形では、FloatingCharsGeometry
のインスタンスと
シェーダを適用したMaterialを
プロパティに持つ

要は、step1でmeshを作って
sceneに追加したように
FloatingCharsのインスタンスを
生成してsceneに追加して使う

FloatingCharsGeometryに話を戻します。

まずattributesを定義

以下のそれぞれの情報が
羅列されている配列を生成

  • 頂点情報(3次元)
  • UV座標(2次元)
  • 正方形のインデックス
  • 頂点シェーダで使用するランダム値(3次元)

attributesは頂点ごとの情報なので、頂点を基準に考えるとわかりやすい。

正方形のインデックス (頂点が何個目の正方形に含まれるか)と
ランダム値は正方形の位置を決める計算に使用するので、
4つ全て同じ値を設定する

たくさん作るとこんな感じになる。

ちなみに全ての正方形は同じ位置にある

次にindexを定義

indexはどの頂点を使って
ポリゴン(三角形)を形成するか

頂点は反時計回りで指定

BufferGeometryにattributes, indexを追加

BufferAttributeのインスタンスを生成、
addAttributeメソッドでattributes
setIndeメソッドでindexを追加

// attributes
// THREE.BufferAttributeの第二引数は、
// 各頂点に対していくつデータを使うか
// positionなら3次元なので3, randomValuesも3次元のため3
// uvは2次元なので2, charIndexは1
this.addAttribute('position', new THREE.BufferAttribute(new Float32Array(this.vertices), 3));
this.addAttribute('randomValues', new THREE.BufferAttribute(new Float32Array(this.randomValues), 3));
this.addAttribute('charIndex', new THREE.BufferAttribute(new Uint16Array(this.charIndices, 1));
this.addAttribute('uv', new THREE.BufferAttribute(new Float32Array(this.uvs), 2));

// index
// THREE.BufferAttributeの第二引数は1
this.setIndex(new THREE.BufferAttribute(new Uint16Array(this.indices), 1));

最後に頂点法線を計算 (今回は使用しませんが、一応)

computeVertexNormalsメソッドで自動計算

this.computeVertexNormals();

Geometryに関してはここまで

index

  • 自己紹介
  • three.jsとは?
  • three.jsはとりあえずこれがあればOK
  • Geometryを自分で作ろう
  • WebFontをテクスチャとして使う
  • Vertex ShaderでGPGPU
  • three.jsでシェーダを使用する

WebFontをテクスチャとして使う

three.jsでは、画像・動画などを
Materialに適用することができる。

Textureクラスのインスタンスを生成し、Materialに適用

var texture = new THREE.Texture();
var material = new THREE.MeshBasicMaterial({
  map: texture
});

WebFontの文字はどうやって
テクスチャにするの?

Canvasにオフスクリーン描画したものを
テクスチャとして使用する
drawTextメソッドを使用する

!!注意!!

CanvasのdrawTextメソッドでWebFontを使用する場合、WebFontのロードが完了しないと
描画できない

Web Font Loaderを使用

以下のように記述

いろんなwebfontサービスに対応 (今回はgoogle)
WebFont.load({
  google: {
    families: [ 'フォント名' ]
  },
  active: function(fontFamily, fontDescription) {
    // ここにロード後の処理を記述
    // このタイミングでcanvasにテキストを描画
  }
});

最終的には1枚のCanvasに複数の文字を描画し
UV座標を調整して多数の正方形に異なる文字を
テクスチャとして反映させたい!

ここはシェーダで何とかする!

とりあえず実際に描画

例として1行に16文字描画

テクスチャに関しては一旦ここまで

続きは後ほど

index

  • 自己紹介
  • three.jsとは?
  • three.jsはとりあえずこれがあればOK
  • Geometryを自分で作ろう
  • WebFontをテクスチャとして使う
  • Vertex ShaderでGPGPU
  • three.jsでシェーダを使用する

Vertex ShaderでGPGPU

GPGPUとは

GPGPU(General-purpose computing on graphics processing units; GPUによる汎用計算)とは、GPUの演算資源を画像処理以外の目的に応用する技術のことである

今回で言うと

  • たくさんの正方形をバラバラに飛ばすための頂点座標計算
  • それぞれの正方形にバラバラの文字の
    テクスチャを適用するためのUV座標計算

じゃあthree.jsでどうやって自分で書いたシェーダをつかうの?

index

  • 自己紹介
  • three.jsとは?
  • three.jsはとりあえずこれがあればOK
  • Geometryを自分で作ろう
  • WebFontをテクスチャとして使う
  • Vertex ShaderでGPGPU
  • three.jsでシェーダを使用する

three.jsでシェーダを使用する

three.jsで自分で書いたシェーダ
を使用するには?

RawShaderMaterialを使用する

※あらかじめ組み込みの値等が宣言されている
ShaderMaterialというのもある

今回使用するRawShaderMaterialの定義

// RawShaderMaterial生成
this.material = new THREE.RawShaderMaterial({
  transparent: true,       // 透過するかどうか
  side: THREE.DoubleSide,  // ポリゴンの両面を描画
  uniforms: {
    // 各種uniform変数を定義
  },
  vertexShader: "頂点シェーダのプログラムをテキストで渡す",
  fragmentShader: "フラグメントシェーダのプログラムをテキストで渡す"
  // (その他オプションあり)
});
uniformsのtypeはこちらを参照

頂点シェーダとフラグメントシェーダの内容は
jQueryを使用して、HTMLのscriptタグに
記述されている内容を取得

vertexShader: $('#vertexShader').text(),
fragmentShader: $('#fragmentShader').text()

three.jsでは、あらかじめ次のunform変数
が定義されているので、
これらはRawShaderMaterialの
uniformsで改めて定義しなくてよい

※一部略
  • modelMatrix
    - mat4 オブジェクト座標からワールド座標へ変換する行列
  • viewMatrix
    - mat4 ワールド座標から視点座標へ変換
  • modelViewMatrix
    - mat4 modelMatrix x viewMatrixの積算
  • projectionMatrix
    - mat4 クリップ座標系に変換する行列
  • cameraPosition
    - vec3 カメラの座標

あとはシェーダをガリガリ書いていこう!

おまけ

dat.guiを使うと
パラメータをいじれるので楽しい

GLSLでよく使う関数

座標(3次元)ベクトルの回転

vec3 rotateVec3(vec3 p, float angle, vec3 axis){
  vec3 a = normalize(axis);
  float s = sin(angle);
  float c = cos(angle);
  float r = 1.0 - c;
  mat3 m = mat3(
    a.x * a.x * r + c,
    a.y * a.x * r + a.z * s,
    a.z * a.x * r - a.y * s,
    a.x * a.y * r - a.z * s,
    a.y * a.y * r + c,
    a.z * a.y * r + a.x * s,
    a.x * a.z * r + a.y * s,
    a.y * a.z * r - a.x * s,
    a.z * a.z * r + c
  );
  return m * p;
}
  • p: 座標
  • angle: 回転する角度 (ラジアン)
  • axis: 回転の軸

数値の範囲変換

float map(float value, float inputMin, float inputMax, float outputMin, float outputMax, bool clamp) {
  if(clamp == true) {
    if(value < inputMin) return outputMin;
    if(value > inputMax) return outputMax;
  }

  float p = (outputMax - outputMin) / (inputMax - inputMin);
  return ((value - inputMin) * p) + outputMin;
}
  • value: 値
  • inputMin: 変換前の値の最小値
  • inputMax: 変換前の値の最大値
  • outputMin: 変換後の値の最小値
  • outputMax: 変換後の値の最大値
  • clamp: 値を範囲内に収めるかどうか

HSVをRGBに変換

vec3 hsv2rgb(vec3 c) {
  vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
  vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
  return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
  • c: HSVカラー

FIN.