Comater sur une page Web
Posté : mer. 1 oct. 2014 19:20
				
				
			Code : Tout sélectionner
package;
import gltoolbox.render.RenderTarget2Phase;
import gltoolbox.render.RenderTarget;
import lime.graphics.GLRenderContext;
import lime.graphics.opengl.GLBuffer;
import lime.graphics.opengl.GLTexture;
import lime.math.Vector2;
import lime.utils.Float32Array;
import shaderblox.ShaderBase;
class GPUFluid{
	var gl:GLRenderContext;
	public var width  (default, null) : Int;
	public var height (default, null) : Int;
	public var cellSize (default, set) : Float;
	public var solverIterations         : Int;
	public var aspectRatio (default, null) : Float;
	//Render Targets
	public var velocityRenderTarget   (default, null) : RenderTarget2Phase;
	public var pressureRenderTarget   (default, null) : RenderTarget2Phase;
	public var divergenceRenderTarget (default, null) : RenderTarget;
	public var dyeRenderTarget        (default, null) : RenderTarget2Phase;
	//User Shaders
	public var applyForcesShader (default, set) : ApplyForces;
	public var updateDyeShader   (default, set) : UpdateDye;
	//Internal Shaders
	var advectShader                    : Advect = new Advect();
	var divergenceShader                : Divergence = new Divergence();
	var pressureSolveShader             : PressureSolve = new PressureSolve();
	var pressureGradientSubstractShader : PressureGradientSubstract = new PressureGradientSubstract();
	//Geometry
	var textureQuad : GLBuffer;
	public function new(gl:GLRenderContext, width:Int, height:Int, cellSize:Float = 8, solverIterations:Int = 18){
		this.gl = gl;
		this.width = width;
		this.height = height;
		this.solverIterations = solverIterations;
		this.aspectRatio = this.width/this.height;
		this.cellSize = cellSize;
		var texture_float_linear_supported = true;
		//setup gl
		#if js //load floating point extension
			 //(no need for this unless we use linearFactory - for performance and compatibility, it's best to avoid this extension if possible!)
			if(gl.getExtension('OES_texture_float_linear') == null) texture_float_linear_supported = false;
			if(gl.getExtension('OES_texture_float') == null) null;
		#end
		//geometry
		//	inner quad, for main fluid shaders
		textureQuad = gltoolbox.GeometryTools.getCachedTextureQuad(gl);
		//create texture
		//	seems to run slightly faster with rgba instead of rgb in Chrome?
		var nearestFactory = gltoolbox.TextureTools.customTextureFactory(gl.RGBA, gl.FLOAT , gl.NEAREST);
		velocityRenderTarget = new RenderTarget2Phase(gl, nearestFactory, width, height);
		pressureRenderTarget = new RenderTarget2Phase(gl, nearestFactory, width, height);
		divergenceRenderTarget = new RenderTarget(gl, nearestFactory, width, height);
		dyeRenderTarget = new RenderTarget2Phase(gl, 
			gltoolbox.TextureTools.customTextureFactory(
				gl.RGB, gl.FLOAT, 
				texture_float_linear_supported ? gl.LINEAR : gl.NEAREST
			),
			width,
			height
		);
		//texel-space parameters
		updateCoreShaderUniforms(advectShader);
		updateCoreShaderUniforms(divergenceShader);
		updateCoreShaderUniforms(pressureSolveShader);
		updateCoreShaderUniforms(pressureGradientSubstractShader);
	}
	public function step(dt:Float){
		gl.viewport(0, 0, this.width, this.height);
		//inner quad
		gl.bindBuffer(gl.ARRAY_BUFFER, textureQuad);
		advect(velocityRenderTarget, dt);
		applyForces(dt);
		computeDivergence();
		solvePressure();
		subtractPressureGradient();
		updateDye(dt);
		advect(dyeRenderTarget, dt);
	}
	public inline function resize(width:Int, height:Int){
		velocityRenderTarget.resize(width, height);
		pressureRenderTarget.resize(width, height);
		divergenceRenderTarget.resize(width, height);
		dyeRenderTarget.resize(width, height);
		this.width = width;
		this.height = height;
	}
	public inline function clear(){
		velocityRenderTarget.clear(gl.COLOR_BUFFER_BIT);
		pressureRenderTarget.clear(gl.COLOR_BUFFER_BIT);
		dyeRenderTarget.clear(gl.COLOR_BUFFER_BIT);
	}
	public function simToClipSpaceX(simX:Float) return simX/(this.cellSize * this.aspectRatio);
	public function simToClipSpaceY(simY:Float) return simY/(this.cellSize);
	public inline function advect(target:RenderTarget2Phase, dt:Float){
		advectShader.dt.set(dt);
		//set velocity and texture to be advected
		advectShader.target.set(target.readFromTexture);
		advectShader.velocity.set(velocityRenderTarget.readFromTexture);
		renderShaderTo(advectShader, target);
		target.swap();
	}
	inline function applyForces(dt:Float){
		if(applyForcesShader == null)return;
		//set uniforms
		applyForcesShader.dt.set(dt);
		applyForcesShader.velocity.set(velocityRenderTarget.readFromTexture);
		//render
		renderShaderTo(applyForcesShader, velocityRenderTarget);
		velocityRenderTarget.swap();
	}
	inline function computeDivergence(){
		divergenceShader.velocity.set(velocityRenderTarget.readFromTexture);
		renderShaderTo(divergenceShader, divergenceRenderTarget);
	}
	inline function solvePressure(){
		pressureSolveShader.divergence.set(divergenceRenderTarget.texture);
		pressureSolveShader.activate(true, true);
		for (i in 0...solverIterations) {
			pressureSolveShader.pressure.set(pressureRenderTarget.readFromTexture);
			//(not using renderShaderTo to allow for minor optimization)
			pressureSolveShader.setUniforms();
			pressureRenderTarget.activate();
			gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
			pressureRenderTarget.swap();
		}
		
		pressureSolveShader.deactivate();
	}
	inline function subtractPressureGradient(){
		pressureGradientSubstractShader.pressure.set(pressureRenderTarget.readFromTexture);
		pressureGradientSubstractShader.velocity.set(velocityRenderTarget.readFromTexture);
		renderShaderTo(pressureGradientSubstractShader, velocityRenderTarget);
		velocityRenderTarget.swap();
	}
	inline function updateDye(dt:Float){
		if(updateDyeShader==null)return;
		//set uniforms
		updateDyeShader.dt.set(dt);
		updateDyeShader.dye.set(dyeRenderTarget.readFromTexture);
		//render
		renderShaderTo(updateDyeShader, dyeRenderTarget);
		dyeRenderTarget.swap();
	}
	inline function renderShaderTo(shader:ShaderBase, target:gltoolbox.render.ITargetable){
		shader.activate(true, true);
		target.activate();
		gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
		shader.deactivate();
	}
	inline function updateCoreShaderUniforms(shader:FluidBase){
		if(shader==null)return;
		//set uniforms
		shader.aspectRatio.set(this.aspectRatio);
		shader.invresolution.data.x = 1/this.width;
		shader.invresolution.data.y = 1/this.height;
	}
	inline function set_applyForcesShader(v:ApplyForces):ApplyForces{
		this.applyForcesShader = v;
		this.applyForcesShader.dx.data = this.cellSize;
		updateCoreShaderUniforms(this.applyForcesShader);
		return this.applyForcesShader;
	}
	inline function set_updateDyeShader(v:UpdateDye):UpdateDye{
		this.updateDyeShader = v;
		this.updateDyeShader.dx.data = this.cellSize;
		updateCoreShaderUniforms(this.updateDyeShader);
		return this.updateDyeShader;
	}
	inline function set_cellSize(v:Float):Float{
		//shader specific
		cellSize = v;
		advectShader.rdx.set(1/cellSize);
		divergenceShader.halfrdx.set(0.5*(1/cellSize));
		pressureGradientSubstractShader.halfrdx.set(0.5*(1/cellSize));
		pressureSolveShader.alpha.set(-cellSize*cellSize);
		return cellSize; 
	}
}
@:vert('#pragma include("Source/shaders/glsl/fluid/texel-space.vert")')
@:frag('#pragma include("Source/shaders/glsl/fluid/fluid-base.frag")')
class FluidBase extends ShaderBase{}
@:frag('#pragma include("Source/shaders/glsl/fluid/advect.frag")')
class Advect extends FluidBase{}
@:frag('#pragma include("Source/shaders/glsl/fluid/velocity-divergence.frag")')
class Divergence extends FluidBase{}
@:frag('#pragma include("Source/shaders/glsl/fluid/pressure-solve.frag")')
class PressureSolve extends FluidBase{}
@:frag('#pragma include("Source/shaders/glsl/fluid/pressure-gradient-subtract.frag")')
class PressureGradientSubstract extends FluidBase{}
@:frag('
	uniform sampler2D velocity;
	uniform float dt;
	uniform float dx;
	varying vec2 texelCoord;
	varying vec2 p;
')
class ApplyForces extends FluidBase{}
@:frag('
	uniform sampler2D dye;
	uniform float dt;
	uniform float dx;
	varying vec2 texelCoord;
	varying vec2 p;
')
class UpdateDye extends FluidBase{}