どうも。営業部の伊藤です。
前回のブログの続編です。
以下の見慣れないエレメントタイプはバーテックスシェーダ(頂点シェーダ)とフラグメントシェーダ(ピクセルシェーダ)という点まで書きました。
「x-shader/x-vertex」
「x-shader/x-fragment」
シェーダはSTEP3のタイトル(シェーダを生成&コンパイル)にも記載しているようにプログラム内でコンパイルして利用するため、コンパイルするまでは単なる文字列しかありません。
そのため、今回はHTML内に記載しておりますが、JavaScript内部で文字列として宣言、管理する形でも問題ないです。
該当の属性はHTMLで定義された正式なエレメントではないため、一般的なブラウザでは理解されず無視されます。
注意しなければいけないのが、scriptタグの中身がJavaScriptと誤認されないようにしなければいけないという点です。
それを回避するため、type属性を使って各々のシェーダを宣言しています。
次はシェーダ内の記述を見てみます。
言語はGLSL(OpenGL Shading Language)で実装しています。
GLSLは名前のとおりOpenGLと親和性がある、C言語ライクな言語となります。
■バーテックスシェーダ(頂点シェーダ)
attribute vec2 vertexTexture;
uniform mat4 vertexMatrix1;
uniform mat4 vertexMatrix2;
varying vec2 textureCoord;
void main(void) {
gl_Position = vertexMatrix1 * vertexMatrix2 * vec4(vertexPosition, 1.0);
textureCoord = vertexTexture;
}
■フラグメントシェーダ(ピクセルシェーダ)
uniform sampler2D fragmentTexture;
varying vec2 textureCoord;
void main(void) {
gl_FragColor = texture2D(fragmentTexture, vec2(textureCoord));
}
attribute、vec3、vec2、uniform、mat4、varyingなど様々な単語が出てきましたね。
まず、開発経験者の方であればある程度想像できると思いますが、先頭部分は修飾子(attribute、uniform、varying)、その次はデータ型(vec3、vec2、mat4)、最後は任意の変数名(vertexPositionなど)となります。
修飾子、データ型について纏めます。
○attribute
バーテックスシェーダ(頂点シェーダ)専用の修飾子です。
attribute修飾子で宣言された変数はWebGLとシェーダ間で頂点情報を橋渡しする(シェーダ側が受け取る)ための変数となります。
WebGL側で同じ名前の変数に頂点情報を設定しておき、シェーダ側に情報を渡します。
○uniform
バーテックスシェーダ(頂点シェーダ)、フラグメントシェーダ(ピクセルシェーダ)の両方で利用できる修飾子です。
attribute同様に、uniformで宣言された変数はWebGL側から情報を渡すための変数となります。
ではattributeと何が違うのか?
attribute修飾子は3Dオブジェクトの頂点に関する情報を受け渡すための専用修飾子です。
逆にuniform修飾子は「全頂点に対して一律に処理される情報」を受け渡すための修飾子となります。
要約すると、3Dを描画しているcanvas上全体に情報を受け渡す場合に利用します。
STEP6(座標変換行列を生成&通知)で詳しく説明します。
○varying
バーテックスシェーダ(頂点シェーダ)、フラグメントシェーダ(ピクセルシェーダ)の両方で利用できる修飾子です。
前回のブログにも書きましたが、フラグメントシェーダ(ピクセルシェーダ)が着色内容を計算、決定するには頂点関連の情報が必要になります。
このバーテックスシェーダ(頂点シェーダ)とフラグメントシェーダ(ピクセルシェーダ)の橋渡しをするための変数をvaryingで定義します。
○vec2、vec3、mat4
vec(数値)は浮動小数点のベクトル型です。
数値には1~4が設定でき、添字によりベクトル要素数が変わります。
vec2 => 2Dベクトル
vec3 => 3Dベクトル
vec4 => 4Dベクトル
mat(数値)は浮動小数点の正方行列です。
数値には2~4を設定でき、添字により正方行列数が変わります。(matは恐らくマトリックスの意ですね)
mat2 => 2×2
mat3 => 3×3
mat4 => 4×4
attributeで宣言したvertexPositionやvertexTexture、uniformで宣言したvertexMatrix1、vertexMatrix2はSTEP6(座標変換行列を生成&通知)で値の受け渡しを実施し、
varyingで宣言したtextureCoordは頂点関連情報を受取後、フラグメントシェーダ(ピクセルシェーダ)にデータを受け渡しています。
シェーダの宣言部(WebGLとのデータ連携方法や受皿、シェーダ間の連携方法)は以上となり、次はシェーダの処理部です。
ご覧のとおり、バーテックスシェーダ(頂点シェーダ)、フラグメントシェーダ(ピクセルシェーダ)共にmainという関数が定義されています。C言語ライクですね。
必ずmainという関数を定義し、そのなかに処理内容を記述します。
そして、バーテックスシェーダ(頂点シェーダ)ではgl_Positionという組込み変数に頂点データを渡し、フラグメントシェーダ(ピクセルシェーダ)ではgl_FragColoという組み込み変数にデータを渡します。
※頂点データは必須となりますが、フラグメンテーションは必須ではないです。
変数宣言(WebGLとの連係用受皿)、シェーダ間連携内容の実装、計算処理内容の実装などシェーダ自体の宣言が完了したため、次はWebGLで該当のシェーダをコンパイルします。
まず、getElementByIdでHTML内のバーテックスシェーダ(頂点シェーダ)、フラグメントシェーダ(ピクセルシェーダ)エレメントを取得します。
var shaderElement = document.getElementById(id);
if (!shaderElement) {
return null;
}
その後、エレメントタイプをチェックし、タイプ毎に該当のシェーダ生成します。
ここで初めてシェーダを生成(createShader)しています。
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 null;
}
次に、HTML内で宣言した変数、連携内容、計算処理などの文字列(ソース)を上記シェーダに割り当て(shaderSource)るため、firstChildでエレメント内の文字列を取得します。
一通りの文字列取得が完了したら最後にコンパイル(compileShader)です。
var source = “”;
var childNodes = shaderElement.firstChild;
while (childNodes) {
if (childNodes.nodeType == 3) {
source += childNodes.textContent;
}
childNodes = childNodes.nextSibling;
}
gl.shaderSource(shader, source); // ソース割り当て
gl.compileShader(shader); // コンパイル
これでSTEP3の「シェーダを生成&コンパイル」は終わりです。
&
今回のブログも終わりです!!
細かなところは端折りながら書いているつもりですが…想定外に長文&長期化しております。。。
まだまだSTEP3ですが、STEP4以降も頑張って連載していきますので宜しくお願いします。
ではでは。