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]) が関連付けられている。WebGLRenderingContextは WebGLContextAttributes オブジェクトの中に context creation parameters を持つ。WebGLRenderingContextは描画バッファーが作成されるたびに設定される actual context parameters を WebGLContextAttributes オブジェクトに持つ。WebGLRenderingContextは最初は設定されていない webgl context lost フラグを持つ。
キャンバスのメソッド呼び出し getContext('webgl') が contextId webgl
[CANVASCONTEXTS] の新しいオブジェクトを返す場合、ブラウザーは以下の手順を実行する必要がある:
新しい
WebGLRenderingContextオブジェクトであるコンテキストを作成する。そのコンテキストのキャンバスをメソッド
getContextが関連付けられているキャンバスとする。新しい
WebGLContextAttributesオブジェクトcontextAttributesを作成する。getContext()に第二引数としてoptionsを指定していた場合、指定した属性をcontextAttributesに設定する。contextAttributesで指定された設定を使用して 描画バッファーを作成 し、その描画バッファーとコンテキストを関連付ける。描画バッファーの作成に失敗した場合は、以下の手順を実行する:
6.1 キャンバスで WebGL コンテキスト作成エラーを発生させる。
6.2
nullを返してこれらの手順を終了する。
新しい
WebGLContextAttributesオブジェクトであるactualAttributesを作成する。新しく作成した描画バッファーのプロパティに基づいて
actualAttributesの属性を設定する。コンテキストの context creation parameters を
contextAttributesに設定する。コンテキストの actual context parameters を
actualAttributesに設定する。コンテキストを返す。
experimental-webgl に関する記述は軽視する。
2.2 The Drawing Buffer¶
API 呼び出しがレンダリングされる描画バッファーは WebGLContextAttributes オブジェクトの生成時に定義されるものとする。以下、描画バッファーの作成方法を定義する。
この表は、描画バッファーを構成するすべてのバッファーごとに、その最小サイズと、デフォルトで定義されているかどうかを示している。
この描画バッファーのサイズは、キャンバスの
widthおよびheightによって決定される。下の表では、
最初に作成されたとき、
サイズが変更されたとき、
または
preserveDrawingBufferコンテキスト作成属性がfalseのときの出現後に
各バッファーがクリアされるべき値も併せて示す。
バッファー |
クリア値 |
最小サイズ |
既定値が存在するか |
|---|---|---|---|
色 |
|
8 ビット |
存在する |
奥行き |
|
16 ビット整数 |
存在する |
ステンシル |
|
8 ビット |
存在しない |
寸法が \({0 \times 0}\) のキャンバスでは \({1 \times 1}\) の
drawingBufferWidth,drawingBufferHeightになる。要求された幅や高さを満たすことができない場合、描画バッファーが最初に作成されたとき、またはキャンバスの幅や高さの属性が変更されたときに、より小さな寸法の描画バッファーが作成される。実際に使用される寸法は実装に依存し、同じアスペクト比のバッファーが作成されることは保証されない。
実際の描画バッファーのサイズは属性
drawingBufferWidthとdrawingBufferHeightから得ることができる。
WebGL の実装では、高解像度のディスプレイで描画バッファーのサイズを自動的に拡縮してはならない。コンテキストの
drawingBufferWidthおよびdrawingBufferHeightは、実装依存の制約を除いて、キャンバスの属性widthおよびheightに可能なかぎり一致しなければならない。
Non-normative
オプションの 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の場合、レンダリング関数が戻ってきた後に、このコンテキストを元画像として使用した操作を実行しようとすると、未定義の動作を引き起こす可能性がある。これには、readPixelsやtoDataURLの呼び出し、他のコンテキストの
texImage2DやdrawImageの呼び出しのソース画像としてのこのコンテキストの使用、このコンテキストのキャンバスからの
ImageBitmap[HTML] の作成
などがある。
Non-normative
描画バッファーを保持することが望ましい場合もあるが、プラットフォームによってはパフォーマンスが著しく低下する可能性がある。可能な限り、このフラグは
falseのままにして、他の手法を使うべきだ。描画バッファーの内容を取得するには、同期的な描画バッファーアクセス、例えば、描画バッファーへのレンダリングを行うのと同じ関数内で
readPixelsやtoDataURLを呼び出す、などの手法を使用できる。一連の呼び出しで同じ描画バッファーにレンダリングする必要がある場合は、 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 では、アプリケーションがレンダリング時に使用するブレンドモードを変更することができる。そのため、描画バッファー内のアルファー値の解釈様式を制御することができる。WebGLContextAttributes の
premultipliedAlpha引数を見ろ。HTML キャンバス API の
toDataURLおよびdrawImageは、premultipliedAlphaコンテキスト生成パラメーターを考慮する必要がある。 WebGL がレンダリングされているキャンバスに対してtoDataURLが呼び出されたとき、要求された画像フォーマットが
premultipliedAlphaを指定しておらず、WebGL コンテキストの
premultipliedAlphaパラメーターがtrueに設定されている場合は、
ピクセル値を逆乗算、すなわち色チャンネルをアルファーチャンネルで除算する必要がある。この操作は非可逆的だ。
WebGL でレンダリングされたキャンバスを
CanvasRenderingContext2DのdrawImageメソッドに渡す場合、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 のための領域を確保するためで、その後
texSubImageやbufferSubData呼び出しを使って変更する。これらの呼び出しに初期データが与えられない場合、WebGL の実装ではその内容をゼロで初期化する。このためには、要求された VBO のサイズに合わせてゼロの一時バッファーを作成し、正しく初期化できなければならない。
テクスチャーや VBO にデータをロードする他のすべての形式は、
ArrayBufferまたは画像などの DOM オブジェクトを含むため、すでに初期化されていなければならない。
WebGL リソースが
drawElementsやdrawArraysなどの呼び出しによってシェーダーからアクセスされる場合、 WebGL の実装はシェーダーが境界外のデータや初期化されていないデータにアクセスさせないものとする。WebGL の実装で実施しなければならない制限事項については 6.6 Enabled Vertex Attributes and Range Checking に記述がある。
ユーザー側に有利なように仕様が決められていることがうかがえる。
4.2 Origin Restrictions¶
情報漏洩を防ぐため、WebGL では次に挙げるものをテクスチャーとしてアップロードすることを禁じる:
WebGLRenderingContextのcanvas要素を含む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";
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およびdo…whileループは、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¶
シェーダーは、アプリケーション自身のデータの外側にある配列要素を読み書きすることはできない。これは、配列型の変数や、配列添字構文を使ってアクセスされる vec3
や mat4 などのベクトル型や行列型を含む。コンパイル中にこのようなアクセスが検出された場合、エラーが発生し、シェーダーのコンパイルができなくなる。そうでなければ、実行時において、範囲外の読み取りは以下のいずれかの値を返さなければならないものとする:
プログラムがアクセス可能な記憶域内の任意の場所から得られる値。
値ゼロ、または読み取りベクトルに対しては
(0, 0, 0, x)の形のベクトル。ここでxはそのベクトル成分の型で表現される有効な値であり、以下のいずれかだ:成分が整数の場合は 0, 1, あるいは表現可能な最大の正の整数値
浮動小数点成分の場合は 0.0 または 1.0
範囲外の書き込みは、破棄されるか、プログラムがアクセス可能な記憶域内の不特定の値を変更する。
Non-normative
この動作は [KHRROBUSTACCESS] で定義されたものと同じだ。
シェーダー内の配列インデックス操作の静的解析を簡素化する制限については 4.3 Supported GLSL Constructs を参照。