今回は、マンデルブロ集合をTransform Feedbackで高速描画するための下準備を行いました。
このページでマンデルブロ集合を手軽に描画できるようにWebGL2を利用することとしました。
過去に作成したLIFEGAME LIGHTNINGはWebGL1.0で作成していましたが、このバージョンだとTransform Feedbackが利用できないため土台部分のコードをWebGL2.0で書き直し、その上でTransform Feedbackの基礎処理を実装しました。
中核となるコード(概略)は以下通りです。
// --- Transform Feedback ---
gl.useProgram(this.shader_tf);
this.shader_tf.enableAttribute();
// [入力] VBO のバインド
gl.bindBuffer(gl.ARRAY_BUFFER, this.vID);
gl.vertexAttribPointer(this.shader_tf["aVertexPosition"], this.v.itemSize, gl.FLOAT, false, 0, 0);
// [出力] 書き込み先の VBO をバインド
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, this.tf_rID);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 1, this.tf_iID);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 2, this.tf_cID);
gl.enable(gl.RASTERIZER_DISCARD);
gl.beginTransformFeedback(gl.POINTS);
// 実行
gl.drawArrays(gl.POINTS, 0, this.points);
gl.disable(gl.RASTERIZER_DISCARD);
gl.endTransformFeedback();
// テスト出力
//const view = new Float32Array(this.points);
//gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 2, this.tf_cID);
//gl.getBufferSubData(gl.TRANSFORM_FEEDBACK_BUFFER, 0, view);
//console.log(view);
this.shader_tf.disableAttribute();
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, null);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 1, null);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 2, null);
入力に初期座標を、出力にマンデルブロ集合の計算に必要となる複素数の計算結果「実部」と「虚部」と「計算回数」を設定しました。
Transform Feedback用の頂点シェーダはとりあえず以下のようにしました。
const POINT_VS_TF = '#version 300 es\n\
in vec3 aVertexPosition;\
\
out float tf_real;\
out float tf_img;\
out float tf_count;\
\
void main(void) {\
tf_real = 0.0;\
tf_img = 0.0;\
tf_count = 0.0 < aVertexPosition.x && 0.0 < aVertexPosition.y ? 1.0 : 0.0;\
gl_Position = vec4(aVertexPosition, 1.0);\
}';
描画用の頂点シェーダは以下のようにしました。
const POINT_VS = '#version 300 es\n\
in vec3 aVertexPosition;\
in vec3 aVertexColor;\
in float aVertexAlpha;\
in float tf_count;\
\
out vec4 vColor;\
\
uniform float uPointSize;\
\
uniform mat4 uMVMatrix;\
uniform mat4 uPMatrix;\
\
void main(void) {\
vColor = vec4(aVertexColor, aVertexAlpha * tf_count);\
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);\
gl_PointSize = uPointSize;\
}';
描画用のフラグメントシェーダは以下のようにし、各点を円形のグラデーションで描画しています
const POINT_FS = '#version 300 es\n\
precision mediump float;\
\
in vec4 vColor;\
out vec4 outColor;\
\
void main(void) {\
vec3 target;\
target.xy = (gl_PointCoord - 0.5) * 2.0;\
float r2 = target.x * target.x + target.y * target.y;\
if (1.0 < r2) {\
discard;\
}\
outColor = vec4(vColor.r, vColor.g, vColor.b, vColor.a * 0.4 * (1.0 - r2));\
}';
テストで、Transform Feedback用の頂点シェーダでは、入力座標 x y が共に 0以上 ならば tf_count に 1.0 を出力するようにしています。
見事にシェーダでの計算結果が反映されました。
ちなみにフルスクリーン切り替え処理や、ちょっとした設定の切り替え機能(今のところFPSの表示/非表示だけ)も実装しました。今回は超がんばりました。
あとはTransform Feedback用の頂点シェーダに複素数の計算を記述し、描画用のシェーダで解像度を上げていけばマンデルブロ集合の画像が表示されるはずです。