WebGL Specification 1.0 読書ノート 1 of 4

WebGL Specification を読んでいく。

Abstract

  • 本仕様は HTML 5 要素 canvas [CANVAS] に対する追加的なレンダリングコンテキストおよびサポートオブジェクトについて記述するものだ。

  • このコンテキストは OpenGL ES 2.0 API に忠実に準拠した API を使用してレンダリングをすることができる。

Status of this document

  • 本文書は編集者の草稿だ。この文書を work in progress 以外の目的で引用してはならない。

本ノートもそういうつもりで記していく。

Feedback

  • 本仕様についての一般的な議論はメーリングリストで歓迎される。

  • 本仕様やその適合試験に関するバグは GitHub のアカウントに報告する。プルリクエストもウェルカム。

各 URL は本仕様書のオリジナルを参照して欲しい。当ノートには明記しない。

1 Introduction

Non-normative

  • WebGL はウェブ用に設計された即時モードの 3D レンダリング API だ。

    • OpenGL ES 2.0 から派生したもので、同様のレンダリング機能を HTML のコンテキストで提供している。

    • WebGL は HTML Canvas 要素のレンダリングコンテキストとして設計されている。

    • Canvas の仕様の一部として記述されているインターフェイスは 2D のレンダリングコンテキストである CanvasRenderingContext2D だが、この文書ではもう一つのインターフェイスである WebGLRenderingContext について説明する。こちらはWebGL の API を表す。

  • この API の即時モードの性質は、ほとんどの Web APIとは異なる。

    • WebGL は、どのような使用例にも適用できる柔軟なプリミティブを用意するという方法論を採用している。ライブラリーは WebGL の上に特定の分野に特化した API を提供することができて、 WebGL に利便性の高いレイヤーを追加し、開発を加速、簡略化することができる。

    • OpenGL ES 2.0 の伝統を受け継いでいるため、最新のデスクトップ OpenGL や OpenGL ES 2.0 の開発に慣れている開発者であれば、WebGL の開発に移行するのは簡単なはずだ。

1.1 Conventions

  • 本文には OpenGL ES のマニュアルページへのリンクが多く含まれてるので、当ノートではばっさり削る。

  • OpenGL ES 2.0 [GLES20] と合致するように努力しているが、誤りを含む場合がある。矛盾が生じた場合は、OpenGL ES 2.0 [GLES20] を正とする。

  • 本文書は OpenGL ES 2.0 仕様と併読することを意図している。特に指定のない限り、各メソッドの動作は OpenGL ES 2.0 で定義されている。この仕様は、相互運用性やセキュリティーを担保するために、OpenGL ES 2.0 から分岐している場合がある。

    • OpenGL ES 2.0 には実装上の定義を残している部分があり、それらを当仕様で定義するものが多い。そのような違いについては、6 Differences Between WebGL and OpenGL ES 2.0 でまとめられている。

2 Context Creation and Drawing Buffer Presentation

WebGL API を使用する前に、プログラム作者は以下に示すように、指定された HTMLCanvasElement [CANVAS] または OffscreenCanvas [OFFSCREENCANVAS]WebGLRenderingContext オブジェクトを取得する必要がある。このオブジェクトは OpenGL の状態を管理し、描画バッファーへのレンダリングを行うためのもので、コンテキスト作成時に作成する必要がある。

2.1 Context Creation

本節で述べられている仕様を canvas.getContext("webgl") の呼び出しが実現するものと思われる。


  • WebGLRenderingContext には作成時に設定されるキャンバス([CANVAS], [OFFSCREENCANVAS]) が関連付けられている。

  • WebGLRenderingContextWebGLContextAttributes オブジェクトの中に context creation parameters を持つ。

  • WebGLRenderingContext は描画バッファーが作成されるたびに設定される actual context parametersWebGLContextAttributes オブジェクトに持つ。

  • WebGLRenderingContext は最初は設定されていない webgl context lost フラグを持つ。

キャンバスのメソッド呼び出し getContext('webgl')contextId webgl [CANVASCONTEXTS] の新しいオブジェクトを返す場合、ブラウザーは以下の手順を実行する必要がある:

  1. 新しい WebGLRenderingContext オブジェクトであるコンテキストを作成する。

  2. そのコンテキストのキャンバスをメソッド getContext が関連付けられているキャンバスとする。

  3. 新しい WebGLContextAttributes オブジェクト contextAttributes を作成する。

  4. getContext() に第二引数として options を指定していた場合、指定した属性を contextAttributes に設定する。

  5. contextAttributes で指定された設定を使用して 描画バッファーを作成 し、その描画バッファーとコンテキストを関連付ける。

  6. 描画バッファーの作成に失敗した場合は、以下の手順を実行する:

  7. 新しい WebGLContextAttributes オブジェクトである actualAttributes を作成する。

  8. 新しく作成した描画バッファーのプロパティに基づいて actualAttributes の属性を設定する。

  9. コンテキストの context creation parameterscontextAttributes に設定する。

  10. コンテキストの actual context parametersactualAttributes に設定する。

  11. コンテキストを返す。

experimental-webgl に関する記述は軽視する。

2.2 The Drawing Buffer

API 呼び出しがレンダリングされる描画バッファーは WebGLContextAttributes オブジェクトの生成時に定義されるものとする。以下、描画バッファーの作成方法を定義する。

  • この表は、描画バッファーを構成するすべてのバッファーごとに、その最小サイズと、デフォルトで定義されているかどうかを示している。

    • この描画バッファーのサイズは、キャンバスの width および height によって決定される。

    • 下の表では、

      • 最初に作成されたとき、

      • サイズが変更されたとき、

      • または preserveDrawingBuffer コンテキスト作成属性が false のときの出現後に

      各バッファーがクリアされるべき値も併せて示す。

バッファー

クリア値

最小サイズ

既定値が存在するか

(0, 0, 0, 0)

8 ビット

存在する

奥行き

1.0

16 ビット整数

存在する

ステンシル

0

8 ビット

存在しない

  • 寸法が \({0 \times 0}\) のキャンバスでは \({1 \times 1}\)drawingBufferWidth, drawingBufferHeight になる。

  • 要求された幅や高さを満たすことができない場合、描画バッファーが最初に作成されたとき、またはキャンバスの幅や高さの属性が変更されたときに、より小さな寸法の描画バッファーが作成される。実際に使用される寸法は実装に依存し、同じアスペクト比のバッファーが作成されることは保証されない。

    • 実際の描画バッファーのサイズは属性 drawingBufferWidthdrawingBufferHeight から得ることができる。

  • WebGL の実装では、高解像度のディスプレイで描画バッファーのサイズを自動的に拡縮してはならない。コンテキストの drawingBufferWidth および drawingBufferHeight は、実装依存の制約を除いて、キャンバスの属性 width および height に可能なかぎり一致しなければならない。

Non-normative

  • 上記の制約は、高精細ディスプレイであっても、キャンバス要素がウェブページ上で消費する空間の大きさを変えるものではない。キャンバスの固有寸法 [CANVAS] はその座標空間のサイズに等しく、数値は CSS ピクセルで解釈されるのであって、解像度に依存しない [CSS]

  • WebGL アプリケーションは、プロパティー window.devicePixelRatio などを確認し、キャンバスの幅と高さをその係数で乗じて、CSS の幅と高さを元の幅と高さに設定することで、高解像度のディスプレイ上で、描画バッファーのピクセルと画面上のそれの比率を 1:1 にすることができる。

  • オプションの WebGLContextAttributes オブジェクトを使って、バッファーを定義するかどうかを変更することができる。また、色バッファーにアルファーチャンネルを含めるかどうかを定義するのにも使用できる。

    • 定義する場合、アルファーチャンネルは、HTML 合成器が色バッファーをページの残りの部分と結合するために使用される。

    • WebGLContextAttributes オブジェクトは getContext の最初の呼び出し時にしか用いられない。描画バッファーの作成後にその属性を変更する機能はない。

  • 奥行き、ステンシル、アンチエイリアスの属性は、true に設定されている場合、要求であって要件ではない。WebGL の実装はこれらの属性を考慮するように努力をする必要がある。ただし、これらの属性が false に設定されている場合、WebGL の実装は関連する機能を提供しない。

    • WebGL の実装やグラフィックスハードウェアでサポートされていない属性を組み合わせても、WebGLContextAttributes の作成に失敗することはない。

    • actual context parameters には、作成された描画バッファーの属性が設定される。

    • 属性 alpha, premultipliedAlpha, preserveDrawingBuffer は WebGL の実装に従わなければならない。

  • WebGL は、合成操作の直前にその描画バッファーを HTML ページの合成器に提示するが、それは前回の合成操作以降に以下の少なくとも一つが発生している場合に限る:

    • コンテキストの作成

    • キャンバスのサイズ変更

    • 描画バッファーが現在束縛されているフレームバッファーである間に、clear, drawArrays または drawElements が呼び出された。

  • 描画バッファーが合成のために提示される前に、実装はすべてのレンダリング操作が描画バッファーに flush されていることを確認する。デフォルトでは、合成後、描画バッファーの内容は、上の表に示されているように、それらの既定値に消去されなければならない。

  • この既定の動作を WebGLContextAttributes オブジェクトの属性 preserveDrawingBuffer を設定することで変更できる。

    • このフラグが true の場合、描画バッファーの内容は、作者が消去するか上書きするまで保存される。

    • このフラグが false の場合、レンダリング関数が戻ってきた後に、このコンテキストを元画像として使用した操作を実行しようとすると、未定義の動作を引き起こす可能性がある。これには、

      • readPixelstoDataURL の呼び出し、

      • 他のコンテキストの texImage2DdrawImage の呼び出しのソース画像としてのこのコンテキストの使用、

      • このコンテキストのキャンバスからの ImageBitmap [HTML] の作成

      などがある。

Non-normative

  • 描画バッファーを保持することが望ましい場合もあるが、プラットフォームによってはパフォーマンスが著しく低下する可能性がある。可能な限り、このフラグは false のままにして、他の手法を使うべきだ。

    描画バッファーの内容を取得するには、同期的な描画バッファーアクセス、例えば、描画バッファーへのレンダリングを行うのと同じ関数内で readPixelstoDataURL を呼び出す、などの手法を使用できる。

    一連の呼び出しで同じ描画バッファーにレンダリングする必要がある場合は、 Frame buffer オブジェクトを使用することができる。

  • バッファー作者が他のプロセスからバッファーの内容にアクセスできないことを保証する限り、実装は、必要な描画バッファーの暗黙の消去操作を最適化することができる。例えば、バッファー作者が明示的な消去を行った場合、暗黙の消去は必要ない。

2.3 The WebGL Viewport

  • OpenGL は、描画バッファー内のレンダリング結果の配置を定義する状態の一部として、長方形のビューポートをやりくりする。WebGL コンテキストの作成時に、ビューポートを、

    • 原点が (0, 0) で、

    • 幅と高さが (gl.drawingBufferWidth, gl.drawingBufferHeight) に等しい長方形に初期化する。

  • WebGL の実装では、キャンバス要素のサイズ変更に応じて OpenGL ビューポートの状態に影響を与えてはならない。

Example I

WebGL プログラムにビューポートを設定するロジックが含まれていないと、キャンバスのサイズが変更された場合に適切に対処できないことに注意。次の例は、WebGL プログラムが(ユーザーの操作によるものではなく)プログラムによってキャンバスのサイズを変更する方法を示す:

const canvas = document.getElementById('canvas1');
const gl = canvas.getContext('webgl');
canvas.width = newWidth;
canvas.height = newHeight;
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);

そうする根拠としては、ビューポートを自動的に設定すると、それを手動で設定するアプリケーションと干渉するからだ。アプリケーションは onresize イベントハンドラーを使用して、キャンバスのサイズの変更に応答し、OpenGL ビューポートを設定することが期待される。

2.4 Premultiplied Alpha, Canvas APIs and texImage2D

  • OpenGL API では、アプリケーションがレンダリング時に使用するブレンドモードを変更することができる。そのため、描画バッファー内のアルファー値の解釈様式を制御することができる。WebGLContextAttributespremultipliedAlpha 引数を見ろ。

  • HTML キャンバス API の toDataURL および drawImage は、 premultipliedAlpha コンテキスト生成パラメーターを考慮する必要がある。 WebGL がレンダリングされているキャンバスに対して toDataURL が呼び出されたとき、

    • 要求された画像フォーマットが premultipliedAlpha を指定しておらず、

    • WebGL コンテキストの premultipliedAlpha パラメーターが true に設定されている場合は、

    ピクセル値を逆乗算、すなわち色チャンネルをアルファーチャンネルで除算する必要がある。この操作は非可逆的だ。

  • WebGL でレンダリングされたキャンバスを

    • CanvasRenderingContext2DdrawImage メソッドに渡す場合、 CanvasRenderingContext2D の実装の乗算の必要性に応じて、描画操作中にレンダリングされた WebGL 内容を変更する必要がある場合とない場合がある。

    • texImage2D に渡す場合、渡されたキャンバスの premultipliedAlpha コンテキスト作成パラメーターと、送信先の WebGL コンテキストの UNPACK_PREMULTIPLY_ALPHA_WEBGL ピクセル格納パラメーターの設定に応じて、ピクセルデータを事前に乗算された形式に変更したり、乗算された形式から変更したりする必要がある。


WebGL でもアルファブレンディングは難しい仕事らしい。

3 WebGL Resources

  • OpenGL は、その状態の部分として、いくつかの型のリソースを統制している。これらのオブジェクトには整数の名前が付けられ、それにより識別され、さまざまな作成コールによって OpenGL から得る。一方、WebGL はこれらのリソースを DOM オブジェクトとして表現する。各オブジェクトは、WebGLObject インターフェースから派生している。現在サポートされているリソースは次のようなものだ:

    • テクスチャー

    • バッファー (e.g. VBO)

    • フレームバッファー

    • レンダーバッファー

    • シェーダー

    • プログラム

  • インターフェース WebGLRenderingContext には、型ごとに WebGLObject のサブクラスを作成するためのメソッドが用意されている。基礎にあるグラフィックライブラリーから来るデータは、これらのオブジェクトに格納され、完全に管理される。

  • DOM オブジェクトは、オーナーが明示的な参照を保持している間だけでなく、内包されているグラフィックスライブラリーが使用している間じゅう存続する。

  • DOM オブジェクトが破壊されると、そのリソースに削除のマークを付ける。破壊される前にオブジェクトを削除するようにマークしたい場合、オーナーは deleteTexture などの、それぞれに対応する delete 関数を明示的に呼び出せる。

4 Security

この章はなぜかセキュリティーという名前だ。

4.1 Resource Restrictions

  • テクスチャーや頂点バッファーオブジェクト (VBO) などの WebGL リソースは、ユーザーデータの初期値を含まずに作成された場合でも、初期化されたデータを含まねばならない。

    • 初期値なしでリソースを作成するのは、テクスチャーや VBO のための領域を確保するためで、その後 texSubImagebufferSubData 呼び出しを使って変更する。

    • これらの呼び出しに初期データが与えられない場合、WebGL の実装ではその内容をゼロで初期化する。このためには、要求された VBO のサイズに合わせてゼロの一時バッファーを作成し、正しく初期化できなければならない。

    • テクスチャーや VBO にデータをロードする他のすべての形式は、ArrayBuffer または画像などの DOM オブジェクトを含むため、すでに初期化されていなければならない。

  • WebGL リソースが drawElementsdrawArrays などの呼び出しによってシェーダーからアクセスされる場合、 WebGL の実装はシェーダーが境界外のデータや初期化されていないデータにアクセスさせないものとする。


ユーザー側に有利なように仕様が決められていることがうかがえる。

4.2 Origin Restrictions

情報漏洩を防ぐため、WebGL では次に挙げるものをテクスチャーとしてアップロードすることを禁じる:

  • WebGLRenderingContextcanvas 要素を含む Document の発信元と同じではない発信元を持つイメージまたはビデオ要素

  • ビットマップの origin-clean フラグが false に設定されている canvas 要素

  • ビットマップの origin-clean フラグが false に設定されている ImageBitmap オブジェクト

texImage2D メソッドまたは texSubImage2D メソッドが、これらの制限に違反する

  • HTMLImageElement,

  • HTMLVideoElement,

  • HTMLCanvasElement または

  • ImageBitmap

を含む正しい引数で呼び出された場合に SECURITY_ERR 例外を送出するものとする。

Non-normative

  • WebGL ではシェーダーを使用して GPU にアップロードされたテクスチャーの内容を間接的に推測することができることから、クロスドメインメディアの使用に 2D キャンバスレンダリングコンテキストなどの他の API よりも強い制限を課すのは当然だ。

  • WebGL アプリケーションは、目的のメディアを置いているサーバーの許可を得て、 Cross-Origin Resource Sharing [CORS] を使用して、他のドメインから来た画像やビデオを利用できる。

    • このようなメディアを使用するには、アプリケーションとサーバーの間でその許可をやりとりする必要がある。

    • CORS を利用して他のドメインから画像やビデオの要素を取得すると、これらの要素の発信元は含まれる Document [HTML] のものに設定される。

Example II

次の例では、別のドメインから送られてくる画像に対して CORS リクエストを発行する方法を示している。画像は、認証情報 (cookie) なしでサーバーから得られる:

const gl = document.querySelector("canvas").getContext("webgl");
const image = new Image();

// The onload handler should be set to a function which uploads the ``HTMLImageElement``
// using texImage2D or texSubImage2D.
image.onload = ...;
image.crossOrigin = "anonymous";
image.src = "http://other-domain.com/image.jpg";
  • なお、これらのルールは、WebGL を使用してレンダリングされた canvasorigin-clean フラグが決して false に設定されないことを含意することに注意。

  • 詳しくは以下を見ろとある:

4.3 Supported GLSL Constructs

WebGL 1.0 における GLSL の仕様。基本的には OpenGL ES の GLSL ver 1.0 [GLES20GLSL] であり(これは別にノートをとる予定)、そこからいくつかの機能を削ったものとみなしてよいようだ。


WebGL は、The OpenGL ES Shading Language, Version 1.00 に準拠し、Appendix A のセクション 4 および 5 で義務付けられている最小機能を超えないシェーダーしか受け入れてはならない。具体的には:

  • デスクトップ版 OpenGL など、他のバージョンの GLSL で利用可能な状態変数や関数を参照するシェーダーに対して、ロードを許可しない。

  • for ループは Appendix A の構造的制約に従うものとする。

  • while および dowhile ループは、Appendix A ではオプションとなっているため、許可しない。

  • Appendix A では、配列のインデックス付けの特定の形式を義務付けている。例えば、フラグメントシェーダー内では、インデックス付けは constant-index-expression でしかできない([GLES20GLSL] 参照)。 WebGL API は Appendix A で義務付けられているインデックス付与の形式しかサポートしない。

前述の仕様にある予約済み識別子に加えて、webgl_ および _webgl_ で始まる識別子が WebGL のために予約済みだ。これらの接頭辞で始まる関数、変数、構造体名、構造体フィールドを宣言しているシェーダーのロードを許可してはならない。

WebGL 1.0 では、シェーダーの中で行継続文字 \ を追加サポートする必要がある。

4.4 Defense Against Denial of Service

Non-normative

  • レンダリングに時間がかかることはたいへん起こりがちだ。レンダリングがなくても時間がかかるスクリプトについても言えることなのだが、長時間の描画呼び出しはブラウザーだけでなく、ウィンドウシステム全体の操作性を損ねる可能性が高い。

  • この問題を防衛するのに入力シェーダーの構造に何か制約を加えるということは、一般的にはできない。

  • ブラウザーは過度に長い描画時間や、それに付随する操作性の低下を防ぐためにセーフガードを実装する必要がある。次のようなセーフガードを推奨する:

    • 多数の要素を含む描画呼び出しを、より小さなものに分割する。

    • 個々の描画呼び出しにタイミングを合わせ、特定のタイムアウトを超えた場合には、そのページでの追加的描画を禁止する。

    • ユーザーレベル、グラフィックス API レベル、OS レベルのどれかで利用可能な監視機能を使用して、描画呼び出しの継続時間を制限する。

    • ブラウザーのグラフィックレンダリングを、アプリケーションの状態を失うことなく終了および再起動できる別個のシステムプロセスに分離する。

OS やグラフィックス API 層の基盤は時間の経過とともに改善されることが期待されるため、これらの保護機能の正確な性質を規定することはしない。

4.5 Out-of-Range Array Accesses

シェーダーは、アプリケーション自身のデータの外側にある配列要素を読み書きすることはできない。これは、配列型の変数や、配列添字構文を使ってアクセスされる vec3mat4 などのベクトル型や行列型を含む。コンパイル中にこのようなアクセスが検出された場合、エラーが発生し、シェーダーのコンパイルができなくなる。そうでなければ、実行時において、範囲外の読み取りは以下のいずれかの値を返さなければならないものとする:

  • プログラムがアクセス可能な記憶域内の任意の場所から得られる値。

  • 値ゼロ、または読み取りベクトルに対しては (0, 0, 0, x) の形のベクトル。ここで x はそのベクトル成分の型で表現される有効な値であり、以下のいずれかだ:

    • 成分が整数の場合は 0, 1, あるいは表現可能な最大の正の整数値

    • 浮動小数点成分の場合は 0.0 または 1.0

範囲外の書き込みは、破棄されるか、プログラムがアクセス可能な記憶域内の不特定の値を変更する。

Non-normative