一.canvas 2D で矩形を描画
canvas 2D で矩形を描画するのは簡単で、数行のコードで簡単に完了します:
<!-- html -->
<h5>canvas2d</h5>
<canvas id="canvas2d" width="400" height="400">
Please use a browser that supports "canvas"
</canvas>
// js
// 要素参照を取得
var canvas2d = document.getElementById('canvas2d');
// 2D context を取得
var ctx = canvas2d.getContext('2d');
// 塗りつぶし色を設定
ctx.fillStyle = 'rgba(255, 0, 255, 0.75)';
// 矩形ブロックを描画
ctx.fillRect(100, 100, 200, 200);
実際の効果は 200x200px の薄紫色の矩形ブロックで、周囲に幅 100px の透明な輪があります。コードの観点から見ると、400x400px の透明な canvas の中心に不透明度 0.75 の 200x200px の紫色の矩形ブロックが塗られたということです
ctx.fillRect(100, 100, 200, 200); から、canvas 2D の座標系は画面座標系と同じで、左上が (0, 0)、x 軸は右が正、y 軸は下が正であることがわかります
ctx.fillStyle = 'rgba(255, 0, 255, 0.75)'; から、rgba() は CSS と同じで、rgb 値は 0255、a は 01 で不透明度を表すことがわかります
二.WebGL で矩形を描画
###1.context を取得
canvas2d.getContext('2d') と同様に、WebGL も専用の context を取得する必要があります:
var webgl = document.getElementById('webgl');
var gl = webgl.getContext('webgl');
ここで WebGL の context を慣例的に gl と命名するのは、今後 API を呼び出す際に OpenGL の呼び出し方と一致させるためです。ええ、見た目重視です
###2.canvas をクリア
canvas 2D が提供する clearRect メソッドと同様に、WebGL にも消しゴムのようなものがあり、しかもより強力で、クリア後の色を指定できます:
// クリアする canvas の色を指定
// パラメータは rgba、範囲 0.0~1.0
gl.clearColor(0.0, 0.0, 0.0, 1.0);
// canvas をクリア
// gl.COLOR_BUFFER_BIT 色バッファ、デフォルトでクリア色 rgba(0.0, 0.0, 0.0, 0.0) 透明黒色、gl.clearColor で指定
// gl.DEPTH_BUFFER_BIT 深度バッファ、デフォルト深度 1.0、gl.clearDepth で指定
// gl.STENCIL_BUFFER_BIT ステンシルバッファ、デフォルト値 0、gl.clearStencil() で指定
gl.clear(gl.COLOR_BUFFER_BIT);
この例では canvas をクリアするのは必須ではありませんが、この方法を必ず使えるようにする必要があります。そうでなければ困ります。canvas 2D で黒色背景を設定する場合、次のようにするからです:
// 黒色背景
ctx.fillStyle = 'rgba(0, 0, 0, 1)';
ctx.fillRect(0, 0, canvas2d.width, canvas2d.height);
同じ思路で、canvas 全体を覆う黒いブロックを描画するのは、WebGL でももちろん可能ですが、非常に面倒です
###3.矩形を描画
矩形を描画するために、まず 8 つのことを行う必要があります:
- シェーダーソースプログラムを記述
シェーダー言語(GLSL ES)を使用してシェーダーソースプログラムを記述し、文字列として WebGL システム内部に渡してコンパイル実行します:
// 0.シェーダーソースプログラム
// 頂点シェーダーソースプログラム
var vsSrc = 'void main() {' +
'gl_Position = vec4(0.0, 0.0, 0.0, 1.0);' + // 座標を設定
'gl_PointSize = 200.0;' + // サイズを設定
'}';
// 断片シェーダーソースプログラム
var fsSrc = 'void main() {' +
'gl_FragColor = vec4(1.0, 0.0, 1.0, 0.75);' + // 色を設定
'}';
GLSL ES の構文は C 言語に似ています。具体的な構文規則は今後のノートで説明します
- シェーダーオブジェクトを作成
言うことはありません。ゲームのルールです
// 1.シェーダーオブジェクトを作成
var vs = gl.createShader(gl.VERTEX_SHADER);
var fs = gl.createShader(gl.FRAGMENT_SHADER);
// 作成結果をチェック
if (vs === null) {
log('gl.createShader(gl.VERTEX_SHADER) failed');
}
if (fs === null) {
log('gl.createShader(gl.FRAGMENT_SHADER) failed');
}
3. ソースプログラムを埋め込む
シェーダーオブジェクトがあれば、シェーダーソースプログラムを投入できます
// 2.ソースプログラムを埋め込む
gl.shaderSource(vs, vsSrc);
gl.shaderSource(fs, fsSrc);
4. コンパイル
ソースコードがあれば、すぐにコンパイルしてエラーが出るか確認します
// 3.コンパイル
gl.compileShader(vs);
gl.compileShader(fs);
// コンパイルエラーをチェック
if (!gl.getShaderParameter(vs, gl.COMPILE_STATUS)) {
log('gl.compileShader(vs) failed');
log(gl.getShaderInfoLog(vs)); // エラー情報を出力
}
if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS)) {
log('gl.compileShader(fs) failed');
log(gl.getShaderInfoLog(fs)); // エラー情報を出力
}
5. プログラムオブジェクトを作成
シェーダーオブジェクトの他に、プログラムオブジェクトも必要です。js と WebGL 内部の通信は主にこのプログラムオブジェクトを通じて行われるので、まず 1 つ作成します
// 4.プログラムオブジェクトを作成
var prog = gl.createProgram();
// 作成結果をチェック
if (prog === null) {
log('gl.createProgram() failed');
}
注意:上記 4 歩はすべて頂点 XX と断片 XX が 1 つずつですが、このステップでは prog オブジェクトは 1 つだけです
- プログラムオブジェクトにシェーダーを割り当て
各 prog には 2 つのシェーダー(1 つの頂点シェーダー、1 つの断片シェーダー)が必要なので、割り当てエラーをチェックする際は 2 と比較して、prog に 2 つの shader がバインドされているか確認します
// 5.プログラムオブジェクトにシェーダーを割り当て
gl.attachShader(prog, vs);
gl.attachShader(prog, fs);
// 割り当てエラーをチェック
if (gl.getProgramParameter(prog, gl.ATTACHED_SHADERS) !== 2) {
log('gl.getProgramParameter(prog, gl.ATTACHED_SHADERS) failed');
}
7. プログラムオブジェクトをリンク
C 言語プログラムの compile -> link -> run と似ています
// 6.プログラムオブジェクトをリンク
gl.linkProgram(prog);
// リンクエラーをチェック
if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
log('gl.linkProgram(prog) failed');
log(gl.getProgramInfoLog(prog));
}
8. プログラムオブジェクトを使用
エラーチェックは行いません。戻り値がないためです
// 7.プログラムオブジェクトを使用
gl.useProgram(prog);
これで、矩形 1 つを描画するための準備作業が完了しました
最後に何かを描画します
// 矩形を描画(1 つの点だが、点のサイズがやや大きい)
gl.drawArrays(gl.POINTS, 0, 1);
三.DEMO
上記コードを含む完全な例については、こちらをご覧ください:http://www.ayqy.net/temp/webgl/hoho/index.html
四.WebGL と canvas 2D の違い
###1.座標系が異なる
WebGL では、canvas の中心が (0, 0)、x 軸は右が正、y 軸は上が正、z 軸は画面から射出して顔を指す方向が正(右手座標系)です
###2.座標値が異なる
WebGL では、キャンバス全体は Rect(-1.0, 1.0, 2, 2) です。つまり canvas の左上座標は (-1.0, 1.0)、右上座標は (1.0, 1.0)、キャンバスサイズは 2x2 です。canvas 自体が 400x400px の場合、WebGL での座標値の単位は 200px になります
注意:座標値以外の属性、例えば上記シェーダーソースプログラム中の gl_PointSize の場合、その単位は依然として px です
###3.rgb 値が異なる
断片シェーダーには色値が使用されています。vec4(r, g, b, a) で、CSS の rgba() と異なり、WebGL では rgb 値の範囲は 0.0~1.0 で、しかも必ず小数形式でなければなりません。vec4 コンストラクタは float 型パラメータのみを受け付け、int は暗黙的に float に変換されません
###4.色の重ね合わせ規則が異なる
DEMO を実行すると、canvas 2D と Web GL で描画された紫色の矩形の色が異なることがわかります。canvas 2D ではまず黒色背景を描画し、その上に紫色の矩形を描画しました。紫色の矩形には 0.25 の透明度があり、最終的に表示される色は紫色と黒色の重ね合わせです。
一方 WebGL では、紫色の矩形の色は黒色と重ね合わせられていません(body 背景色は私たちが紫色の矩形に指定した色で、WebGL で紫黒の重ね合わせが発生していないことがはっきりわかります)。紫色の矩形の背後(z 座標が -0.1)にもう 1 つ黒色の矩形を描画してみましたが、色は依然として重ね合わせられていません。これは特に注意が必要です
五.まとめ
API 使用ステップから見ると WebGL はこれほど面倒です。矩形 1 つを描画するのにこれほど多くのコードを書く必要があります。実際には私たちは 1 つの点(やや大きい点)を描画しただけで、矩形を描画するにはさらに面倒で、4 つの頂点を使用して 2 つの三角片を確定し、それぞれに色を塗りつぶす必要があります
頂点シェーダー、断片シェーダーとは何か?どのような役割があるのか?もう少し複雑な例で説明する必要があります。今後のノートで説明します
参考資料
- 『WebGL プログラミングガイド》
コメントはまだありません