GPUComputationRenderer constructor
GPUComputationRenderer(
- int sizeX,
- int sizeY,
- WebGLRenderer renderer
)
Implementation
GPUComputationRenderer(int sizeX, int sizeY, WebGLRenderer renderer ) {
int dataType = FloatType;
camera.position.z = 1;
final Map<String,dynamic> passThruUniforms = {
'passThruTexture': { 'value': null }
};
String getPassThroughVertexShader() {
return 'void main() {\n' +
'\n' +
' gl_Position = vec4( position, 1.0 );\n' +
'\n' +
'}\n';
}
String getPassThroughFragmentShader() {
return 'uniform sampler2D passThruTexture;\n' +
'\n' +
'void main() {\n' +
'\n' +
' vec2 uv = gl_FragCoord.xy / resolution.xy;\n' +
'\n' +
' gl_FragColor = texture2D( passThruTexture, uv );\n' +
'\n' +
'}\n';
}
void addResolutionDefine( materialShader ) {
materialShader.defines['resolution'] = 'vec2( ' + sizeX.toStringAsFixed( 1 ) + ', ' + sizeY.toStringAsFixed( 1 ) + ' )';
}
ShaderMaterial createShaderMaterial(String computeFragmentShader,[Map<String,dynamic>? uniforms ]) {
uniforms = uniforms ?? {};
final material = ShaderMaterial.fromMap( {
'name': 'GPUComputationShader',
'uniforms': uniforms,
'vertexShader': getPassThroughVertexShader(),
'fragmentShader': computeFragmentShader
});
addResolutionDefine( material );
return material;
}
final passThruShader = createShaderMaterial( getPassThroughFragmentShader(), passThruUniforms );
final mesh = Mesh( PlaneGeometry( 2, 2 ), passThruShader );
scene.add( mesh );
setDataType = ( type ) {
dataType = type;
return this;
};
addVariable = ( variableName, computeFragmentShader, initialValueTexture ) {
final material = this.createShaderMaterial( computeFragmentShader );
final variable = {
'name': variableName,
'initialValueTexture': initialValueTexture,
'material': material,
'dependencies': null,
'renderTargets': [],
'wrapS': null,
'wrapT': null,
'minFilter': NearestFilter,
'magFilter': NearestFilter
};
variables.add( variable );
return variable;
};
setVariableDependencies = ( variable, dependencies ) {
variable['dependencies'] = dependencies;
};
init = () {
if ( renderer.capabilities.maxVertexTextures == 0 ) {
return 'No support for vertex shader textures.';
}
for ( int i = 0; i < variables.length; i ++ ) {
final variable = variables[ i ];
// Creates rendertargets and initialize them with input texture
variable['renderTargets'].add(createRenderTarget( sizeX, sizeY, variable['wrapS'], variable['wrapT'], variable['minFilter'], variable['magFilter'] ));
variable['renderTargets'].add(createRenderTarget( sizeX, sizeY, variable['wrapS'], variable['wrapT'], variable['minFilter'], variable['magFilter'] ));
renderTexture( variable['initialValueTexture'], variable['renderTargets'][ 0 ] );
renderTexture( variable['initialValueTexture'], variable['renderTargets'][ 1 ] );
// Adds dependencies uniforms to the ShaderMaterial
final material = variable['material'] as Material?;
final uniforms = material?.uniforms;
if ( variable['dependencies'] != null ) {
for (int d = 0; d < variable['dependencies'].length; d ++ ) {
final Map<String,dynamic> depVar = variable['dependencies'][d];
if ( depVar['name'] != variable['name'] ) {
// Checks if variable exists
bool found = false;
for ( int j = 0; j < variables.length; j ++ ) {
if ( depVar['name'] == variables[j]['name'] ) {
found = true;
break;
}
}
if ( ! found ) {
return 'Variable dependency not found. Variable=' + variable['name'] + ', dependency=' + depVar['name'];
}
}
uniforms?[depVar['name']] = { 'value': null };
material?.fragmentShader = '\nuniform sampler2D ' + depVar['name'] + ';\n${material.fragmentShader}';
}
}
}
currentTextureIndex = 0;
return null;
};
compute = () {
final currentTextureIndex = this.currentTextureIndex;
final nextTextureIndex = this.currentTextureIndex == 0 ? 1 : 0;
for (int i = 0, il = variables.length; i < il; i ++ ) {
final variable = variables[i];
// Sets texture dependencies uniforms
if ( variable['dependencies'] != null ) {
final Map<String,dynamic> uniforms = variable['material'].uniforms;
for ( int d = 0, dl = variable['dependencies'].length; d < dl; d ++ ) {
final Map<String,dynamic> depVar = variable['dependencies'][ d ];
uniforms[depVar['name']] = {'value' :depVar['renderTargets'][ currentTextureIndex ].texture};
}
}
// Performs the computation for this variable
doRenderTarget( variable['material'], variable['renderTargets'][ nextTextureIndex ] );
}
this.currentTextureIndex = nextTextureIndex;
};
getCurrentRenderTarget = (Map<String,dynamic> variable ) {
return variable['renderTargets'][ currentTextureIndex ];
};
getAlternateRenderTarget = (Map<String,dynamic> variable ) {
return variable['renderTargets'][ currentTextureIndex == 0 ? 1 : 0 ];
};
dispose = () {
mesh.geometry?.dispose();
mesh.material?.dispose();
final variables = this.variables;
for (int i = 0; i < variables.length; i ++ ) {
final variable = variables[ i ];
if ( variable['initialValueTexture'] ) variable['initialValueTexture'].dispose();
final renderTargets = variable['renderTargets'];
for ( int j = 0; j < renderTargets.length; j ++ ) {
final renderTarget = renderTargets[ j ];
renderTarget.dispose();
}
}
};
this.addResolutionDefine = addResolutionDefine;
// The following functions can be used to compute things manually
this.createShaderMaterial = createShaderMaterial;
createRenderTarget = ( sizeXTexture, sizeYTexture, wrapS, wrapT, minFilter, magFilter ) {
sizeXTexture = sizeXTexture ?? sizeX;
sizeYTexture = sizeYTexture ?? sizeY;
wrapS = wrapS ?? ClampToEdgeWrapping;
wrapT = wrapT ?? ClampToEdgeWrapping;
minFilter = minFilter ?? NearestFilter;
magFilter = magFilter ?? NearestFilter;
final renderTarget = WebGLRenderTarget( sizeXTexture, sizeYTexture, WebGLRenderTargetOptions({
'wrapS': wrapS,
'wrapT': wrapT,
'minFilter': minFilter,
'magFilter': magFilter,
'format': RGBAFormat,
'type': dataType,
'depthBuffer': false
}));
return renderTarget;
};
createTexture = () {
final data = Float32Array( sizeX * sizeY * 4 );
final texture = DataTexture( data, sizeX, sizeY, RGBAFormat, FloatType );
texture.needsUpdate = true;
return texture;
};
renderTexture = (Texture input, RenderTarget output ) {
// Takes a texture, and render out in rendertarget
// input = Texture
// output = RenderTarget
passThruUniforms['passThruTexture'] = {'value': input};
doRenderTarget( passThruShader, output );
passThruUniforms['passThruTexture']= {'value': null};
};
doRenderTarget = (Material material, RenderTarget output ) {
final currentRenderTarget = renderer.getRenderTarget();
final currentXrEnabled = renderer.xr.enabled;
final currentShadowAutoUpdate = renderer.shadowMap.autoUpdate;
renderer.xr.enabled = false; // Avoid camera modification
renderer.shadowMap.autoUpdate = false; // Avoid re-computing shadows
mesh.material = material;
renderer.setRenderTarget( output );
renderer.render( scene, camera );
mesh.material = passThruShader;
renderer.xr.enabled = currentXrEnabled;
renderer.shadowMap.autoUpdate = currentShadowAutoUpdate;
renderer.setRenderTarget( currentRenderTarget );
};
}