My website has been renewaled
2016.07.08
Category
Tags
かねてより企んでいた自身のサイトのリニューアルが完了しました。
自分の実績を載せるページが欲しかったのですが、コンテンツが寂しいと思ったので、今までサブドメインで運用していたブログとブックマークサイトを統合しました。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等を使用して決めてます。
すごい適当な説明になってしまいましたが、要は結構本気出して実装しました。