突拍子もないWebサイトの作り方 技術編
はじめに
こんにちはウコンパワーです。
今回は私が所属しておりますジュニにて一つWebサイトを公開しまして、ちょっとトリッキーなことをやったりしましたので、いくつか紹介できればなと思いこちらの記事を書いております。
制作全体に関しても記事をまとめたりもしてますのでぜひ参照ください。
というわけで今回公開したサイトは以下です。↓↓
GPUがアツアツですね!!
すべてをさらけ出す
そして今回はただのTips紹介じゃないぞ!の気持ちです。
なぜなら、リポジトリが公開されてるからです。
こんな記事読まなくても git clone
すれば済む話なのでこの記事はおまけ程度に見てもらえますとです。
ライブラリ / ツール
主に使用したライブラリやツールは以下です。
ライブラリ
- Threejs
- cannon.js
- ore-three
ツール
- VSCode
- Bender
- Fgma
- Miro
フレームワーク
無数のWebフレームワークが溢れてる昨今、今回はgulp & webpackの伝統的構成で挑戦しました。
なぜかって??
ちょっぱやで使えるテンプレがそれだったからです。🙄
シーンづくり
今回のサイト、全セクションをThree.jsを用いた3Dで表現してます。
シーンづくりをscene.add(new THREE.Mesh(geo,mat))
でやっていたらなかなかつらいものがあります。そこでBlenderの出番です。
基本的なシーンのモデリングは概ねBlender上で行います。
Blenderで作成したシーンをglTFでWebに持ってきて、各オブジェクトを'scene.getObjectByName()'で取得しそれぞれのクラスに渡していくというのが基本の流れです。
/*-------------------------------
Logo
-------------------------------*/
this.logo = new Logo( scene.getObjectByName( 'Logo' ) as THREE.Object3D, this.commonUniforms );
this.logo.switchVisibility( this.sectionVisibility );
以下のようなモデリングでは難しいような形状や演出がされるオブジェクトに関してはThree.js上でメッシュを作ってます。
アニメーション
Blender側で作成するアニメーションはこのバクのキャラクターの動きのみです。
セクションごとにActionを作りTHREE.AnimationMixerなどを用いてアニメーションを切り替えてます。
その他のオブジェクトの動きなどはすべてスクリプト制御してます。
ORE.Animator
というDurationと値を渡すとDuration分時間をかけて値をアニメーションしてくれるクラスを用意してあり、その値をシェーダーに突っ込むことが多いです。
いくつかピックアップ
ちょっと変わった表現をいくつかピックアップします。
屈折
このガラス的な表現を行うにはちょっと手間がかかります。
ガラスのオブジェクトを描画する前に、その奥に存在しているオブジェクトのみが描画されているフレームバッファを用意しないといけないからですね。
THREE.MeshクラスなどにはonBeforeRender
という、そのメッシュが描画される寸前に呼ばれるコールバックが用意されています。
今回はそれを用いて背景のみの描画結果を別フレームバッファに転写しています。
そうして用意したフレームバッファとメッシュの法線を用いてUVを歪ませながら背景色を取得することでガラスの屈折を表現することができます。
こちらが屈折のシェーダー箇所です。
forで歪みの強さを徐々に強くすることでにじむようにし、RGBごとに歪みのつよさをかえることで色ズレの表現をしてます。
// refract
vec3 refractCol = vec3( 0.0 );
vec2 screenUv = gl_FragCoord.xy / winResolution.xy;
vec2 refractUv = screenUv;
float slide;
vec2 refractUvR;
vec2 refractUvG;
vec2 refractUvB;
float refractPower = 0.3;
vec2 refractNormal = geo.normal.xy * ( 1.0 - geo.normal.z * 0.85 );
#pragma unroll_loop_start
for ( int i = 0; i < 16; i ++ ) {
slide = float( UNROLLED_LOOP_INDEX ) / 16.0 * 0.1 + random( screenUv ) * 0.03;
refractUvR = refractUv - refractNormal * ( refractPower + slide * 1.0 ) * uTransparent;
refractUvG = refractUv - refractNormal * ( refractPower + slide * 1.5 ) * uTransparent;
refractUvB = refractUv - refractNormal * ( refractPower + slide * 2.0 ) * uTransparent;
refractCol.x += texture2D( uSceneTex, refractUvR ).x;
refractCol.y += texture2D( uSceneTex, refractUvG ).y;
refractCol.z += texture2D( uSceneTex, refractUvB ).z;
}
#pragma unroll_loop_end
refractCol /= float( 16 );
トレイル
今回は前提としてミドルレンジ後半~ハイエンドあたりの端末で見られればOKということもあり、せっかくなのでGPGPUを行いました。
GPGPUを行った演出の一つが以下のトレイルです。
こちらはPC時のみ見れる演出で、マウスカーソルの後ろをなぞるように一本の線がビヨンと伸びるものです。
コードはこちら
トレイルははるか昔に解説を書いていたりして基本的な考え方はこちらと同じですが、今回はORE.GPUComputationController
というコンピュートシェーダー(役のフラグメントシェーダー)を渡すとよしなにフレームバッファをやりくりしてくれるやつを用意してます。
以下がコンピュートシェーダーです。128x1pxのフレームバッファに対してシェーディングが行われます。
uCursorPos
にマウスカーソルの位置(をトレイルのローカル座標に変換したもの)が送られてくるのでそれを一番左のピクセルに描画します。他のピクセルはそれぞれの一個左のピクセルの値を描画することでトレイルの動作を作ります。
uniform vec2 dataSize;
uniform sampler2D uPosDataTex;
uniform sampler2D uNoiseTex;
uniform float time;
uniform float uMaterial[6];
uniform vec3 uCursorPos;
#pragma glslify: rotate = require('./rotate.glsl' )
void main() {
if( gl_FragCoord.x <= 1.0 ) {
vec2 uv = gl_FragCoord.xy / dataSize.xy;
gl_FragColor = vec4( uCursorPos, 1.0 );
} else {
vec2 uv = gl_FragCoord.xy / dataSize.xy;
vec2 bUV = ( gl_FragCoord.xy - vec2( 1.0, 0.0 ) ) / dataSize.xy;
vec3 pos = texture2D( uPosDataTex, uv ).xyz;
vec3 beforePos = texture2D( uPosDataTex, bUV ).xyz;
float blend = 0.0;
blend += uMaterial[0] * 0.4;
blend += uMaterial[1] * 0.2;
blend += uMaterial[2] * 0.3;
blend += uMaterial[4] * 0.1;
blend += uMaterial[5] * 0.2;
vec3 newPos = mix(beforePos, pos, blend);
// sec4 床むりやりやぞ
newPos -= -12.0;
newPos.y *= 1.0 - 0.1 * uMaterial[3];
newPos += -12.0;
// sec5 ノイズ
newPos.xyz += ( texture2D( uNoiseTex, pos.xy * 0.02 ).xyz - 0.50 ) * 0.05 * uMaterial[4];
// sec6 進むやつ
newPos += uMaterial[5] * vec3( 0.1, -.06, 0.1 );
gl_FragColor = vec4(newPos,1.0);
}
}
左のピクセルの値を描画する際に、自身のピクセルの値とブレンドすることでトレイルが引っ張られるような動きにしています。
ブレンドあり
ブレンドなし
まとめ
あ、あれも!それも!と今までやってみたかった実装をたくさんツメツメしたWebサイトになりました。
パフォーマンスもギリギリ(アウト?)くらいのところまで行き、なにやらiOS Safariでスクショを撮ると100%固まる模様です。
....またそれも一興。
今回は我々のありのまま(主に自分の)をさらけ出すという意味でリポジトリも全て公開しています。
こんな有様も全て公開しています。
シンパシーを感じた方、ぜひお待ちしておりますよ😎
Discussion