インタラクティブ3Dアート！Three.jsで画像ピクセルをパーティクルに変換する方法

# 画像ピクセルをパーティクル化！Three.jsとシェーダーで作るインタラクティブな3Dグラフィック

## 概要
この記事では、Three.jsとシェーダーを使用して、画像のピクセルデータをパーティクルとして3D空間に配置し、インタラクティブで表現力豊かなグラフィックを実現する方法を解説します。
GSAPによるアニメーションやSplitTextによるテキストエフェクトも組み合わせ、視覚的に美しい名言集を作成します。

![スクリーンショット 2023-12-28 18.55.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3637864/eb0f1733-728c-48d1-32bb-ad276f87bdaf.png)


https://codepen.io/Prodouga/pen/LYqbwmx
---

## 目次

1. **[プロジェクトの概要](#プロジェクトの概要)**
   - 使用技術
   - 制作意図
   - こだわりポイント

2. **[HTMLの解説](#htmlの解説)**
   - Three.jsのキャンバス配置
   - 名言テキストとボタンの配置

3. **[CSSの解説](#cssの解説)**
   - レイアウトとスタイリング
   - レスポンシブデザイン

4. **[JavaScriptの解説](#javascriptの解説)**
   - Three.jsの初期化とシーンの構築
   - パーティクルの生成とシェーダーの設定
   - GSAPによるアニメーション制御
   - マウスインタラクションの実装

5. **[シェーダーの解説](#シェーダーの解説)**
   - 頂点シェーダー
   - フラグメントシェーダー

6. **[まとめ](#まとめ)**
   - プロジェクトの学びと今後の展望

---

## プロジェクトの概要

### **使用技術**
- **Three.js**: 3Dグラフィックの描画
- **GSAP**: アニメーションの制御
- **SplitText**: テキストの分割表示
- **GLSL**: シェーダーによるパーティクルの表現

### **制作意図**
- Three.jsの可能性を探る
- アニメーションとインタラクションの組み合わせ方を学ぶ
- 視覚的に美しいデザインを実現する

### **こだわりポイント**
- シェーダーを使ったパーティクルの表現
- マウスインタラクションによるパーティクルの動き
- 名言とアニメーションの調和

---

## HTMLの解説

```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の解説

```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の初期化**
```javascript
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);
```

### **パーティクルの生成**
```javascript
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によるアニメーション**
```javascript
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 });
```

### **マウスインタラクション**
```javascript
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);
}
```

---

## シェーダーの解説

### **頂点シェーダー**
```glsl
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;
}
```

### **フラグメントシェーダー**
```glsl
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グラフィック制作の一助となれば幸いです。質問やフィードバックがあれば、ぜひコメントをお寄せください！🚀