WebGL におけるテクスチャのアニメーション
=======================

* « 前のページ

今回のデモンストレーションでは、前回の例で用いた静的なテクスチャを Ogg ビデオファイルのフレームに置き換えます。実はこれを行うのはとても簡単ですが、見ていて楽しいです。さっそく始めましょう。なお同様のコードを、どんな種類のデータ（`<canvas>` など）をテクスチャのソースとして用いる場合でも使用することができます。

動画へのアクセス
--------

始めに、ビデオフレームを取り出すのに使う `<video>` 要素を作成します。

**メモ:** この宣言を "webgl-demo.js" スクリプトの始めに追加しましょう。


```js
// 動画がテクスチャにコピーできる場合は true に設定する
let copyVideo = false;
```


**メモ:** この関数を "webgl-demo.js" スクリプトに追加しましょう。


```js
function setupVideo(url) {
  const video = document.createElement("video");

  let playing = false;
  let timeupdate = false;

  video.playsInline = true;
  video.muted = true;
  video.loop = true;

  // 動画のデータが確実にあることを保証するために、
  // この 2 つのイベントを待つ

  video.addEventListener(
    "playing",
    () => {
      playing = true;
      checkReady();
    },
    true,
  );

  video.addEventListener(
    "timeupdate",
    () => {
      timeupdate = true;
      checkReady();
    },
    true,
  );

  video.src = url;
  video.play();

  function checkReady() {
    if (playing && timeupdate) {
      copyVideo = true;
    }
  }

  return video;
}
```


最初に動画要素を作成します。動画を自動再生し、音をミュートし、ループ再生するように設定します。次に、動画が再生され、時刻が更新されたことを確認するために 2 つのイベントを設定します。まだ利用できるデータがない動画を WebGL にアップロードするとエラーになるため、この両方のチェックが必要です。これらのイベントをどちらも調べることで、利用できるデータがあることが確認され、WebGL テクスチャへの動画のアップロードを始めるのには安全であることが保証されます。上記のコードでは、これらのイベントをどちらも取得したことを確認しています。取得した場合は、グローバル変数 `copyVideo` に true を設定し、動画をテクスチャにコピーし始めても安全であることを示しています。

そして最後に、 `src` 属性を設定し、 `play` を呼び出して動画の読み込みと再生を開始します。

WebGL にテクスチャデータを提供するために使用するためには、元の動画を安全なソースから読み込む必要があります。つまり、安全なウェブサーバーを使用するようにコードを展開する必要があるだけでなく、テストするための安全なサーバーも必要になります。ローカルにテスト用サーバーを用意するには？を参照してください。

動画フレームをテクスチャとして使用する
-------------------

次に変更するのは `initTexture()` です。画像ファイルを読み込む必要がなくなったため、とても単純になります。画像を読み込む代わりに、空のテクスチャオブジェクトを作成して、後で使用するフィルターを設定します。

**メモ:** "webgl-demo.js" の `loadTexture()` 関数を以下のコードに置き換えましょう。


```js
function initTexture(gl) {
  const texture = gl.createTexture();
  gl.bindTexture(gl.TEXTURE\_2D, texture);

  // 動画はインターネット経由でダウンロードしなければならない
  // ため、準備ができるまで時間がかかる可能性があります。
  // そのため、テクスチャに単一のピクセルを置くことで、
  // 直ちにそれを利用することができます。
  const level = 0;
  const internalFormat = gl.RGBA;
  const width = 1;
  const height = 1;
  const border = 0;
  const srcFormat = gl.RGBA;
  const srcType = gl.UNSIGNED\_BYTE;
  const pixel = new Uint8Array([0, 0, 255, 255]); // 不透明の青
  gl.texImage2D(
    gl.TEXTURE\_2D,
    level,
    internalFormat,
    width,
    height,
    border,
    srcFormat,
    srcType,
    pixel,
  );

  // mips をオフにし、 wrapping を clamp to edge に設定することで、動画の大きさに関係なく動作するようになります。
  gl.texParameteri(gl.TEXTURE\_2D, gl.TEXTURE\_WRAP\_S, gl.CLAMP\_TO\_EDGE);
  gl.texParameteri(gl.TEXTURE\_2D, gl.TEXTURE\_WRAP\_T, gl.CLAMP\_TO\_EDGE);
  gl.texParameteri(gl.TEXTURE\_2D, gl.TEXTURE\_MIN\_FILTER, gl.LINEAR);

  return texture;
}
```


**メモ:** 以下の関数を "webgl-demo.js" に追加しましょう。


```js
function updateTexture(gl, texture, video) {
  const level = 0;
  const internalFormat = gl.RGBA;
  const srcFormat = gl.RGBA;
  const srcType = gl.UNSIGNED\_BYTE;
  gl.bindTexture(gl.TEXTURE\_2D, texture);
  gl.texImage2D(
    gl.TEXTURE\_2D,
    level,
    internalFormat,
    srcFormat,
    srcType,
    video,
  );
}
```


このコードは以前にも見たことがあるでしょう。前回の例の画像 onload 関数とほぼ同じですが、 `texImage2D()` を呼び出すときに `Image` オブジェクトを渡す代わりに `<video>` 要素を渡している点が異なります。 WebGL は現在のフレームを取り出し、テクスチャとして使用する方法を知っています。

次に、これらの新しい関数を `main()` 関数から呼び出す必要があります。

**メモ:** `main()` 関数で、 `loadTexture()` の呼び出しを次のコードに置き換えましょう。


```js
const texture = initTexture(gl);
const video = setupVideo("Firefox.mp4");
```


**メモ:** また、Firefox.mp4 ファイルを JavaScript ファイルと同じローカルディレクトリーにダウンロードする必要があります。

**メモ:** `main()` 関数で、 `render()` 関数をこのように置き換えましょう。


```js
// シーンを反復して描画
function render(now) {
  now \*= 0.001; // 秒へ変換
  deltaTime = now - then;
  then = now;

  if (copyVideo) {
    updateTexture(gl, texture, video);
  }

  drawScene(gl, programInfo, buffers, texture, cubeRotation);
  cubeRotation += deltaTime;

  requestAnimationFrame(render);
}
```


`copyVideo` が true の場合、 `drawScene()` 関数を呼び出す直前に `updateTexture()` を呼び出します。

以上です。

コードを確認する | 新しいページでデモを開く

関連情報
----

* HTML5 の audio 要素と video 要素の使用
* « 前のページ