はじめに
Three.js や Babylon.js を使えば、数十行で3Dシーンが動きます。
その一方で「中で何が起きているのか分からないまま使っている」という感覚を持ったことはないでしょうか。
今回読んだ『初めてのWebGL 2 第2版』は、その違和感に正面から答えてくれる一冊でした。
本書は、WebGL 2 の API を直接触りながら 小さな3Dライブラリを一から育て、最終的に自動車の3Dビューアを作る という構成になっています。
この記事では、設計・アーキテクチャに関心のあるエンジニア向けに、次の観点で本書をレビューします。
- 本書が何を教えてくれるのか
- 各章で何が学べるのか
- 設計の視点から見て面白かったところ
- 読む前に知っておきたい注意点
記事中のコードは、理解のために私が書き起こした短いスケッチです。
本書のサンプルコードそのものではありません。実際のコードは書籍と公式のサンプルリポジトリを参照してください。
書籍情報
| 項目 | 内容 |
|---|---|
| 書名 | 初めてのWebGL 2 第2版 ―JavaScriptで開発するリアルタイム3Dアプリケーション |
| 著者 | Farhad Ghayour, Diego Cantor |
| 訳者 | あんどうやすし |
| 出版社 | オライリー・ジャパン |
| 初版 | 2021年7月 |
| 原書 | Real-Time 3D Graphics with WebGL 2 (2nd Edition) |
| 対象 | JavaScript の基礎があり、ブラウザで3Dアプリを作りたい人 |
一言でいうと
「自分用の Three.js もどき」を作りながら、WebGL 2 の仕組みを体で覚える本
訳者まえがきでも、既存ライブラリに頼る人が土台を理解するための本という位置づけが語られています。
トピックを辞書的に並べるのではなく、章ごとにライブラリを少しずつ拡張していくため、コードが多く、手を動かす前提 の本です。
全体の構成
12章は大きく次の流れに分けられます。
| 区分 | 章 | 主な内容 |
|---|---|---|
| 基礎 | 1〜2章 | canvas と WebGL コンテキスト、レンダリングパイプライン、バッファ、VAO |
| 見た目 | 3〜4章 | 光源・法線・マテリアル、シェーダー、カメラと行列 |
| 動き | 5章 | 行列スタック、アニメーション、タイミング制御 |
| 表現 | 6〜7章 | 色、深度、ブレンディング、テクスチャ |
| 操作 | 8章 | ピッキング(クリックで物体を選ぶ) |
| 統合 | 9章 | 自動車ショールームの完成 |
| 発展 | 10〜12章 | 応用テクニック、WebGL 2 の新機能、周辺エコシステム |
学習の流れを図にすると、次のようなイメージです。
各章のレビュー
1章 イントロダクション
最初の章は、いきなり3Dを描かず、WebGL の「立ち位置」を整理することから始まります。
- WebGL の歴史(なぜブラウザで GPU を扱えるようになったのか)
- canvas 要素と WebGL 2 コンテキストの取得
- WebGL は ステートマシン であること
- レンダリング方式の違い(ソフトウェア / ハードウェア、サーバー / クライアント、保持モード / 即時モード)
設計面で特に重要なのは、最後の「保持モード」と「即時モード」の対比です。
- 保持モード:ライブラリがシーンを覚えていて、変更を指示すると再描画してくれる
- 即時モード:毎フレーム、アプリ側がすべての描画命令を出す
WebGL は後者です。自由度と最適化の余地が大きい代わりに、状態管理とシーン管理は自分で設計する ことになります。
この前提を最初に押さえておくと、後の章で出てくる Scene や Transforms といった抽象化の動機がよく分かります。
2章 レンダリング
本書の土台となる章で、ここでの理解が以降すべてに効いてきます。
扱われる主要な要素は次のとおりです。
| 用語 | 役割 |
|---|---|
| VBO | 頂点データを持つバッファ |
| IBO | 頂点の接続関係(インデックス)を持つバッファ |
| 頂点シェーダー | 頂点ごとに GPU 上で実行される関数 |
| フラグメントシェーダー | ピクセル(フラグメント)の色を決める関数 |
| attribute | 頂点ごとに値が変わる入力 |
| uniform | 1回の描画呼び出しの間は変わらない入力 |
| varying | 頂点シェーダーからフラグメントシェーダーへ渡す補間される値 |
| VAO | attribute の設定一式をまとめて保存するオブジェクト |
これらがパイプラインでどうつながるかを、簡略化して書くと次のようになります。
この章で印象に残ったポイントを挙げます。
-
WebGL は結局ラスタライザである
点・線・三角形を描くだけで、それ以上のことは自分でシェーダーとして書く、という割り切りが明確です。 -
drawArraysとdrawElementsの使い分け
インデックスを使うと同じ頂点の再計算を避けられる、というコスト面の説明が丁寧です。 -
VAO の導入
状態をグローバルに触り続けると描画のたびに設定が必要になりますが、VAO にまとめれば初期化時に一度設定して描画時は bind するだけになります。
VAO の考え方を、私なりに書き起こすと次のようになります。
// 初期化時:バッファと attribute の設定を VAO に閉じ込める
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
// ... VBO / IBO の作成と attribute 設定 ...
gl.bindVertexArray(null);
// 描画時:VAO を bind するだけで済む
gl.bindVertexArray(vao);
gl.drawElements(gl.TRIANGLES, indexCount, gl.UNSIGNED_SHORT, 0);
gl.bindVertexArray(null);
また、後半では JSON 形式のジオメトリを fetch で読み込む流れも紹介されます。
「描画ロジック」と「データ」を分離する最初の一歩として、設計的にも自然な流れです。
3章 光源
ここから3Dらしい見た目の話になります。理論パートと実装パートがバランスよく配置されています。
理論面
- 点光源と平行光源の違い
- 法線の役割と、共有頂点の法線の求め方
- シェーディング(補間の方式)とライティング(反射モデル)を 別の概念として区別する こと
- グーローシェーディング(頂点で計算)とフォンシェーディング(フラグメントで計算)
- ランバート反射と、環境光・拡散反射・鏡面反射を組み合わせたフォン反射
「シェーディング」と「ライティング」は混同されがちですが、本書は「計算をどこで行うか」と「何を計算するか」という二軸で整理しています。
この切り分けは、シェーダーの設計を考えるうえで非常に役に立ちました。
実装面
- ESSL(OpenGL ES Shading Language)の型、修飾子、ベクトルの swizzle
- WebGL 2 では
attribute/varyingがin/outに変わったこと - シェーダー先頭の
#version 300 esが必須であること - フラグメントシェーダーの出力変数を自分で宣言する必要があること
WebGL 1 の記事や情報が多い中で、2 の流儀を最初から学べるのは本書の大きな利点です。
4章 カメラ
WebGL には「カメラ」というオブジェクトが存在しません。
この章は、その事実から出発して、カメラを 行列の組み合わせとして実現する 方法を解説します。
- モデルビュー行列、投影行列、法線行列の役割
- アフィン変換と同次座標
- カメラ行列はモデルビュー行列の逆行列であるという関係
- 軌道カメラ(orbiting)と追跡カメラ(tracking)
- 平行投影と透視投影
「カメラを動かす」と「世界を逆に動かす」が数学的に等価、という説明は、実装を読むうえで何度も立ち返ることになる重要な考え方です。
線形代数に自信がなくても、行列演算ライブラリに任せつつ概念を押さえる、というスタンスで書かれているので読み進めやすいと感じました。
5章 アニメーション
静的なシーンを動かす章です。キーワードは次の3つです。
-
グローバル変換とローカル変換
カメラ移動は全オブジェクトに効く(グローバル)。個々の物体の動きは別に持つ(ローカル)。 -
行列スタック
グローバルな行列を push で退避し、ローカル変換を掛けて描画し、pop で戻す。 -
タイミング戦略
requestAnimationFrameとタイマーの違い、フォーカスを失ったときの扱い。
行列スタックは、描画前後の状態の保存と復元という、設計パターンとしても馴染みのある形をしています。
アニメーションの速度を描画頻度に依存させない、という指針も実務に直結する内容でした。
6章 色、奥行き、半透明
見た目の表現力を一気に引き上げる章です。
- 頂点ごと・フラグメントごと・一定の色の3通りの扱い
- 複数光源と、uniform 配列を使った一般化
- 点光源から作るスポットライト
- 深度テストとアルファブレンディング
- 半透明は描画順序に強く依存する こと
- カリングを使った、単体オブジェクトの半透明表現
特に面白かったのは「アルファ値を設定しただけでは半透明にならない」という話です。
ブレンディングの有効化、奥から手前への描画順、必要に応じたカリング、という複数の条件が揃って初めて期待通りに見えます。
これは Three.js などを使っているときに遭遇する「透明物が変に見える」問題の根本原因そのものでした。
7章 テクスチャ
テクスチャの作成と適用、テクスチャ座標とマッピング、複数テクスチャ、キューブマップを扱います。
画像を貼るだけの話に見えて、実際にはシェーダーとバッファと状態管理が絡み合う領域です。
ここまで読み進めてきた読者であれば、その複雑さを構造として理解できるようになっているはずです。
8章 ピッキング
3D空間のオブジェクトをクリックで選択する仕組みです。
マウス座標からどのオブジェクトを指したかを判定し、アプリ固有の処理を差し込めるフック(コールバック)も用意します。
「描画」と「入力」をつなぐ部分で、UIとしての3Dアプリを作るなら避けて通れない章です。
9章 WebGLアプリケーション
ここまでの部品を統合し、自動車ショールームを完成させる章です。
-
Program、Scene、Clock、Camera、Controls、Transforms、LightsManagerといったクラスの組み立て - Blender で作ったモデルを OBJ 経由でアプリ用 JSON に変換する流れ
- Blender のマテリアル情報を扱えるシェーダーの作成
個別の技術から「アプリの形」へ視点が上がる章で、設計の観点では本書のハイライトだと感じました。
共通機能を common ディレクトリに切り出し、章ごとに拡張していく構成も、リファクタリングの教材として優れています。
10〜12章 発展と周辺
- 10章:ポストプロセッシング、ポイントスプライト、法線マッピング、レイトレーシングなどの応用テクニック
- 11章:WebGL 2 の主な機能、WebGL 1 からの移行方法
- 12章:Three.js、Babylon.js、A-Frame、StackGL、Unity、PlayCanvas など周辺ツールの紹介、ビジュアル回帰テスト、3D再構築、物理ベースレンダリング、コミュニティ
12章のテスト戦略が個人的には新鮮でした。
canvas の中身は DOM のように調べられない「ブラックボックス」なので、スクリーンショット比較によるビジュアル回帰テストや、シーン内の要素を問い合わせられるようにする内部公開の工夫が紹介されています。
設計・アーキテクチャの視点で面白かったところ
ここからは、書評というより個人的な読みどころです。
1. 状態機械をどう扱うか
WebGL はグローバルな状態を持つステートマシンです。
本書は、この性質を隠さずに見せたうえで、VAO や行列スタックといった「状態を安全に扱うための仕組み」を段階的に導入します。
| 課題 | 本書での解決策 |
|---|---|
| attribute 設定が描画のたびに必要 | VAO にまとめる |
| ローカル変換でグローバル行列が壊れる | 行列スタックで保存と復元 |
| アニメーションが描画頻度に依存する | タイミング戦略で分離する |
| 描画順で半透明が壊れる | Scene に描画順の操作を持たせる |
2. 「uniform は描画ごとの定数」という整理
attribute は頂点ごと、uniform は1回の描画呼び出しの間は一定、という区別が最初に丁寧に説明されます。
この区別を軸にすると、光源やマテリアルをどこで持つか、というデータ設計の判断がしやすくなります。
3. 共通部品を育てていく構成
章を追うごとに utils、Program、Scene などが拡張されていきます。
完成形だけを見せるのではなく、必要になった時点で抽象化する 過程が追えるため、「なぜこの形になったのか」が分かります。
4. 抽象化の「下」を知る価値
12章では既存ライブラリが紹介されます。
本書を読み終えてからこれらを眺めると、各ライブラリの API が何を肩代わりしてくれているのかが見えてきます。
ライブラリ選定の判断材料としても効いてきます。
読む前に知っておきたい注意点
次の点は、購入前に把握しておくとミスマッチを避けられます。
- コード量が多い:流し読みでも学びはありますが、手を動かさないと身につきにくい構成です。
- 線形代数の知識があると楽:必須ではありませんが、行列やベクトルの基礎に触れておくと理解が早いです。
- ブラウザ環境の準備が必要:サンプルはローカルのウェブサーバー経由で動かす前提です。
- 仕様やツールは変わりうる:2021年刊行のため、周辺ライブラリやブラウザ対応状況は最新の情報で確認してください。
- 実務ではライブラリ併用が現実的:本書の目的は「土台の理解」であり、そのまま製品開発に使うライブラリを作る本ではありません。
こんな人におすすめ
- Three.js などを使っていて、内部の仕組みを理解したい人
- 状態管理や抽象化の設計を、グラフィックスを題材に学びたい人
- WebGL 1 の情報は見たが、WebGL 2 の書き方を体系的に知りたい人
- 手を動かしながらじっくり学びたい人
- 最短でデモを作りたいだけの人(ライブラリの公式ドキュメントのほうが早いです)
- 最新のレンダリング技術(WebGPU など)を学びたい人
読み方のコツ
私が考える進め方(クリックで展開)
- まず 1〜2章を丁寧に読み、ステートマシンとパイプラインの全体像を掴む
- 3〜4章は、数式を完全に追うより「どの行列が何を担当するか」を表にまとめる
- 5〜6章は、サンプルのパラメータを自分で書き換えて挙動を確認する
- 9章で全体の設計を俯瞰し、自分なりにクラス構成を図にしてみる
- 12章を読んだうえで、既存ライブラリのソースを少し覗いてみる
まとめ
『初めてのWebGL 2 第2版』は、次のような一冊でした。
- ライブラリの「中身」を、小さく作りながら理解できる
- ステートマシンという性質と、それを扱う設計の工夫が学べる
- WebGL 2 の書き方を最初から体系的に身につけられる
- コード量は多いが、そのぶん手応えがある
「使えるけれど分かっていない」状態から抜け出したい方にとって、遠回りに見えて確実な道筋を示してくれる本だと思います。
3Dに限らず、状態管理や抽象化の設計力を鍛える題材としても価値があると感じました。