テラByteの時代にキロByte

shader又はdemosceneに関係する事

webGLで同等の事をする。

前回のpythonソースをwebGLに書き直すと、openGL部分は、ほぼ一緒ですが、音楽部分が別物です。

<script id="vs" type="x-shader/x-vertex">
#version 300 es
void main() {    
    gl_Position = vec4(ivec2(gl_VertexID&1,gl_VertexID>>1)*2-1,0,1);
}
</script>


<script id="fs" type="x-shader/x-vertex">
#version 300 es

precision highp float;
uniform vec2  resolution;
uniform float time;
out vec4 fragColor;

void main() {
    vec2 p = (2.0 * gl_FragCoord.xy - resolution.xy) / resolution.y;
    p += vec2(cos(time), sin(time)) * 0.5;
    float g = exp(-1.5 * dot(p,p));
    fragColor = vec4(g, g, g, 1.0);
  }
</script>
<script id="vs_mzk" type="x-shader/x-vertex">
#version 300 es
out vec2 gain; 

uniform float sampleRate; 

#define PI2 6.2831
#define speed 3.0

float sound(float time)
{
return sin(6.2831*440.0*fract(time));
}

void main() {
float time = float(gl_VertexID) / sampleRate;
gain = vec2(sound(time));
}
</script>

<script id="fs_mzk" type="x-shader/x-fragment">
#version 300 es
void main() {}
</script>

<canvas id="canvas"  width="360" height="240"></canvas>
<script type="text/javascript">

var gl = canvas.getContext("webgl2") || canvas.getContext("experimental-webgl2");

var compileShader = function(prog, src, type){
var sh = gl.createShader(type);
    gl.shaderSource(sh, src.replace(/^\n/, ""));
    gl.compileShader(sh);
    if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS)) {
       alert(gl.getShaderInfoLog(sh));
    } 
    gl.attachShader(prog, sh);
    gl.deleteShader(sh);
};

var p = gl.createProgram();
compileShader(p, vs.text, gl.VERTEX_SHADER);
compileShader(p, fs.text, gl.FRAGMENT_SHADER);
gl.linkProgram(p);
gl.useProgram(p);
gl.uniform2f(gl.getUniformLocation(p, "resolution"), canvas.width, canvas.height);

var audio = new AudioContext();
var sampleRate = audio.sampleRate;
var bufferSize = audio.sampleRate * 10; // 10 sec
var audioBuffer = audio.createBuffer(2, bufferSize, audio.sampleRate);
var node = audio.createBufferSource();

var p_mzk = gl.createProgram();
compileShader(p_mzk, vs_mzk.text, gl.VERTEX_SHADER);
compileShader(p_mzk, fs_mzk.text, gl.FRAGMENT_SHADER);
gl.transformFeedbackVaryings(p_mzk, ['gain'], gl.SEPARATE_ATTRIBS);
gl.linkProgram(p_mzk);
gl.useProgram(p_mzk); 
gl.uniform1f(gl.getUniformLocation(p_mzk, "sampleRate"), sampleRate); 

var VBOs = [gl.createBuffer(),gl.createBuffer()];
for (var i = 0; i < 2; ++i) {
    gl.bindBuffer(gl.ARRAY_BUFFER, VBOs[i]);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(bufferSize*2), gl.STATIC_DRAW);
}

gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, VBOs[0]);
gl.enable(gl.RASTERIZER_DISCARD);
gl.beginTransformFeedback(gl.POINTS);
gl.drawArrays(gl.POINTS, 0, bufferSize);
gl.endTransformFeedback();
gl.disable(gl.RASTERIZER_DISCARD);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, null);
gl.bindBuffer(gl.ARRAY_BUFFER, VBOs[0]);
var aryBuffer = new ArrayBuffer(bufferSize*4*2);
var dataView = new DataView(aryBuffer);
gl.getBufferSubData(gl.ARRAY_BUFFER, 0, dataView); 
var buf = new Float32Array(aryBuffer);

var data0 = audioBuffer.getChannelData(0);
var data1 = audioBuffer.getChannelData(1);
for(var i = 0; i < bufferSize; i++) {
    data0[i] = buf[i*2];
    data1[i] = buf[i*2+1];
}

node.buffer = audioBuffer;
node.loop = false; 
node.connect(audio.destination);

var zero = Date.now();
gl.useProgram(p);
node.start(0);
(function () { 
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    gl.uniform1f(gl.getUniformLocation(p, "time"), (Date.now() - zero) * 0.001);
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
    requestAnimationFrame(arguments.callee);
})();
</script>

これで、C言語pythonwebGLが揃いました。shader中心で書いていると、これだけあれば、他のソースは要らないです。
これを状況によって、ちょっと変化させる程度で済みます。
でも、今度compute shaderに手を出すので、ここで1回まとめてみました。
これだけミニ化していると説明要素の密度が濃すぎて解説は書きようが無いです。 いずれ、pythonwebGLとのコマンドの違いは書くつもりです。 ブログには、webGLが使えるみたいなので試してみました。ちょっとうるさいので消音してあります。

以前

qiita.com

を書きましたが、必要以上のwebGLのミニ化は、それ程、魅力的では無くなってしまったので、一応、参考ということでリンクを貼っておきました。