シェーダのコンパイルとリンク
https://wgld.org/d/webgl/w011.html
レンダリングに至る手順を大まかに確認
HTML から canvas エレメントを取得
canvas から WebGL コンテキストの取得
シェーダのコンパイル
モデルデータを用意
頂点バッファ( VBO )の生成と通知
座標変換行列の生成と通知
描画命令の発行
canvas を更新してレンダリング
ここに挙げたなかで、現時点で概念を全く解説していないのは最後の二つ、
描画命令に関する部分と
canvas の更新だけです。
HTML と canvas の処理
code:html
<html>
<head>
<title>WebGL TEST</title>
<script src="script.js" type="text/javascript"></script>
<script src="minMatrix.js" type="text/javascript"></script>
<script id="vs" type="x-shader/x-vertex">
attribute vec3 position;
uniform mat4 mvpMatrix;
void main(void){
gl_Position = mvpMatrix * vec4(position, 1.0);
}
</script>
<script id="fs" type="x-shader/x-fragment">
void main(void){
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
</script>
</head>
<body>
<canvas id="canvas"></canvas>
</body>
</html>
頂点シェーダの処理
code:glsl
attribute vec3 position;
uniform mat4 mvpMatrix;
void main(void){
gl_Position = mvpMatrix * vec4(position, 1.0);
}
ここでは一つの attribute 変数と、
一つの uniform 変数が出てきていますね。
position は変数の型が vec3 で宣言されていることからもわかるとおり、三つの要素を持つベクトルです。
これが頂点の位置情報
一方 uniform 宣言されている変数 mvpMatrix は、
変数の型が mat4 であることから 4 x 4 の正方行列だということがわかります。
ここには、モデル・ビュー・プロジェクションの各変換行列を掛け合わせた座標変換行列が入ってきます。
今回の頂点シェーダでは、座標変換行列を用いて頂点の座標位置を変換するだけですので、双方を掛け合わせます。
この際、 position を正しく行列と掛け合わせるために、組み込みの vec4 関数で四つの要素を持つベクトルに変換して掛け合わせています。
最終的な計算結果が gl_Position に代入されて、頂点シェーダは完結します。
フラグメントシェーダの処理
code:glsl
void main(void){
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
今回は真っ白にする
色に関する情報は、基本的に vec3 か、もしくは vec4 で表されることが多いです。
これは単純に RGB や RGBA が、3 ~ 4 の要素数だからです。
ソースをコンパイルしてシェーダを生成
さて、続いては頂点シェーダとフラグメントシェーダのコンパイルについて見てみます。
コンパイルとは言っても、特別なコンパイラなどは必要ありません。
コンパイル自体は WebGL 内でメソッドを呼び出すだけで実行できます。
シェーダのソースからコンパイルされ、実際にシェーダが生成されるまでの一連の流れを一つの関数にしてみます。
シェーダを生成・コンパイルする関数
code:js
function create_shader(id){
// シェーダを格納する変数
var shader;
// HTMLからscriptタグへの参照を取得
var scriptElement = document.getElementById(id);
// scriptタグが存在しない場合は抜ける
if(!scriptElement){return;}
// scriptタグのtype属性をチェック
switch(scriptElement.type){
// 頂点シェーダの場合
case 'x-shader/x-vertex':
shader = gl.createShader(gl.VERTEX_SHADER);
break;
// フラグメントシェーダの場合
case 'x-shader/x-fragment':
shader = gl.createShader(gl.FRAGMENT_SHADER);
break;
default :
return;
}
// 生成されたシェーダにソースを割り当てる
gl.shaderSource(shader, scriptElement.text);
// シェーダをコンパイルする
gl.compileShader(shader);
// シェーダが正しくコンパイルされたかチェック
if(gl.getShaderParameter(shader, gl.COMPILE_STATUS)){
// 成功していたらシェーダを返して終了
return shader;
}else{
// 失敗していたらエラーログをアラートする
alert(gl.getShaderInfoLog(shader));
}
}
この関数を利用する際には、引数に script タグの id 属性を文字列で渡します。関数内部で id 属性を元にエレメントを参照します。
シェーダを生成するには、WebGL のメソッドである createShader を使います。
このメソッドによってシェーダを生成する際に、
それが頂点シェーダなのかフラグメントシェーダなのかによって与える引数が変わってきます。 gl.VERTEX_SHADER を指定した場合には頂点シェーダが
gl.FRAGMENT_SHADER を指定した場合にはフラグメントシェーダが生成されます。
生成されたシェーダに、まずはソースを割り当てます。
shaderSource メソッドを使って、
第一引数に対象となるシェーダオブジェクト、
第二引数にシェーダのソースを渡します。
この時点ではシェーダにソースが割り当てられただけなので、まだコンパイルはされていません。
実際にコンパイルを行なっているのが、 compileShader メソッドですね。
このメソッドの引数に対象となるシェーダを渡して実行することで、シェーダがコンパイルされます。
シェーダが正しくコンパイルされたのかどうかは、シェーダが持つパラメータを参照することで確認できます。
パラメータを参照するには、
getShaderParameter メソッドに、WebGL の組み込み定数である COMPILE_STATUS を指定します。
もしここで false が返ってくるようなら、何かしらの原因でコンパイルに失敗しています。
原因を特定するためには、 getShaderInfoLog に対象となるシェーダを渡して実行することで、ログを確認できます。
この自作の関数では、頂点シェーダでもフラグメントシェーダでも、どちらでもコンパイルが可能です。
プログラムオブジェクトの生成とリンク
さて、シェーダを生成できたら、次はプログラムオブジェクトを生成します。
ここで唐突に登場したプログラムオブジェクトとは、そもそもいったいどんな働きをするオブジェクトなのでしょうか。
頂点シェーダからフラグメントシェーダへデータを渡すために、 varying修飾子付きの変数を使う
実は、このシェーダからシェーダへのデータの橋渡しを実現してくれるのが、
他ならぬプログラムオブジェクト
プログラムオブジェクトは、頂点シェーダとフラグメントシェーダ、また WebGL プログラムと各シェーダとのデータのやり取りを管理してくれる重要なオブジェクトなのです。
プログラムオブジェクトの生成とシェーダのリンクを行なう関数
code:js
function create_program(vs, fs){
// プログラムオブジェクトの生成
var program = gl.createProgram();
// プログラムオブジェクトにシェーダを割り当てる
gl.attachShader(program, vs);
gl.attachShader(program, fs);
// シェーダをリンク
gl.linkProgram(program);
// シェーダのリンクが正しく行なわれたかチェック
if(gl.getProgramParameter(program, gl.LINK_STATUS)){
// 成功していたらプログラムオブジェクトを有効にする
gl.useProgram(program);
// プログラムオブジェクトを返して終了
return program;
}else{
// 失敗していたらエラーログをアラートする
alert(gl.getProgramInfoLog(program));
}
}
この関数は、引数に頂点シェーダとフラグメントシェーダを受け取ります。
その後、まずプログラムオブジェクトを生成し、割り当てます。プログラムオブジェクトの生成には、WebGL のメソッドである createProgram メソッドを使います。
さらに、そこで生成されたプログラムオブジェクトにシェーダを割り当てるのですが、ここでは attachShader メソッドが使われます。 attachShader メソッドの第一引数にはプログラムオブジェクトを、第二引数にはシェーダを渡します。
シェーダの割り当てが済んだら、プログラムオブジェクトによって二つのシェーダをリンクします。これには linkProgram メソッドを使います。引数には対象となるプログラムオブジェクトを渡します。
シェーダを生成したときと同じように、正しくシェーダのリンクが行なわれたのかどうかをチェックします。
その際は getProgramParameter メソッドにプログラムオブジェクトと、組み込み定数 LINK_STATUS を与えて実行します。
問題がなければ、プログラムオブジェクトを有効にするために useProgram メソッドを実行します。
このメソッドを実行しておかないとプログラムオブジェクトが WebGL に正しく認識されませんので注意しましょう。
リンクに失敗していた場合にはログをアラートするようにしておきます。 getProgramInfoLog を実行することでログが得られます。
次回は、頂点データ、つまりモデルデータを用意して、それを VBO に変換する処理をやってみます。少しずつやることが増えていきますが一つ一つ順に理解していけば問題ないはずです。がんばりましょう。