Loading...

Loading...

Scroll

Blog

Development and Information etc...

Categories

Tags

Monthly Archives

Keyword

Back

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等を使用して決めてます。

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

Related Posts

Top