Browse Source

added shadow map blur

Nicolas Cannasse 11 years ago
parent
commit
fb1bd61fd0
7 changed files with 179 additions and 8 deletions
  1. 107 0
      h3d/pass/Blur.hx
  2. 13 1
      h3d/pass/ShadowMap.hx
  3. 39 0
      h3d/shader/BlurShader.hx
  4. 4 0
      hxsl/Checker.hx
  5. 10 5
      hxsl/Eval.hx
  6. 4 2
      hxsl/Flatten.hx
  7. 2 0
      samples/shadows/Main.hx

+ 107 - 0
h3d/pass/Blur.hx

@@ -0,0 +1,107 @@
+package h3d.pass;
+
+class Blur {
+	
+	/**
+		Gives the blur quality : 0 for disable, 1 for 3x3, 2 for 5x5, etc.
+	**/
+	public var quality(default, set) : Int;
+	
+	/**
+		The amount of blur (gaussian blur value).
+	**/
+	public var sigma(default, set) : Float;
+		
+	var values : Array<Float>;
+	var shader : h3d.shader.BlurShader;
+	var pass : h3d.mat.Pass;
+	var manager : h3d.shader.Manager;
+	var plan : h3d.prim.Plan2D;
+
+	public function new(quality = 1, sigma = 1.) {
+		this.quality = quality;
+		this.sigma = sigma;
+		manager = new h3d.shader.Manager(["output.position", "output.color"]);
+		shader = new h3d.shader.BlurShader();
+		pass = new h3d.mat.Pass("blur", new hxsl.ShaderList(shader));
+		pass.culling = None;
+		pass.depth(false, Always);
+		plan = new h3d.prim.Plan2D();
+	}
+	
+	function set_quality(q) {
+		values = null;
+		return quality = q;
+	}
+
+	function set_sigma(s) {
+		values = null;
+		return sigma = s;
+	}
+	
+	function gauss( x:Int, s:Float ) : Float {
+		if( s <= 0 ) return x == 0 ? 1 : 0;
+		var sq = s * s;
+		var p = Math.pow(2.718281828459, -(x * x) / (2 * sq));
+		return p / Math.sqrt(2 * Math.PI * sq);
+	}
+
+	public function apply( src : h3d.mat.Texture, ?tmp : h3d.mat.Texture, isDepth = false ) {
+		
+		if( quality == 0 ) return;
+		
+		var alloc = tmp == null;
+		if( alloc )
+			tmp = new h3d.mat.Texture(src.width, src.height, [Target, TargetNoFlipY]);
+			
+		if( values == null ) {
+			values = [];
+			var tot = 0.;
+			for( i in 0...quality + 1 ) {
+				var g = gauss(i, sigma);
+				values[i] = g;
+				tot += g;
+				if( i > 0 ) tot += g;
+			}
+			for( i in 0...quality + 1 )
+				values[i] /= tot;
+		}
+			
+		var engine = h3d.Engine.getCurrent();
+		
+		shader.Quality = quality + 1;
+		shader.texture = src;
+		shader.values = values;
+		shader.isDepth = isDepth;
+		shader.pixel.set(1 / src.width, 0);
+		
+		var shaders : Array<hxsl.Shader> = [shader];
+		var rts = manager.compileShaders(shaders);
+		
+
+		engine.setTarget(tmp,0xFFFF0000);
+		engine.selectMaterial(pass);
+		engine.selectShader(rts);
+		var buf = new h3d.shader.Buffers(rts);
+		manager.fillGlobals(buf, rts);
+		manager.fillParams(buf, rts, shaders);
+		engine.uploadShaderBuffers(buf, Globals);
+		engine.uploadShaderBuffers(buf, Params);
+		engine.uploadShaderBuffers(buf, Textures);
+		plan.render(engine);
+		engine.setTarget(null);
+
+		engine.setTarget(src);
+		shader.texture = tmp;
+		shader.pixel.set(0, 1 / tmp.height);
+		manager.fillParams(buf, rts, shaders);
+		engine.uploadShaderBuffers(buf, Params);
+		engine.uploadShaderBuffers(buf, Textures);
+		plan.render(engine);
+		engine.setTarget(null);
+
+		if( alloc )
+			tmp.dispose();
+	}
+
+}

+ 13 - 1
h3d/pass/ShadowMap.hx

@@ -3,6 +3,7 @@ package h3d.pass;
 class ShadowMap extends Base {
 
 	var texture : h3d.mat.Texture;
+	var blurTexture : h3d.mat.Texture;
 	var lightCamera : h3d.Camera;
 	var shadowMapId : Int;
 	var shadowProjId : Int;
@@ -14,6 +15,7 @@ class ShadowMap extends Base {
 	public var color : h3d.Vector;
 	public var power = 10.0;
 	public var bias = 0.01;
+	public var blur : Blur;
 
 	public function new(size) {
 		super();
@@ -29,6 +31,7 @@ class ShadowMap extends Base {
 		shadowPowerId = hxsl.Globals.allocID("shadow.power");
 		shadowBiasId = hxsl.Globals.allocID("shadow.bias");
 		color = new h3d.Vector();
+		blur = new Blur(2, 3);
 	}
 	
 	public dynamic function getSceneBounds( bounds : h3d.col.Bounds ) {
@@ -54,15 +57,24 @@ class ShadowMap extends Base {
 	
 	override function draw( ctx : h3d.scene.RenderContext, passes) {
 		if( texture == null || texture.width != size ) {
-			if( texture != null ) texture.dispose();
+			if( texture != null ) {
+				texture.dispose();
+				blurTexture.dispose();
+				blurTexture = null;
+			}
 			texture = new h3d.mat.Texture(size, size, [Target, TargetDepth, TargetNoFlipY]);
 		}
+		if( blur.quality > 0 && blurTexture == null )
+			blurTexture = new h3d.mat.Texture(size, size, [Target, TargetNoFlipY]);
 		var ct = ctx.camera.target;
 		lightCamera.target.set(ct.x, ct.y, ct.z);
 		lightCamera.pos.set(ct.x - lightDirection.x, ct.y - lightDirection.y, ct.z - lightDirection.z);
 		ctx.engine.setTarget(texture, 0xFFFFFFFF);
 		passes = super.draw(ctx, passes);
 		ctx.engine.setTarget(null);
+		
+		blur.apply(texture, blurTexture, true);
+		
 		ctx.sharedGlobals.set(shadowMapId, texture);
 		ctx.sharedGlobals.set(shadowProjId, lightCamera.m);
 		ctx.sharedGlobals.set(shadowColorId, color);

+ 39 - 0
h3d/shader/BlurShader.hx

@@ -0,0 +1,39 @@
+package h3d.shader;
+
+class BlurShader extends hxsl.Shader {
+
+	static var SRC = {
+		@input var input : {
+			position : Vec2,
+			uv : Vec2,
+		};
+		var output : {
+			position : Vec4,
+			color : Vec4,
+		};
+		
+		@param var texture : Sampler2D;
+		@param @const var Quality : Int;
+		@param @const var isDepth : Bool;
+		@param var values : Array<Float,Quality>;
+		@param var pixel : Vec2;
+		
+		function vertex() {
+			output.position = vec4(input.position, 0, 1);
+		}
+		function fragment() {
+			if( isDepth ) {
+				var val = 0.;
+				for( i in -Quality+1...Quality )
+					val += unpack(texture.get(input.uv + pixel * i.toFloat())) * values[i < 0 ? -i : i];
+				output.color = pack(val.min(0.9999999));
+			} else {
+				var color = vec4(0, 0, 0, 0);
+				for( i in -Quality+1...Quality )
+					color += texture.get(input.uv + pixel * i.toFloat()) * values[i < 0 ? -i : i];
+				output.color = color;
+			}
+		}
+	}
+	
+}

+ 4 - 0
hxsl/Checker.hx

@@ -424,8 +424,10 @@ class Checker {
 			if( (e == null) != (curFun.ret == TVoid) )
 				error("This function should return " + curFun.ret.toString(), e.pos);
 			var e = e == null ? null : typeWith(e, curFun.ret);
+			type = TVoid;
 			TReturn(e);
 		case EFor(v, it, block):
+			type = TVoid;
 			var it = typeExpr(it, Value);
 			switch( it.t ) {
 			case TArray(t, _):
@@ -445,9 +447,11 @@ class Checker {
 			}
 		case EContinue:
 			if( !inLoop ) error("Continue outside loop", e.pos);
+			type = TVoid;
 			TContinue;
 		case EBreak:
 			if( !inLoop ) error("Break outside loop", e.pos);
+			type = TVoid;
 			TBreak;
 		case EArray(e1, e2):
 			var e1 = typeExpr(e1, Value);

+ 10 - 5
hxsl/Eval.hx

@@ -138,6 +138,13 @@ class Eval {
 		return handleReturn(e);
 	}
 	
+	function evalCall( g : TGlobal, args : Array<TExpr> ) {
+		return switch( [g,args] ) {
+		case [ToFloat, [ { e : TConst(CInt(i)) } ]]: TConst(CFloat(i));
+		default: null;
+		}
+	}
+	
 	function evalExpr( e : TExpr ) : TExpr {
 		var d : TExprDef = switch( e.e ) {
 		case TGlobal(_), TConst(_): e.e;
@@ -166,8 +173,9 @@ class Eval {
 			var c = evalExpr(c);
 			var args = [for( a in args ) evalExpr(a)];
 			switch( c.e ) {
-			case TGlobal(_):
-				TCall(c, args);
+			case TGlobal(g):
+				var v = evalCall(g, args);
+				if( v != null ) v else TCall(c, args);
 			case TVar(v) if( funMap.exists(v) ):
 				var f = funMap.get(v);
 				var outExprs = [], undo = [];
@@ -340,13 +348,10 @@ class Eval {
 			var e = switch( it.e ) {
 			case TBinop(OpInterval, { e : TConst(CInt(start)) }, { e : TConst(CInt(len)) } ):
 				var out = [];
-				var old = varMap;
 				for( i in start...len ) {
-					varMap = [for( c in old.keys() ) c => old.get(c)];
 					constants.set(v, TConst(CInt(i)));
 					out.push(evalExpr(loop));
 				}
-				varMap = old;
 				constants.remove(v);
 				TBlock(out);
 			default:

+ 4 - 2
hxsl/Flatten.hx

@@ -153,8 +153,10 @@ class Flatten {
 				var emat = switch( e.e ) { case TCall(e, _): e; default: throw "assert"; };
 				return { e : TCall(emat, args), t : e.t, p : e.p };
 			}
-		case TArray( { e : TArrayDecl(el) }, { e : TConst(CInt(i)) } ) if( i >= 0 && i < el.length ):
-			return el[i];
+		case TArray( { e : TArrayDecl(el) }, { e : TConst(CInt(i)) } ):
+			if( i >= 0 && i < el.length )
+				return el[i];
+			Error.t("Reading outside array bounds", e.p);
 		default:
 		}
 		return e;

+ 2 - 0
samples/shadows/Main.hx

@@ -39,6 +39,8 @@ class Main extends hxd.App {
 		
 		shadow = cast(s3d.getPass("shadow"), h3d.pass.ShadowMap);
 		shadow.lightDirection = dir.direction;
+		shadow.size = 512;
+		shadow.blur.quality = 4;
 	}
 	
 	override function update( dt : Float ) {