見出し画像

My website has been renewaled

※この記事はtkmh.me上で掲載している記事 (2016.07.08 掲載) を転載、加筆・修正したものです。

---------

かねてより企んでいた自身のサイトのリニューアルが完了しました。

自分の実績を載せるページが欲しかったのですが、コンテンツが寂しいと思ったので、今までサブドメインで運用していたブログとブックマークサイトを統合しました。RSSを登録頂いていた方はいかに登録し直していただけると幸いです。

・Blog
https://tkmh.me/blog/feed/

・Bookmarks
https://tkmh.me/bookmarks/feed/

ついでに新設のWorksのRSSも。

・Works
https://tkmh.me/works/feed/

技術的にも盛り込みたいものは盛り込めたし (WebGL、非同期遷移、レスポンシブ etc…) 、デザインも綺麗にまとまって、割と気に入ってます。

このサイトの一番の見せ場はメインビジュアルのトランスフォームです。エンジニアの方向けに少しだけ解説します。

メインビジュアルはWebGL (three.js) で組んでいます。ロゴの形の3Dオブジェクトはモデリングをしたのではなく、プログラムで構築しています。直方体を組み合わせた形なので、そんなに複雑ではないです。

ただし、ポリゴンをバラバラにしてトランスフォームさせたかったので、BoxGeometryを使用せず、BefferGeometryに自分で頂点を追加しています。かつバラバラのキューブ状でも動かしたかったので、ボクセルを構築しています。なので普通にBoxGeometryを使用して同じ形を作るよりも、頂点数は遥かに多いです。

あとはポリゴン単位で動かしたり、キューブ単位で動かしたり、頂点単位で動かしたりしています。
頂点の座標計算はすべてシェーダないで行っています。これはGPGPUという手法です。

GPGPUでトランスフォームをどのように制御しているかというと、頂点シェーダ内にすべてのアニメーションパターンを記述しておき、それを係数によってどのアニメーションを適用するかを決めています。以下に簡単な例を記述します。

// 頂点シェーダ例
uniform mat4 modelViewMatrix;
uniform mat4 modelMatrix;
uniform mat4 projectionMatrix;
uniform float animationParam1;
uniform float animationParam2;

void main() {
 vec3 pos = position;

 // パターン1
 pos.x += animationParam1 * 100;

 // パターン2
 pos.y += animationParam2 * 100;

 gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}

デフォルトの状態ではanimationParam1に1をセット、animationParam2に0をセットし、パターン1のみが適用されている状態で、パターン1からパターン2にトランスフォームする際は、animationParam1を0に、animationParam1を1に値を徐々に変化させる処理を同時行っています。そうすることによって、2つのアニメーションパターン間をシームレスに遷移させることができます。

実際はイージングの処理を入れたり、ノイズを入れたり、時間差処理を入れたりしてるので、これよりもずっと複雑ですが、面倒なので割愛。

各コンテンツのタイトルの形状に変形させていますが、それは予め各コンテンツのタイトルの文字の画像 (Works) などを読み込んで、ピクセル情報を取得し、黒い色が付いているところをランダムでピックアップし、GeometryのAttributeにvec3として渡しているので、その情報を元に座標を決めています。

↑こんな背景透過画像を使用しています。(白の背景色を入れていますが、実際に使用している画像は白い部分が透過になっているものです。)

以下はタイトルの画像のピクセル情報取得部分の抜粋です。 (CoffeeScript)

# 一部抜粋
$img = $('').one 'load', (e)=>
 # drawImageでタイトルの文字をcanvasに描画
 img = $img.get 0
 canvas = document.createElement 'canvas'
 context = canvas.getContext '2d'
 canvas.width = img.width
 canvas.height = img.height
 context.drawImage img, 0, 0, img.width, img.height

 # ピクセルの色情報を取得
 imgData = context.getImageData 0, 0, img.width, img.height

 points = []
 for i in [0...imgData.data.length - 1] by 4
   alpha = imgData.data[i + 3]
   index = i / 4
   if alpha > 0
     # アルファが0より大きい場合に、ピクセルの座標をpointsにpush
     points.push {
       x: index % img.width - img.width / 2
       y: img.height / 2 - Math.floor(index / img.width)
     }

 # ... 以下、pointsに入っている座標をランダムにピックアップし、シェーダに渡す

.attr 'src', imgPath  # imgPathはタイトルの文字の画像パス

色の制御は適当にsimplexNoise等を使用して決めてます。

すごい適当な説明になってしまいましたが、要は結構本気出して実装しました。

ソースコードも公開しています。(readmeに説明がありませんがあしからず。。)



サポートいただければ、レッドブルを飲んでより頑張れると思います。翼を授けてください。