2014年5月29日木曜日

シェーダー

今回はシェーダを使ってキャンバスに点を描いてみたいと思います。

前回も書いたように、WebGLは、シェーダがないと何も出力できません。できるのは前回やったようにせいぜいキャンバスを塗りつぶすぐらいです。点1つ描くのにもシェーダの記述が必要になるのです。

シェーダはGPU(グラフィスクボード)で動くプログラムで、JavaScriptからシェーダへデータを渡し、GPU側で処理して高速に描画することができます。

WebGLで使用するシェーダにはバーテックス(頂点)シェーダとフラグメントシェーダの2種類があります。WebGLのプログラミングではこの2種類のシェーダを用意する必要があります。

バーテックスシェーダは読んで字のごとく各頂点単位の処理を実行するプログラムです。WebGLでは何かを描画する時にその描画する形を面(ポリゴン)の集まりとしてデータを定義します。面の最小単位は三角形で、その三角形は3つの線から構成されます。そして線は最低2つの点から構成されます。この1つ1つの点を処理するのがバーテックスシェーダということになります。

Polygon

1つ1つの頂点の座標は通常、3次元空間におけるX,Y,Z座標で定義されます。また3次元空間の中でもどの位置からどこを見ているかによって画面に表示される物体の見え方は変わってきます。一方、出力先の画面は2次元の座標(X,Y座標)なので、画面に正しく表示するためには、3次元空間内の視点からみた内容を画面の座標(スクリーン座標)へ変換する必要があります。この変換を行うのがバーテックスシェーダの仕事です。またバーテックスシェーダでは色の計算に必要な情報を必要に応じてフラグメントシェーダへ渡す役目もあります。

Convert

フラグメントシェーダの仕事は各フラグメントの色を決定することです。バーテックスシェーダで決定された座標や頂点の色、テクスチャ等の情報を元に描画先に出力する色をフラグメント単位で決定します。フラグメントとは画面の1つのピクセルの色に関する情報をひとまとめにした単位と考えることができます。厳密にはフラグメント=ピクセルではない(フラグメントは複数のピクセルから構成される場合もあるし、複数のフラグメントで1つのピクセルを構成する場合もある)のですが、イメージ的には描画先を構成する最小単位と考えればいいと思います。

Shader

バーテックスシェーダは頂点単位で動作しますが、フラグメントシェーダはフラグメント単位で動作するので、実際に実行される回数はフラグメントシェーダの方が圧倒的に多くなります。そのため、フラグメントシェーダーに負荷のかかる計算を記述すると処理が重くなることになります。

バーテックスシェーダーで記述できるならバーテックスシェーダーで記述した方がよいということになります。バーテックスシェーダで決定された情報は頂点間で補間されてフラグメントシェーダーへ渡されます。例えばバーテックスシェーダーで頂点Aで赤、頂点Bで青の情報をフラグメントシェーダーへ渡した場合、フラグメントシェーダーが頂点Aと頂点Bの中間点を処理する時、青と赤の中間色である紫色の情報が渡されることになります。

このように非常に多くの計算が実行されることになりますが、シェーダはGPUで並列に動作することができるので、ポリゴンの全ての色を一瞬で決定することができます。そのため非常に高速に描画することができるのです。

そのシェーダーですが、どこに書くかというと、いろいろやり方はあります。htmlファイル内に記述する方法、JavaScriptに文字列として書く方法、外部ファイルに記述してJavaScriptから読み出す方法など。今回は、HTMLファイル内に記述する方法でやってみたいと思います。


バーテックスシェーダー

まず、バーテックスシェーダーです。main.htmlのheadタグ内に次のように書きます。


: <script id="shader-vs" type="x-shader/v-vertex"> attribute vec4 a_Position; void main() { gl_Position = a_Position; gl_PointSize = 10.0; } </script> :

scriptタグの中に処理内容を記述しています。type="x-shader/x-vertex"という書き方は別に決められたものではなく、単にブラウザがJavaScriptと解釈して実行してしまわないように慣例的にこのように書いています。

WebGLのシェーダーを記述する言語はGLSL(OpenGL Shading Language)と呼ばれるものです。構文はC言語に非常によく似ています。処理の開始時もC言語と同じくmain()関数が呼ばれます。

Attribute変数

GLSLには外部とやととりするために3種類の変数があります。これは一般的なC言語にはないものです。その変数の種類の1つが上ででてきているattributeで宣言されるAttribute変数です。

Attibute変数は頂点毎の属性を扱うもので、例えば、3次元空間の座標や、各頂点の色、テクスチャ座標、法線ベクトル等といったものがあります。頂点毎にどういった情報を持つかはフログラマが自由に決めることができます。但し扱う情報はバーテックスシェーダ内でAttribute変数として宣言する必要があります。これらのデータはJavaScript側からバーテックスシェーダへ渡すことになります。Attribute変数はフラグメントシェーダーでは参照できません。

Varying変数、Uniform変数

残りの2つの変数は、Varying変数とUniform変数です。Varying変数は、バーテックスシェーダーからフラグメントシェーダーへ値を橋渡しする時に使う変数で、Uniform変数は、Attribute変数が頂点単位に定義する値なのに対して、頂点に関係なくグローバルに参照できる値を定義する変数です。

Uniform変数はバーテックスシェーダーとフラグメントシェーダーのどちらでも参照できます。また、Varying変数も両シェーダ間の橋渡しに使用する変数なので両シェーダーから参照できますが、バーテックスシェーダーでは書き込み、フラグメントシェーダーでは読み込みのみができることになります。

詳しくは今後これらを使う時に説明します。

書き方


attribute vec4 a_Position;

上の記述は、a_Positionという名前のvec4型のAttribute変数を宣言しています。a_Positionという名前は別になんでも構いません。ここでは、Attribute変数ということが分かりやすい様に、a_というプレフィックスをつけて、頂点座標ということでPositionと合わせて、a_Positionという名前にしています。

vec4は4つの浮動小数からなるベクトル型です。WebGLは3次元空間のデータを扱うので線形代数学でよく扱われるベクトルや行列計算で扱う型と計算用の関数が予め用意されています。この辺も普通のC言語とは異なる特徴です。ベクトル型としてはvec4の他に浮動小数成分を2つもつvec2と、3つもつvec3という型もあります。

main関数の中では、gl_Positionという変数と、gl_PointSizeという変数にそれぞれ値を設定しています。これらの変数の名前は予約されているもので、必ずこの名前を使わなければいけません。

バーテックスシェーダーでは必ずgl_Positionに点の座標を設定してやる必要があります。この座標は3次現空間上の座標ではなく、画面に表示するために座標変換した後の座標です。ここでは、a_Positionの値をそのまま渡しています。つまり、a_Positionには、画面に表示するための座標をJavaScript側から渡す必要があるということです。もちろんバーテックスシェーダーの中で計算して計算結果をgl_Positionに設定することもできます。

gl_PointSizeは点のサイズを指定するもので、こちらは必ずしも値を設定する必要はありません。今回は点の描画を行うので10.0という値を設定しています。gl_PointSizeに渡す値もAttribute変数にしてもよかったのですが、今回は説明を簡単にするために定数をシェーダーの中で設定するようにしました。

ところで、gl_Positionはvec4型の変数です。画面に表示するための座標なのでvec2型でもよさそうなものですが、実際には4つの成分を持ったベクトルを渡すことになっています。これには幾つか理由があります。ひとつはバーテックスシェーダの処理が終わったあとも奥行きの情報が必要なためです。WebGLで描画する場合、通常は視点の手前、又は奥にある全ての物体を描画するようなことはせず範囲を決めてその範囲内にある物体だけを描画するようにします。また複数の物体はそれぞれの奥行きの前後によって見えるもの(描画が必要な部分)と見えないもの(描画が不要な部分)が決まってきます。その決定はバーテックスシェーダーの処理が終わった後にWebGLで処理されますが、そのためにまだこの時点でも点の奥行きを示す座標が必要なためです。

最後の四つ目の成分は、同時座標と呼ばれるもので、通常wを使って表現され(x, y, z, w)といった書き方がされます。

座標が同時座標を使って表現される場合は、実際の座標は、各軸の成分をwで割った(x/w, y/w, z/w)になります。つまり、(10, 20, 30, 1)と、(20, 40, 60, 2)は同じ座標を示すことになります。またwが0の場合、無限遠の座標を示すことになり、これは例えば太陽からの光のように無限の彼方から一様に注ぐ光の方向を定義するために使われたりします。ただ通常は、wに1を使います。vec2やvec3の型の変数をvec4へ代入した場合も、wの値として1が自動的に補完されます。

バーテックスシェーダーを通過した頂点データ(wが1でない場合、wで割った結果の値)は、X、Y, Zの座標がそれぞれ-1〜1の範囲に収まっている頂点のみが描画対象となります。この範囲を超える部分はフラグメントシェーダーへ渡されることなく切り捨てられることになります。

またシェーダの中で頂点の計算、例えば座標の移動や回転、拡大縮小といった操作をする場合に、ベクトルと4x4の行列のかけ算による計算を行いますが、そのためには、ベクトルが4つの成分を持っていた方が都合がいいのです。4x4行列と計算するためにはベクトルも4つの成分を持っている必要があるからです。

今回は、gl_Positionにa_Positionの値をそのまま渡すようにしたため、a_Positionもvec4型にしてあります。


フラグメントシェーダー

次はフラグメントシェーダーです。バーテックスシェーダーに続けて次のように書きます。


: <script id="shader-fs" type="x-shader/x-fragment"> void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); } </script> :

書き方

scriptタグのtypeは、バーテックスシェーダーと異なり、"x-shader/x-fragment"となっています。これも別に決まっているわけではなく慣例的な書き方です。

フラグメントシェーダーの中で必ずやらなければならないことは、gl_FragColorへ色の値を設定することです。gl_FragColorもWebGLで予約されたvec4型の変数で、ここでは、(1.0, 0.0, 0.0, 1.0)という値を固定で渡しています。これは、RGBAのRとAに1.0を設定し、GとBに0.0を設定するという意味です。各成分に設定する値は0.0〜1.0の範囲で、0.0が最小、1.0が最大となります。つまり、ここでは完全に不透明な赤色を設定していることになります。

以上が、JavaScriptから渡された座標位置に10ピクセルの赤い点を描画するためのシェーダーです。


次はJavaScript側から、頂点データ(Attribute変数)をシェーダー側へ渡し、実際に描画することになりますが、その前に、シェーダーをコンパイルする必要があります。

長くなったので次回へ続きます。

2014年5月26日月曜日

Hello WebGL

とりあえず簡単なWebGLのプログラムを書いてみたいと思います。

WebGLのプログラムでは、HTML5とJavaScript、そしてGLSL(シェーダ言語)の記述が必要になりますが、今日はGLSLは使わないで、HTML5とJavaScriptだけを使います。

WebGLはGLSLの記述無しではほとんど何も出力できませんが、画面を塗りつぶすことぐらいはできます。

HTML5

まず、html5の記述です。

<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>WebGL Test</title> <script src="main.js"></script> </head> <body onload="main()"> <canvas id="canvas" width="400" height="400"></canvas> </body> </html>

特になんの変哲もない簡素なhtmlですが、bodyにcanvasタグを書いています。

canvasはHTML5で導入されたタグで、2Dグラフィックスの描画で利用されますが、WebGLの描画先としてもこのcanvasタグが利用されます。

WebGLのコンテンツ自体はJavaScriptを使って操作します。

ここでは、bodyタグのonloadイベントで、main.jsに記述されたmain()関数を呼び出しています。main.jsは、headタグの中のscriptタグで読み込んでいます。

JavaScript

main.jsの内容は次のようになります。


function main() { var canvas = document.getElementById("canvas"); var gl = canvas.getContext("experimental-webgl"); gl.clearColor(0.0, 0.0, 1.0, 1.0); gl.clear(gl.COLOR_BUFFER_BIT); }

main関数がひとつあるだけです。

htmlに記述したcanvasタグをDOMのAPIを使って取得し、WebGLのコンテキストを取得しています。

後はこの取得したコンテキストに対してWebGL用の命令を発行していきます。

2D描画用のコンテキストであれば、


var ctx = canvas.getContext("2d");

といった書き方でコンテキストを取得しますが、WebGLの場合はgetContext()に渡す引数が違います。

ここでは、"experimental-webgl"という文字列を渡していますが、WebGLが未だ実験的な実装の段階ということでこのような文字列になっています。本来は"webgl"という文字列を使います。実際、ChromeやFirefoxでは、"webgl"でも大丈夫なんですが、Safariだと動きませんでした。

他にも、"webkit-3d"とか、"moz-webgl"といった指定もあるようです。なるべく多くのブラウザとバージョンに対応するには、これら可能性のある全ての文字列を試すようにし、全て失敗した場合はエラーメッセージを表示するなどしないといけませんが、ここではWebGLの機能の説明にフォーカスするため、エラー処理などは省いてあります。本サイトでは基本的に、Mac版のChromeブラウザ(現バージョンのv35)でのみ動作検証をし、それ以外のブラウザ対応のための処理等は省いて説明していきますので予めご了承ください。

コンテキストを取得したら、WebGL用の命令を発行できます。clearColor(...)は、キャンバスをクリアする色を指定する命令で、最初の3つの引数はRGB(赤、緑、,青)、最後の引数はアルファ値(不透明度)をそれぞれ、0.0〜1.0の範囲で渡しています。この辺の値の意味は、グラフィックやプログラミングに慣れた方ならご存知だと思います。ここでは、青の色と不透明度に1.0を指定しているので、塗りつぶし色として完全に不透明な青を指定しています。

次のclear(...)で、実際にキャンバスをクリアしています。ここで指定しているgl.COLOR_BUFFER_BITというのは、WebGLコンテキストに予め定義された定数で、クリアの対象となるバッファの種類を表しています。WebGLにはクリア操作の対象となるバッファが3種類あり、gl.COLOR_BUFFER_BITというのは色に関するバッファを表す定数です。残りの2つは、gl.DEPTH_BUFFER_BITとgl.STENCIL_BUFFER_BITで、それぞれ深度バッファとステンシルバッファを表します。これらのバッファについては今後使う機会が出てきた時に改めて説明します。

実行

index.htmlをブラウザで表示させると、次のように表示されます。

Canvas

canvasタグは複数使うこともできます。あまりそんな使い方はしないと思いますが...

index.html


: <body onload="main()"> <canvas id="canvas" width="400" height="400"></canvas> <canvas id="canvas2" width="300" height="300"></canvas> </body> :

main.js


function main() { var canvas = document.getElementById("canvas"); var gl = canvas.getContext("experimental-webgl"); gl.clearColor(0.0, 0.0, 1.0, 1.0); gl.clear(gl.COLOR_BUFFER_BIT); var canvas2 = document.getElementById("canvas2"); var gl2 = canvas2.getContext("experimental-webgl"); gl2.clearColor(1.0, 0.0, 0.0, 1.0); gl2.clear(gl.COLOR_BUFFER_BIT); }

結果

Canvas2

そしてこんな使い方もあまりしないと思いますが、WebGLとCanvas2Dとの混在も可能です。


function main() { var canvas = document.getElementById("canvas"); var gl = canvas.getContext("experimental-webgl"); gl.clearColor(0.0, 0.0, 1.0, 1.0); gl.clear(gl.COLOR_BUFFER_BIT); var canvas2 = document.getElementById("canvas2"); var ctx = canvas2.getContext("2d"); ctx.save(); ctx.fillStyle = 'rgba(0, 255, 0, 1.0)'; ctx.beginPath(); ctx.arc(70, 70, 60, 0, Math.PI*2, false); ctx.fill(); ctx.restore(); }

Canvas3

次回は、シェーダ(GLSL)を使ってみたいと思います。

2014年5月24日土曜日

WebGLとは

WebGLとは

WebGLは、Wikipediaによると、「ウェブブラウザで3次元コンピュータグラフィックスを表示させるための標準仕様」と書かれています。

標準仕様なので特定メーカの思惑に左右されずいろいろなWebブラウザ上で、3Dコンテンツを表示させることができます。しかもFlashやSilverlight等のようなプラグインを必要としません。

Windowsでも、Macでも、Linuxでも、タブレットでも、スマホでも同じプログラムで同じ様に動かすことができます。

実際はまだ仕様が策定されて日が浅いこともあり、全てのブラウザで動かせるわけではありませんが、それは時間の経過と共に解決されていくしょう。当初はマイクロソフトもIEはOpenGLをサポートしないとアナウンスしていましたが、IE11でとうとう正式にサポートされました。これで主要なほとんどの最新ブラウザでWebGLがサポートされます。タブレットやスマホでは、まだなかなか厳しい状況ですが、コンテンツが増えていけばメーカもサポートせざるを得ない状況になるでしょう。

現在提供されているWebGLのハージョンは1.0ですが、これは、組み込みシステム向けのOpenGLである、OpenGL ES2.0がもとになっています。スマホで使われているのもOpenGL ESです。本家のOpen GL自体のバージョンは4.3まで進んでいますね。

OpenGL ESは、OpenGLから冗長と思われる機能を削ぎ落としてモバイル端末向けに特化したOpenGLのサブセット版です。そしてWebGLは、OpenGL ESと同等の仕様となっています。機能を削ぎ落としたとはいえ、バッファオブジェクトや、テクスチャ、シェーダー等十分な機能をもっておりほとんどの用途で不足を感じることはないと思います。

このサイトでは、管理人自身の勉強も兼ねて、WebGL関連の話題を扱っていく予定です。

対応ブラウザ

これも、Wikipediaからの引用になりますが、今日時点で以下のブラウザでWebGLが動きます。

PC
ブラウザ 対応状況
Google Chromev8は要設定。v9から標準で有効。
Internet Explorerv11から。
Mozilla Firefoxv4から。
Operav12は要設定。v15から標準で有効。
Safariv5.1から。要設定。

モバイル
ブラウザ/OS 対応状況
BlackBerry 10v10から。
Firefox for MobileMaemoはv1.0から。Androidはv4から。
Google Chrome for Androidv25は要設定。v30から標準で有効。
Internet Explorer Mobilev11から。
Opera Mobilev12から。(Android のみ)
Tizenv1.0から。
AndroidブラウザサムソンやHTCなどの一部の端末で対応。
iOS未対応

今の所iPhoneやiPad、Android端末のほとんどで動かないですが、それ以外は大丈夫そうです。

iPhoneで動かないのは、技術的な問題というより、マーケティング的な理由のような気がします。スマホのコンテンツといえばアプリが主流ですからね。

それから、Safariですが、これは初期状態ではWebGLが動かないので設定が必要になります。以下に手順を載せておきます。

  1. 設定画面を開き、詳細タブにある「メニューバーに"開発"メニューを表示」にチェックがついていない場合、これにチェックをつけてください。
  2. メニューバーに「開発」メニューが表示されるので、その中の「WebGLを有効にする」を選択してチェックをつけます。これで使えるようになるはずです。

Window版のSafariでも同じように「開発」メニューを表示する設定があったのですが、「WebGLを有効にする」のメニューが出てきません。もしかしたらWindows版のSafariでは対応していないかもしれません。

既に多くのサイトでWebGLのコンテンツが作られています。例えば、これはGoogleのサンプルサイトです。

https://code.google.com/p/webglsamples/

自分のブラウザでWebGLが動くかどうかは、これらのサイトで実際にコンテンツが表示されるか試してみることで確認できます。

Ogre関連の記事は引っ越しました

Ogre関連の記事はこちらへ引っ越しました。

こっちはWebGL関連の記事を書いていきます。