Skip to content

Commit

Permalink
2023/07/30 時点の英語版に同期
Browse files Browse the repository at this point in the history
  • Loading branch information
mfuji09 committed Oct 21, 2023
1 parent fc2aefe commit 9e6a265
Showing 1 changed file with 39 additions and 32 deletions.
71 changes: 39 additions & 32 deletions files/ja/web/api/webgl_api/matrix_math_for_the_web/index.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,42 @@
---
title: ウェブの行列計算
slug: Web/API/WebGL_API/Matrix_math_for_the_web
l10n:
sourceCommit: 0ac4a25134bd20e40bc5ba214c32a7e908dc7aad
---

{{DefaultAPISidebar("WebGL")}}

行列は、空間内のオブジェクトの変換を表すために使用でき、画像を構築したり、ウェブ上でデータを視覚化したりするときに、多くの主要な種類の計算を実行するために使用されます。 この記事では、行列を作成する方法と、[CSS transform](/ja/docs/Web/Guide/CSS/Using_CSS_transforms) および `matrix3d` transform 型でそれらを使用する方法について説明します。
行列は、空間内のオブジェクトの変換を表すために使用でき、画像を構築したり、ウェブ上でデータを視覚化したりするときに、多くの主要な種類の計算を実行するために使用されます。 この記事では、行列を作成する方法と、[CSS 座標変換](/ja/docs/Web/CSS/CSS_transforms/Using_CSS_transforms) および `matrix3d` transform 型でそれらを使用する方法について説明します。

この記事では [CSS](/ja/docs/Web/CSS) を使用して説明を簡略化しますが、行列は [WebGL](/ja/docs/Web/API/WebGL_API)[WebXR](/ja/docs/Web/API/WebXR_Device_API)(VR および AR)API、[GLSL シェーダー](/ja/docs/Games/Techniques/3D_on_the_web/GLSL_Shaders)などのさまざまなテクノロジーで使用されるコアコンセプトです。 この記事は、[MDN コンテンツキット](https://github.com/gregtatum/mdn-matrix-math)としても入手できます。 実際の例では、`MDN` という名前のグローバルオブジェクトで使用できる[ユーティリティ関数](https://github.com/gregtatum/mdn-webgl)のコレクションを使用しています。

## 変換行列

行列には多くの種類がありますが、私たちが興味を持っているのは 3D 変換行列です。 これらの行列は、4x4 のグリッドに配置された 16 個の値のセットで構成されています。 [JavaScript](/ja/docs/Web/JavaScript) では、行列を配列として表すのは簡単です。
行列には多くの種類がありますが、私たちが興味を持っているのは 3D 変換行列です。 これらの行列は、 4×4 のグリッドに配置された 16 個の値のセットで構成されています。 [JavaScript](/ja/docs/Web/JavaScript) では、行列を配列として表すのは簡単です。

まず、**単位行列**identity matrixについて検討します。 これは特別な変換行列であり、スカラー乗算での 1 と同じように機能します。 n \* 1 = n と同様に、任意の行列に単位行列を乗算すると、元の行列と値が一致する結果の行列が得られます。
まず、**単位行列** (identity matrix) について検討します。 これは特別な変換行列であり、スカラー乗算での 1 と同じように機能します。 n \* 1 = n と同様に、任意の行列に単位行列を乗算すると、元の行列と値が一致する結果の行列が得られます。

単位行列は JavaScript では次のようになります。

```js
let identityMatrix = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
```

単位行列の乗算とはどのようなものでしょうか? 最も簡単な例は、単一の点に単位行列を乗算することです。 3D の点に必要なのは 3 つの値(x、y、z)だけであり、変換行列は 4x4 の値の行列なので、点には 4 番目の次元を追加する必要があります。 慣例により、この次元は**パースペクティブ**perspectiveと呼ばれ、文字 w で表されます。 一般的には、w を 1 に設定すると、計算がうまくいきます。
単位行列の乗算とはどのようなものでしょうか? 最も簡単な例は、単一の点に単位行列を乗算することです。 3D の点に必要なのは 3 つの値 (`x``y``z`) だけであり、変換行列は 4×4 の値の行列なので、点には 4 番目の次元を追加する必要があります。 慣例により、この次元は**視点距離** (perspective) と呼ばれ、文字 `w` で表されます。 一般的には、 `w` を 1 に設定すると、計算がうまくいきます。

w 成分を点に追加した後、行列と点がどのようにきれいに並んでいるかに注目してください。

```js
[1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1][(4, 3, 2, 1)]; // Point at [x, y, z, w]
```js-nolint
[1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1]
[4, 3, 2, 1] // Point at [x, y, z, w]
```

w 成分には、この記事の範囲外のいくつかの追加の用途があります。 [WebGL モデルビュー投影](/ja/docs/Web/API/WebGL_API/WebGL_model_view_projection)に関する記事を調べて、どのように役立つかを覗いてみてください。
`w` 成分には、この記事の範囲外のいくつかの追加の用途があります。 [WebGL モデルビュー投影](/ja/docs/Web/API/WebGL_API/WebGL_model_view_projection)に関する記事を調べて、どのように役立つかを覗いてみてください。

### 行列と点の乗算

Expand Down Expand Up @@ -139,13 +146,13 @@ let identityMatrix = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
let someMatrixResult = multiplyMatrices(identityMatrix, someMatrix);
```

> **警告:** これらの行列関数は、説明を明確にするために書かれており、速度やメモリ管理のためには書かれていません。 これらの関数は多くの新しい配列を作成しますが、これはガベージコレクションのために、リアルタイム操作に特にコストがかかる可能性があります。 実際の製品コードでは、最適化された関数を使用するのが最善です。 [glMatrix](http://glmatrix.net/) は、速度とパフォーマンスに重点を置いたライブラリーの例です。 glMatrix ライブラリーの焦点は、更新ループの前に割り当てられるターゲット配列を持つことです。
> **警告:** これらの行列関数は、説明を明確にするために書かれており、速度やメモリ管理のためには書かれていません。 これらの関数は多くの新しい配列を作成しますが、これはガベージコレクションのために、リアルタイム操作に特にコストがかかる可能性があります。 実際の製品コードでは、最適化された関数を使用するのが最善です。 [glMatrix](https://glmatrix.net/) は、速度とパフォーマンスに重点を置いたライブラリーの例です。 glMatrix ライブラリーの焦点は、更新ループの前に割り当てられるターゲット配列を持つことです。
## 平行移動行列

**平行移動行列**translation matrixは単位行列に基づいており、3D グラフィックスで使用され、3 つの方向(x、y、z)の 1 つまたは複数に点またはオブジェクトを移動します。 平行移動を考える最も簡単な方法は、コーヒーカップを手に取るようなものです。 コーヒーがこぼれないように、コーヒーカップは直立させ、同じ方向に向ける必要があります。 それは、テーブルから離れて空中をあちこちと移動できます。
**平行移動行列** (translation matrix) は単位行列に基づいており、3D グラフィックスで使用され、3 つの方向(`x``y``z`)の 1 つまたは複数に点またはオブジェクトを移動します。 平行移動を考える最も簡単な方法は、コーヒーカップを手に取るようなものです。 コーヒーがこぼれないように、コーヒーカップは直立させ、同じ方向に向ける必要があります。 それは、テーブルから離れて空中をあちこちと移動できます。

コーヒーを口の中に注ぐには、カップを傾けたり回転させたりする必要があるため、実際には平行移動行列だけを使用してコーヒーを飲むことはできません。 これを行うために使用する行列の種類(巧妙に**[回転行列](#rotation_matrix)**と呼ばれます)を後で見ていきます。
コーヒーを口の中に注ぐには、カップを傾けたり回転させたりする必要があるため、実際には平行移動行列だけを使用してコーヒーを飲むことはできません。 これを行うために使用する行列の種類(巧妙に**[回転行列](#回転行列)**と呼ばれます)を後で見ていきます。

```js
let x = 50;
Expand All @@ -164,16 +171,16 @@ let translationMatrix = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1];
```html
<div id="move-me" class="transformable">
<h2>Move me with a matrix</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit...</p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit</p>
</div>
```

最後に、各例で 4x4 マトリックスを生成し、`<div>` のスタイルを更新して、transform を適用し、`matrix3d` に設定します。 行列が 4 行 4 列で構成されている場合でも、行列は 16 個の値の 1 行につぶされていることに注意してください。 行列は常に JavaScript の 1 次元のリストに格納されます。
最後に、各例で 4×4 マトリックスを生成し、`<div>` のスタイルを更新して、transform を適用し、`matrix3d` に設定します。 行列が 4 行 4 列で構成されている場合でも、行列は 16 個の値の 1 行につぶされていることに注意してください。 行列は常に JavaScript の 1 次元のリストに格納されます。

```js
// 行列の配列から matrix3d スタイルプロパティを作成します
function matrixArrayToCssMatrix(array) {
return "matrix3d(" + array.join(",") + ")";
return `matrix3d(${array.join(",")})`;
}

// DOM 要素を取得します
Expand All @@ -186,13 +193,13 @@ let matrix3dRule = matrixArrayToCssMatrix(translationMatrix);
moveMe.style.transform = matrix3dRule;
```

[JSFiddle で観る](https://jsfiddle.net/g24mgw6y)
[JSFiddle で見る](https://jsfiddle.net/tatumcreative/g24mgw6y/)

![行列による平行移動の例](matrix-translation.jpg)

## 拡大縮小行列

**拡大縮小行列**scale matrixは、幅、高さ、奥行きの 3 つの次元の 1 つ以上で何かを大きくまたは小さくします。 典型的な(デカルト)座標では、これによりオブジェクトが対応する方向に伸縮します。
**拡大縮小行列** (scale matrix) は、幅、高さ、奥行きの 3 つの次元の 1 つ以上で何かを大きくまたは小さくします。 典型的な(デカルト)座標では、これによりオブジェクトが対応する方向に伸縮します。

幅、高さ、奥行きのそれぞれに適用する変更の量は、左上隅から右下に向かって斜めに配置されます。

Expand All @@ -204,13 +211,13 @@ let d = 1; // depth (z)
let scaleMatrix = [w, 0, 0, 0, 0, h, 0, 0, 0, 0, d, 0, 0, 0, 0, 1];
```

[JSFiddle で観る](https://jsfiddle.net/fndd6e1b)
[JSFiddle で見る](https://jsfiddle.net/tatumcreative/fndd6e1b/)

![行列による拡大縮小の例](matrix-scale.jpg)

## 回転行列

**回転行列**rotation matrixは、点またはオブジェクトを回転させるために使用します。 回転行列は、拡大縮小行列や平行移動行列よりも少し複雑に見えます。 これは、三角関数を使用して回転を実行します。 このセクションでは、手順を完全な詳細に分解しませんが([Wolfram MathWorld のこの記事](http://mathworld.wolfram.com/RotationMatrix.html)を調べてください)、説明のためにこの例を取り上げます。
**回転行列** (rotation matrix) は、点またはオブジェクトを回転させるために使用します。 回転行列は、拡大縮小行列や平行移動行列よりも少し複雑に見えます。 これは、三角関数を使用して回転を実行します。 この節では、手順を完全な詳細に分解しませんが([Wolfram MathWorld のこの記事](https://mathworld.wolfram.com/RotationMatrix.html)を調べてください)、説明のためにこの例を取り上げます。

まず、行列を使用せずに原点を中心に点を回転させるコードを次に示します。

Expand All @@ -221,7 +228,7 @@ let point = [10, 2];
// 原点からの距離を計算します
let distance = Math.sqrt(point[0] * point[0] + point[1] * point[1]);

// ラジアンで60度に相当
// ラジアンで 60 度に相当
let rotationInRadians = Math.PI / 3;

let transformedPoint = [
Expand All @@ -230,13 +237,13 @@ let transformedPoint = [
];
```

これらのタイプのステップを行列にエンコードし、x、y、z の各次元に対してそれを行うことができます。 以下は、X 軸を中心とした回転の表現です
これらのタイプのステップを行列にエンコードし、`x``y``z` の各次元に対してそれを行うことができます。下記は反時計回りの Z 軸回転を左手座標系で表したものです

```js
let sin = Math.sin;
let cos = Math.cos;

// 注: これらの変換にはパースペクティブがないため
// 注: これらの変換には視点距離がないため
// この時点での回転は div を縮小するためにのみ示されます

let a = Math.PI * 0.3; // ラジアンでの回転量
Expand All @@ -262,11 +269,11 @@ let rotateZMatrix = [
];
```

[JSFiddle で観る](https://jsfiddle.net/9vr2dorz)
[JSFiddle で見る](https://jsfiddle.net/9vr2dorz)

![](matrix-rotation.jpg)
![回転行列の例](matrix-rotation.jpg)

3 つの軸のそれぞれを中心に回転するための回転行列を返す関数のセットを次に示します。 大きな注意点の 1 つは、パースペクティブが適用されていないため、まだとても 3D に感じられない可能性があることです。 平面度flatnessは、カメラが遠くのオブジェクトにズームインで非常に接近したときと同じです遠近感sense of perspectiveがなくなります。
3 つの軸のそれぞれを中心に回転するための回転行列を返す関数のセットを次に示します。 大きな注意点の 1 つは、視点距離が適用されていないため、まだとても 3D に感じられない可能性があることです。 平面度 (flatness) は、カメラが遠くのオブジェクトにズームインで非常に接近したときと同じです遠近感 (sense of perspective) がなくなります。

```js
function rotateAroundXAxis(a) {
Expand All @@ -282,18 +289,18 @@ function rotateAroundZAxis(a) {
}
```

[JSFiddle で観る](https://jsfiddle.net/tk072doc)
[JSFiddle で見る](https://jsfiddle.net/tatumcreative/tk072doc/)

## 行列合成

行列の本当の力は、**行列合成**matrix compositionに由来します。 特定のクラスの行列を掛け合わせると、変換の履歴が保持され、元に戻すことができます。 つまり、平行移動、回転、拡大縮小の行列がすべて組み合わされている場合、行列の順序を逆にして再適用すると、元の点が返されます。
行列の本当の力は、**行列合成** (matrix composition) に由来します。 特定のクラスの行列を掛け合わせると、変換の履歴が保持され、元に戻すことができます。 つまり、平行移動、回転、拡大縮小の行列がすべて組み合わされている場合、行列の順序を逆にして再適用すると、元の点が返されます。

行列を乗算する順序は重要です。 数値を乗算する場合、a \* b = c と b \* a = c はどちらも真です。 例えば、3 \* 4 = 12 と 4 \* 3 = 12 です。 数学では、これらの数値は**可換**commutativeであると説明されます。 順序が入れ替わった場合、行列では同じであることが保証されないため、行列は**非可換**non-commutativeです。
行列を乗算する順序は重要です。 数値を乗算する場合、 a \* b = c と b \* a = c はどちらも正しいです。 例えば、3 \* 4 = 12 と 4 \* 3 = 12 です。 数学では、これらの数値は**可換** (commutative) であると説明されます。 順序が入れ替わった場合、行列では同じであることが保証されないため、行列は**非可換** (non-commutative) です。

もう 1 つのマインドベンダーは、WebGL および CSS での行列乗算は、操作が直感的に発生するのとは逆の順序で発生する必要があることです。 例えば、何かを 80縮小し、200 ピクセル下に移動してから、原点を中心に 90 度回転すると、疑似コードでは次のようになります。
もう 1 つのマインドベンダーは、WebGL および CSS での行列乗算は、操作が直感的に発生するのとは逆の順序で発生する必要があることです。 例えば、何かを 80% 縮小し、200 ピクセル下に移動してから、原点を中心に 90 度回転すると、疑似コードでは次のようになります。

```
transformation = rotate * translate * scale
```plain
transformation = rotate * translate * scale
```

### 複数の変換の合成
Expand All @@ -308,7 +315,7 @@ let transformMatrix = MDN.multiplyArrayOfMatrices([
]);
```

[JSFiddle で観る](https://jsfiddle.net/qxxg3yvc)
[JSFiddle で見る](https://jsfiddle.net/tatumcreative/qxxg3yvc/)

![行列合成の例](matrix-composition.jpg)

Expand All @@ -325,8 +332,8 @@ let transformMatrix = MDN.multiplyArrayOfMatrices([
]);
```

## 行列が重要な理由
## 行列が重要である理由

行列は、空間での幅広い変換を表すことができる数の小さなセットで構成されるため、重要です。 それらはプログラム内で簡単に共有できます。 さまざまな座標空間を行列で記述できます。 一部の行列乗算では、1 つのデータのセットを 1 つの座標空間から別の座標空間に移動します。 行列は、それらを生成するために使用された以前の変形のすべての部分を効果的に記憶します
行列が重要なのは、空間内のさまざまな変換を記述することができる小さな数の集合で構成されているからです。行列はプログラムの中で簡単に共有できます。さまざまな座標空間を行列で記述することができ、いくつかの行列の乗算によって、ある座標空間から別の座標空間へデータ集合を移動させることができます。行列は、それを生成するために使用した前回変換のすべての部分を効果的に記憶します

WebGL で使用する場合、グラフィックスカードは、空間内の多数の点に行列を乗算するのに特に適しています。 点の配置、照明の計算、アニメのキャラクターのポーズなどのさまざまな操作はすべて、この基本的なツールに依存しています。

0 comments on commit 9e6a265

Please sign in to comment.