[Work/TechInfo/other]

WebGL2(GLSL3)によるTransformFeedbackを用いたGPGPU / 2017-10-10 (火)

TransformFeedback

Vertexシェーダでどうにかした(行列演算した)後の値をmainプログラムに戻すためのものなのだが,display:noneしたcanvasをwebgl2用として使えば,GPGPUができる.WebCLがまともに動かない現状,JavaScriptでGPGPUを使うためには,これしか方法がない.(ただしChrome限定)

HTMLファイル

display:noneするcanvasと,計算結果を出力するdivがあるだけ.

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8" />
    <title>WebGL2 GPGPU Evaluator</title>

    <script type="text/javascript" src="./webgl2_gpgpu_evaluator.js"></script>

  </head>
  <body>

    <canvas id="webgl_canvas"></canvas>
    <div id="result_display"></div>

  </body>
</html>

JavaScriptファイル

//webgl2_gpgpu_evaluator.js
'use strict';
window.addEventListener('load', () => {
    if(window.navigator.userAgent.toLowerCase().indexOf('chrome') == -1){
	alert('Currently Only Chrome Browser can allow GLSL3.0(WebGL2)');
    }

    window.glCanvas = document.getElementById("webgl_canvas");
    window.glCanvas.width = 640;
    window.glCanvas.height = 480;
    window.glCanvas.setAttribute("style", "display: none");
    
    const gl2 = window.glCanvas.getContext("webgl2");
    // 文字列としてGLSLシェーダプログラムを渡す.
    // Vertexシェーダに書く
    let vsScriptText = "#version 300 es \n out float result; in vec3 vertexPositions; void main(void){result = vertexPositions[0] + vertexPositions[1] + vertexPositions[2];}";
    window.vertexShader = gl2.createShader(gl2.VERTEX_SHADER);
    gl2.shaderSource(window.vertexShader, vsScriptText);
    gl2.compileShader(window.vertexShader);

    // こちらはFragmentシェーダ.
    // とりあえず何もしないので,mainの中が空.
    let fsScriptText = "#version 300 es \n void main(void){}";
    window.fragmentShader = gl2.createShader(gl2.FRAGMENT_SHADER);
    gl2.shaderSource(window.fragmentShader, fsScriptText);
    gl2.compileShader(window.fragmentShader);

    window.program = gl2.createProgram();
    gl2.attachShader(window.program, window.vertexShader);
    gl2.attachShader(window.program, window.fragmentShader);

    // TransformFeedbackの登録
    gl2.transformFeedbackVaryings(window.program, ["result"], gl2.SEPARATE_ATTRIBS);

    // シェーダのリンク
    gl2.linkProgram(window.program);
    gl2.useProgram(window.program);

    // 頂点座標の登録とVBOの生成 GPGPUの場合,GLSLへの数値渡しに使う
    // Vertexシェーダ用なので,vec3で受け渡しするのが一番効率が良い?
    // とりあえずこれで3スレッド同時に走ることになる.
    let vertexPosArray = 
	new Array(1.0, 2.0, 3,0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
    let numOfPoint = 3; //vec3(x, y, z)で頂点3つ分の配列がある.つまり3スレッド同時

    let vboPos = gl2.createBuffer();
    gl2.bindBuffer(gl2.ARRAY_BUFFER, vboPos);
    gl2.bufferData(gl2.ARRAY_BUFFER, new Float32Array(vertexPosArray), gl2.STATIC_DRAW);

    let vertexPosAttribLocation = gl2.getAttribLocation(window.program, 'vertexPositions');
    gl2.enableVertexAttribArray(vertexPosAttribLocation);
    let vertexPosAttribStride = 3; //GLSL3.0コードの中でvec3型なので3
    gl2.vertexAttribPointer(vertexPosAttribLocation,
			    vertexPosAttribStride,
			    gl2.FLOAT,
			    false, 0, 0);
    gl2.bindBuffer(gl2.ARRAY_BUFFER, null);
    // 頂点座標の登録とVBOの生成: ここまで

    // TransformFeedbackの指定
    let vertexTransformFeedback = gl2.createBuffer();
    let transformFeedback = gl2.createTransformFeedback();

    gl2.bindBuffer(gl2.ARRAY_BUFFER, vertexTransformFeedback);
    gl2.bufferData(gl2.ARRAY_BUFFER, numOfPoint * Float32Array.BYTES_PER_ELEMENT, gl2.DYNAMIC_COPY);
    gl2.bindBuffer(gl2.ARRAY_BUFFER, null);

    gl2.bindTransformFeedback(gl2.TRANSFORM_FEEDBACK, transformFeedback);
    gl2.bindBufferBase(gl2.TRANSFORM_FEEDBACK_BUFFER, 0, vertexTransformFeedback);

    gl2.beginTransformFeedback(gl2.POINTS);

    // シェーダの実行
    gl2.drawArrays(gl2.POINTS, 0, numOfPoint);
    gl2.endTransformFeedback();

    let arrBuffer = new ArrayBuffer(numOfPoint * Float32Array.BYTES_PER_ELEMENT);
    arrBuffer = new Float32Array(arrBuffer);
    gl2.getBufferSubData(gl2.TRANSFORM_FEEDBACK_BUFFER, 0, arrBuffer);

    document.getElementById('result_display').innerHTML = arrBuffer[0] + "," + arrBuffer[1] + "," + arrBuffer[2];
});
[ ツッコミの受付は終了しています ]
この記事のリンク元