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];
});