画像ピクセルをパーティクル化!Three.jsとシェーダーで作るインタラクティブな3Dグラフィック
概要
この記事では、Three.jsとシェーダーを使用して、画像のピクセルデータをパーティクルとして3D空間に配置し、インタラクティブで表現力豊かなグラフィックを実現する方法を解説します。
GSAPによるアニメーションやSplitTextによるテキストエフェクトも組み合わせ、視覚的に美しい名言集を作成します。
https://codepen.io/Prodouga/pen/LYqbwmx
目次
-
- 使用技術
- 制作意図
- こだわりポイント
-
- Three.jsのキャンバス配置
- 名言テキストとボタンの配置
-
- レイアウトとスタイリング
- レスポンシブデザイン
-
- Three.jsの初期化とシーンの構築
- パーティクルの生成とシェーダーの設定
- GSAPによるアニメーション制御
- マウスインタラクションの実装
-
- 頂点シェーダー
- フラグメントシェーダー
-
- プロジェクトの学びと今後の展望
プロジェクトの概要
使用技術
- Three.js: 3Dグラフィックの描画
- GSAP: アニメーションの制御
- SplitText: テキストの分割表示
- GLSL: シェーダーによるパーティクルの表現
制作意図
- Three.jsの可能性を探る
- アニメーションとインタラクションの組み合わせ方を学ぶ
- 視覚的に美しいデザインを実現する
こだわりポイント
- シェーダーを使ったパーティクルの表現
- マウスインタラクションによるパーティクルの動き
- 名言とアニメーションの調和
HTMLの解説
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>名言集 - prodouga.com</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Comfortaa:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="./style.css">
</head>
<body>
<div id="wrapper">
<div id="canvas_container"></div>
<div class="text">
<p class="quoteText">物語は<span style="font-style: italic; font-size: 1.4rem; color:#ED760D">ここ</span>から始まるのだ<br><small>手塚治虫</small></p>
</div>
<p class="clickInfo">クリックすると物語が始まります</p>
<img id="first" src="https://i.imgur.com/TwYg3BB.jpg" style="display:none;">
<a href="https://prodouga.com/contact" target="_blank" class="btn btn_works">お問い合わせ</a>
<button class="btn" id="fullscr">フルスクリーン</button>
</div>
<script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/r125/three.min.js'></script>
<script src='https://unpkg.co/gsap@3/dist/gsap.min.js'></script>
<script src='https://assets.codepen.io/16327/SplitText3.min.js'></script>
<script src="./script.js"></script>
</body>
</html>
ポイント
-
#canvas_container
にThree.jsのキャンバスを配置 -
.text
に名言テキストを表示 - GSAPとSplitTextのライブラリを読み込み
- フルスクリーンボタンやお問い合わせリンクを配置
CSSの解説
* {
margin: 0;
padding: 0;
}
body {
overflow: hidden;
}
#wrapper {
position: relative;
height: 100vh;
background: #000;
font-family: 'Comfortaa', serif;
}
#canvas_container {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100vh;
z-index: 0;
}
.text {
width: 100%;
height: 100px;
text-align: center;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
z-index: 10;
cursor: pointer;
color: rgb(255, 255, 255);
background-color: rgba(0, 0, 0, 0.81);
font-size: 1.6rem;
line-height: 1.5;
user-select: none;
overflow: hidden;
display: flex;
align-items:center;
}
ポイント
- Three.jsのキャンバスを全画面表示
- 名言テキストを中央に配置
- レスポンシブ対応のためのメディアクエリ
JavaScriptの解説
Three.jsの初期化
webgl.scene = new THREE.Scene();
webgl.camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 10000);
webgl.camera.position.z = 180;
webgl.renderer = new THREE.WebGLRenderer({ alpha: true });
webgl.renderer.setSize(webgl.container.clientWidth, webgl.container.clientHeight);
webgl.container.appendChild(webgl.renderer.domElement);
パーティクルの生成
webgl.geometryParticles = new THREE.InstancedBufferGeometry();
const positions = new THREE.BufferAttribute(new Float32Array(4 * 3), 3);
positions.setXYZ(0, -0.5, 0.5, 0.0);
positions.setXYZ(1, 0.5, 0.5, 0.0);
positions.setXYZ(2, -0.5, -0.5, 0.0);
positions.setXYZ(3, 0.5, -0.5, 0.0);
webgl.geometryParticles.setAttribute('position', positions);
GSAPによるアニメーション
webgl.firstAnimation1 = gsap.to(webgl.particlesMesh.rotation, 30, { z: 2, repeat: -1, yoyo: true });
webgl.firstAnimation2 = gsap.fromTo(webgl.particlesMesh.material.uniforms.uDepth, 2, { value: 30 }, { value: 45.0, ease: "elastic.in(1, 0.3)", delay: 2 });
マウスインタラクション
window.addEventListener("mousemove", onMouseMove, false);
function onMouseMove(event) {
webgl.mouse.x = (event.clientX / webgl.renderer.domElement.clientWidth) * 2 - 1;
webgl.mouse.y = - (event.clientY / webgl.renderer.domElement.clientHeight) * 2 + 1;
webgl.raycaster.setFromCamera(webgl.mouse, webgl.camera);
}
シェーダーの解説
頂点シェーダー
void main() {
vec2 puv = offset.xy / uTextureSize;
vec4 colA = texture2D(uTexture, puv);
float grey = colA.r * 0.21 + colA.g * 0.71 + colA.b * 0.07;
vec3 displaced = offset;
displaced.z += rndz * (random(pindex) * 2.0 * uDepth);
gl_Position = projectionMatrix * mvPosition;
}
フラグメントシェーダー
void main() {
vec4 color = vec4(0.0);
vec2 uv = vUv;
vec2 puv = vPUv;
vec4 colA = texture2D(uTexture, puv);
float dist = radius - distance(uv, vec2(0.5));
float t = smoothstep(uCircleORsquare, border, dist);
color = colA;
color.a = t;
gl_FragColor = vec4(color.r, color.g, color.b, t - uAlphaCircle);
}
まとめ
このプロジェクトでは、Three.jsとシェーダーを使用して、画像のピクセルデータをパーティクルとして3D空間に配置し、インタラクティブで表現力豊かなグラフィックを実現しました。GSAPによるアニメーションやSplitTextによるテキストエフェクトも組み合わせ、視覚的に美しい名言集を作成しました。
今後の展望として、より複雑なシェーダーエフェクトや、ユーザーが自由に画像をアップロードできる機能を追加することを検討しています。Three.jsの可能性は無限大です!ぜひ、このプロジェクトを参考に、独自の3Dグラフィックを作成してみてください。
この記事が、Three.jsを使った3Dグラフィック制作の一助となれば幸いです。質問やフィードバックがあれば、ぜひコメントをお寄せください!🚀