のぐそんブログ

暗いおじさんがシコシコ書くブログです。

GPGPUがまったくわからないのでGPUComputationRenderer.jsをまず調べてみるメモ

GPGPU用のヘルパーライブラリであるGPUComputationRenderer.jsの使い方を調べてみました。

GPGPUが難しいので上手く説明できておらず、よくわからない文章になってしまっています。 あまり参考にならないかもしれません。

こちらの「Three.jsのGPGPUのサンプルが難しすぎるから解体して勉強してみる」のがとてもわかりやすので参考になります。

やること事

なんとなくのイメージこんな感じです。 GPGPUなのでテクスチャをデータ格納用に利用します。 テクスチャは、参照用と、データ格納用を作成して毎レーム入れ替えていきます。 ※複数のフレームバッファを使う場合です。1つだけの場合は「オフスクリーンレンダリング2」はいりません。

STEP 1

GPUComputationRendererのインスタンスを作成する。

引数のwidth, height, rendererはオフスクリーンレンダリング用のWebGLRenderTargetや、DataTextureを作成する際に利用される。

var gpuRender = new GPUComputationRenderer(width, height, renderer);

GPUComputationRendererのインスタンスを作成すると、はじめに下記のようなオブジェクトが作成される。

var material = new THREE.ShaderMaterial( {
  uniforms:{
    texture: { value: null }
  },
  vertexShader: vs,//中身は↓に記載
  fragmentShader: fs//中身は↓に記載
} );

var geometory =  new THREE.PlaneBufferGeometry( 2, 2 );

var mesh = new THREE.Mesh(geometory, material );
◎vs
void main()  {
    gl_Position = vec4( position, 1.0 );
}

◎fs

uniform sampler2D texture;

void main() {
    vec2 uv = gl_FragCoord.xy / resolution.xy;
    gl_FragColor = texture2D( texture, uv );
}

STEP 2

テクスチャを作成する。

var textuer = gpuCompute.createTexture();

GPUComputationRenderer内で以下の処理が実行されテクスチャデータが作成される。

var arr = new Float32Array( width * height * 4 );
var texture = new THREE.DataTexture( arr, width, height, THREE.RGBAFormat, THREE.FloatType );
texture.needsUpdate = true;

STEP 3

matrialやtextuerなどのデータを格納しておくオブジェクトを作成する。

第二引数はフラグメントシェーダを指定します。 第三引数にはテクスチャを指定します。

var velVar = gpuCompute.addVariable( "textureVelocity", fragmentShaderVel, textuer );

GPUComputationRenderer内で以下の処理が実行され、設定した内容が保持される。

var material = new THREE.ShaderMaterial( {
  uniforms:{},
  vertexShader: vs,//中身は↓に記載
  fragmentShader: fragmentShaderVel
} );


var variable = {
  name: "textureVelocity",
  initialValueTexture: textuer,
  material: material,
  dependencies: null,
  renderTargets: [],
  wrapS: null,
  wrapT: null,
  minFilter: THREE.NearestFilter,
  magFilter: THREE.NearestFilter
};

//作成したデータをインスタンス内に保持する
this.variables.push( variable );
◎vs
void main()  {
    gl_Position = vec4( position, 1.0 );
}

STEP 4

velVar、posVarに設定されたフラグメントシェーダーそれぞれからSTEP 2で作成したテクスチャが参照できるようする為の前準備。 シェーダー間でテクスチャを参照できるようになる。

下記の場合は、velVarのフラグメントシェーダから、velVar、posVarのテクスチャを参照することができる。

gpuCompute.setVariableDependencies( velVar, [ velVar, posVar] );

velVarのフラグメントシェーダから、velVarのテクスチャの参照だけで良い場合は、下記のような定義になる。

gpuCompute.setVariableDependencies( velVar, [ velVar] );

STEP 5

オフスクリーンレンダリング用のWebGLRenderTargetを作成する。

「STEP 3」で空だったrenderTargetsの配列にWebGLRenderTargetのインスタンスを追加します。

gpuCompute.init()

GPUComputationRenderer内では、同じWebGLRenderTargetが2つ作成される。

variable.renderTargets[ 0 ] = this.createRenderTarget( sizeX, sizeY, variable.wrapS, variable.wrapT, variable.minFilter, variable.magFilter );
variable.renderTargets[ 1 ] = this.createRenderTarget( sizeX, sizeY, variable.wrapS, variable.wrapT, variable.minFilter, variable.magFilter );

また設定しているフラグメントシェーダーの頭に、テクスチャの読みこみ用の定義が追加される。

自分で用意したフラグメントシェーダーにuniform sampler2Dの定義がないのに、何故テクスチャが参照できるのだろうと疑問におもっていたがここでライブラリが自動で挿入してくれていた。

uniform sampler2D textureVelocity;
uniform sampler2D texturePosition;

//↓↓fragmentShaderVelに定義した処理がはいる↓↓

material(shaderMaterial)にも、uniformの定義が追加される。 この段階ではまだ、テクスチャの値はnull。

uniforms.textureVelocity = { value: null };

STEP 6

最後に毎フレーム毎に下記を実行して、参照用と格納用のテクスチャを切り替えていく。

gpuCompute.compute();

GPUComputationRenderer内では、STEP 5ではまだnullだったテクスチャデータに、 WebGLRenderTargetが挿入される。

サンプル

実際にサンプルを作りました。 このサンプルはオフスクリーンレンダリングは1つだけです。 内部的にはGPUComputationRendererがテクスチャを複製してくれるので2つです。

GPGPU SAMPLE

まとめ

GPUComputationRendererはGPGPUの面倒なところを代わりにやってくれるので、私のような初学者には大変助かるライブラリです。 だたし、中でなにをやっているかを正しく理解しないと、GPGPUとはなんなのかがわからなくなりそうです。