今回は、WebGL2.0のTransform Feedbackによりマンデルブロ集合をとりあえず描画することが出来ました。
数値が発散するまでの計算回数を元に適当に色付けしています。
シェーダは以下のようになりました。in_realやtf_realが実部、in_imgやtf_imgが虚部に関する計算値です。
#version 300 es
in vec3 aVertexPosition;
in float in_real;
in float in_img;
in float in_count;
out float tf_real;
out float tf_img;
out float tf_count;
uniform float uReset;
uniform float uCalcs;
uniform float uScale;
uniform float uBaseX;
uniform float uBaseY;
void main(void) {
float real = in_real;
float img = in_img;
float count = in_count;
for(float i = 0.0; i < uCalcs; i++){
if (0.0 < uReset && 0.0 == i) {
real = (aVertexPosition.x + uBaseX) * uScale;
img = (aVertexPosition.y + uBaseY) * uScale;
count = 0.0;
} else {
float a = real * real - img * img + (aVertexPosition.x + uBaseX) *uScale;
float b = 2.0 * real * img + (aVertexPosition.y + uBaseY) * uScale;
real = a;
img = b;
if (4.0 < a * a + b * b) {
count = count + 0.05;
} else {
count = count;
}
}
}
tf_real = real;
tf_img = img;
tf_count = count;
gl_Position = vec4(aVertexPosition, 1.0);
}
シェーダの呼出しコード(概略)は以下の通りです。入力と出力のVBOを描画ごとに入れ替えることでマンデルブロ集合の計算を進めています。
~略~
// --- Transform Feedback ---
gl.useProgram(this.shader_tf);
this.shader_tf.enableAttribute();
// [入力] ユニフォーム変数
gl.uniform1f(this.shader_tf["uReset"], bReset ? 1.0 : 0.0);
gl.uniform1f(this.shader_tf["uCalcs"], calcs);
gl.uniform1f(this.shader_tf["uScale"], scale);
gl.uniform1f(this.shader_tf["uBaseX"], baseX);
gl.uniform1f(this.shader_tf["uBaseY"], baseY);
// [入力] VBO のバインド
gl.bindBuffer(gl.ARRAY_BUFFER, this.vID);
gl.vertexAttribPointer(this.shader_tf["aVertexPosition"], this.v.itemSize, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, this.tf_turn ? this.tf_rID : this.tf_r2ID);
gl.vertexAttribPointer(this.shader_tf["in_real"], this.tf_turn ? this.tf_r.itemSize : this.tf_r2.itemSize, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, this.tf_turn ? this.tf_iID : this.tf_i2ID);
gl.vertexAttribPointer(this.shader_tf["in_img"], this.tf_turn ? this.tf_i.itemSize : this.tf_i2.itemSize, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, this.tf_turn ? this.tf_cID : this.tf_c2ID);
gl.vertexAttribPointer(this.shader_tf["in_count"], this.tf_turn ? this.tf_c.itemSize : this.tf_c2.itemSize, gl.FLOAT, false, 0, 0);
// [出力] 書き込み先の VBO をバインド
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, this.tf_turn ? this.tf_r2ID : this.tf_rID);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 1, this.tf_turn ? this.tf_i2ID : this.tf_iID);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 2, this.tf_turn ? this.tf_c2ID : 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();
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);
this.tf_turn = !this.tf_turn;
~略~
動画は画素ごとに1フレーム1回で計算した様子ですが、シェーダ内でループしてまとめて計算もできます。
4Kの解像度で1フレーム1万回の計算をした場合、RTX3070で約18FPSの結果となりました。
これは、 f(z) = z^2 + C の計算を、1秒間に3,840 * 2,160 * 10,000 * 18 = 1,492,992,000,000 回行っていることを意味します。
( -`ω-)キリッ
_人人人人人人_
> 意味します <
 ̄Y^Y^Y^Y^Y^Y ̄