Three.jsで黒い背景の動画の黒の部分を透明にする方法


Three.jsで黒い背景の動画の黒の部分を透明にする方法を、シェーダーを使って実現する方法を紹介します。今回は、`for` ループを使わずに、周辺のピクセルをサンプリングして平均化することで、エッジのみにブラーをかける方法を紹介します。

### 1. 準備

まず、Three.jsの環境を準備します。  HTMLファイルにThree.jsライブラリを読み込み、シーン、カメラ、レンダラーを作成します。

### index.html

```html
<!DOCTYPE html>
<html>
<head>
  <title>Three.js Black Background Transparency</title>
  <style>
    body { margin: 0; }
    canvas { display: block; }
  </style>
</head>
<body>
  <video id="myVideo" width="1280" height="720" loop muted>
    <source src="your_video.mp4" type="video/mp4">
  </video>
  <script src="https://cdn.jsdelivr.net/npm/three@0.147.0/build/three.min.js"></script>
  <script src="script.js"></script>
</body>
</html>
```

### 2. 動画の読み込み

動画ファイルをHTMLに読み込みます。

```html
<video id="myVideo" width="1280" height="720" loop muted>
  <source src="your_video.mp4" type="video/mp4">
</video>
```

### 3. シェーダーの作成

動画テクスチャを適用するメッシュを作成し、シェーダーマテリアルを適用します。  シェーダーコードは以下のように記述します。

### script.js
```javascript
// シーン、カメラ、レンダラーを作成
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 動画テクスチャの作成
const video = document.getElementById("myVideo");
const texture = new THREE.VideoTexture(video);
texture.format = THREE.RGBFormat;
texture.minFilter = THREE.LinearFilter;
texture.magFilter = THREE.LinearFilter;

// シェーダーマテリアルの作成
const material = new THREE.ShaderMaterial({
  uniforms: {
    videoTexture: { value: texture },
  },
  vertexShader: `
    varying vec2 vUv;

    void main() {
      vUv = uv;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
  `,
  fragmentShader: `
    varying vec2 vUv;
    uniform sampler2D videoTexture;

    void main() {
      vec4 color = texture2D(videoTexture, vUv);

      // 黒のしきい値
      float blackThreshold = 0.1;

      // エッジ検出
      vec4 left = texture2D(videoTexture, vUv + vec2(-0.01, 0.0));
      vec4 right = texture2D(videoTexture, vUv + vec2(0.01, 0.0));
      vec4 top = texture2D(videoTexture, vUv + vec2(0.0, 0.01));
      vec4 bottom = texture2D(videoTexture, vUv + vec2(0.0, -0.01));
      float edgeStrength = abs(color.r - left.r) + abs(color.r - right.r) + abs(color.r - top.r) + abs(color.r - bottom.r);
      edgeStrength += abs(color.g - left.g) + abs(color.g - right.g) + abs(color.g - top.g) + abs(color.g - bottom.g);
      edgeStrength += abs(color.b - left.b) + abs(color.b - right.b) + abs(color.b - top.b) + abs(color.b - bottom.b);

      // ブラー処理
      vec4 blurredColor = (color + left + right + top + bottom) / 5.0; // 近傍のピクセルを平均化

      // 黒のしきい値より暗い場合は透明にする
      if (color.r < blackThreshold && color.g < blackThreshold && color.b < blackThreshold) {
        discard;
      } else {
        gl_FragColor = mix(color, blurredColor, edgeStrength * 0.1); // エッジ付近はブラー後の色を適用
      }
    }
  `
});

// メッシュの作成
const geometry = new THREE.PlaneGeometry(10, 10);
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

// 動画の再生
video.play();

// レンダリング
function animate() {
  requestAnimationFrame(animate);
  renderer.render(scene, camera);
}
animate();
```

### 4. 実行

このコードを実行すると、黒い背景の動画の黒の部分が透明になり、エッジがぼかされた効果が得られます。

### 5. 補足説明

**コード解説**

1. **エッジ判定**
   - `edgeThreshold`: エッジの判定しきい値。この値より黒色に近いピクセルはエッジと判定されます。
   - `edgeDistance`: 黒色の RGB 値との距離を計算します。
   - `edgeFactor`: `edgeDistance` を `edgeThreshold` を使って滑らかに変化させ、エッジの強さを表す値を生成します。

2. **ブラー処理**
   - `blurAmount`: ブラーの量。この値が小さいほど、ブラーが弱くなります。
   - 4 つの `mix` 関数を用いて、上下左右に少しずらしたテクスチャのサンプルを、元のピクセルと混ぜています。この処理によって、エッジ付近にぼかし効果がかかります。
   - `edgeFactor` を使って、エッジ付近のピクセルにのみブラー効果を強く適用しています。

3. **最終的な色**
   - 黒色の判定を行い、黒色であれば `discard` します。
   - それ以外の場合は、アンチエイリアシング処理済みで、必要に応じてブラー処理された色を適用します。

**調整ポイント**

* **`edgeThreshold`:** エッジ判定のしきい値です。この値を調整することで、エッジの判定範囲を変更できます。
* **`blurAmount`:** ブラーの量です。この値を調整することで、ブラーの強さを変更できます。

**注意点**

* このコードでは、`for` ループを使用していませんが、テクスチャサンプリングを 4 回行っているため、パフォーマンスへの影響はあります。
* ブラーの範囲が狭い場合は、`blurAmount` を小さくしてください。
* ブラーの強さは、シェーダーコードの他の部分や画像の性質によって調整する必要があります。



### まとめ

この方法では、シェーダーを使用して、黒い背景の動画の黒の部分を透明にし、エッジにブラー効果を適用しました。  `for` ループを使わずに、周辺のピクセルをサンプリングして平均化することで、シンプルなブラー効果を実現しました。

この技術を応用することで、様々なエフェクトや処理をシェーダーで実現できます。