فهرست منبع

added Object.cullingBits for PointLight shadow map plane culling

ncannasse 6 سال پیش
والد
کامیت
2f80ccd78a
6فایلهای تغییر یافته به همراه155 افزوده شده و 57 حذف شده
  1. 70 23
      h3d/pass/PassList.hx
  2. 6 1
      h3d/pass/PointShadowMap.hx
  3. 12 3
      h3d/scene/Object.hx
  4. 17 0
      h3d/scene/pbr/LightSystem.hx
  5. 6 23
      h3d/scene/pbr/Renderer.hx
  6. 44 7
      samples/Lights.hx

+ 70 - 23
h3d/pass/PassList.hx

@@ -17,23 +17,55 @@ class PassListIterator {
 
 @:access(h3d.pass.PassObject)
 class PassList {
+
 	var current : PassObject;
-	var whole : PassObject;
-	var lastWhole : PassObject;
+	var discarded : PassObject;
+	var lastDisc : PassObject;
 
 	public function new(?current) {
 		init(current);
 	}
 
+	/**
+		Set the passes and empty the discarded list
+	**/
 	public inline function init(pass) {
 		current = pass;
-		whole = lastWhole = null;
+		discarded = lastDisc = null;
 	}
 
+	/**
+		Put back discarded passes into the pass list
+	**/
 	public inline function reset() {
-		if( whole != null ) {
-			current = whole;
-			whole = lastWhole = null;
+		if( discarded != null ) {
+			lastDisc.next = current;
+			current = discarded;
+			discarded = lastDisc = null;
+		}
+	}
+
+	/**
+		Save the discarded list, allow to perfom some filters, then call "load" to restore passes
+	**/
+	public inline function save() {
+		return lastDisc;
+	}
+
+	/**
+		load state that was save() before
+	**/
+	public inline function load( p : PassObject ) {
+		if( lastDisc != p ) {
+			lastDisc.next = current;
+			if( p == null ) {
+				current = discarded;
+				discarded = null;
+			} else {
+				current = p.next;
+				p.next = null;
+			}
+			lastDisc = p;
 		}
 	}
 
@@ -41,26 +73,34 @@ class PassList {
 		return current == null;
 	}
 
+	/**
+		Put all passes into discarded list
+	**/
 	public function clear() {
-		if( whole == null )
-			whole = current;
-		else if( current != null ) {
-			lastWhole.next = current;
-			lastWhole = current;
-		}
+		if( current == null )
+			return;
+		if( discarded == null )
+			discarded = current;
+		else
+			lastDisc.next = current;
+		var p = current;
+		while( p.next != null ) p = p.next;
+		lastDisc = p;
 		current = null;
 	}
 
 	public inline function sort( f : PassObject -> PassObject -> Int ) {
 		current = haxe.ds.ListSort.sortSingleLinked(current, f);
-		if( lastWhole != null ) lastWhole.next = current;
 	}
 
+	/**
+		Filter current passes, add results to discarded list
+	**/
 	public inline function filter( f : PassObject -> Bool ) {
 		var head = null;
 		var prev = null;
-		var discarded = whole;
-		var discardedQueue = lastWhole;
+		var disc = discarded;
+		var discQueue = lastDisc;
 		var cur = current;
 		while( cur != null ) {
 			if( f(cur) ) {
@@ -71,26 +111,33 @@ class PassList {
 					prev = cur;
 				}
 			} else {
-				if( discarded == null )
-					discarded = discardedQueue = cur;
+				if( disc == null )
+					disc = discQueue = cur;
 				else {
-					discardedQueue.next = cur;
-					discardedQueue = cur;
+					discQueue.next = cur;
+					discQueue = cur;
 				}
 			}
 			cur = cur.next;
 		}
 		if( prev != null )
 			prev.next = null;
-		if( discardedQueue != null )
-			discardedQueue.next = head;
+		if( discQueue != null )
+			discQueue.next = null;
 		current = head;
-		whole = discarded;
-		lastWhole = discardedQueue;
+		discarded = disc;
+		lastDisc = discQueue;
 	}
 
 	public inline function iterator() {
 		return new PassListIterator(current);
 	}
 
+	/**
+		Iterate on all discarded elements, if any
+	**/
+	public inline function getFiltered() {
+		return new PassListIterator(discarded);
+	}
+
 }

+ 6 - 1
h3d/pass/PointShadowMap.hx

@@ -129,7 +129,7 @@ class PointShadowMap extends Shadows {
 		var pointLight = cast(light, h3d.scene.pbr.PointLight);
 		var absPos = light.getAbsPos();
 
-		for(i in 0 ... 6){
+		for( i in 0...6 ) {
 			lightCamera.setCubeMap(i, new h3d.Vector(absPos.tx, absPos.ty, absPos.tz));
 			lightCamera.zFar = pointLight.range;
 			lightCamera.zNear = pointLight.zNear;
@@ -137,7 +137,12 @@ class PointShadowMap extends Shadows {
 
 			ctx.engine.pushTarget(texture, i);
 			ctx.engine.clear(0xFFFFFF, 1);
+			var save = passes.save();
+			var bit = 1 << i;
+			passes.filter(function(p) return p.obj.cullingBits & bit == 0);
 			super.draw(passes);
+			passes.load(save);
+
 			ctx.engine.popTarget();
 		}
 

+ 12 - 3
h3d/scene/Object.hx

@@ -13,8 +13,10 @@ package h3d.scene;
 	public var FIgnoreBounds = 0x200;
 	public var FIgnoreCollide = 0x400;
 	public var FIgnoreParentTransform = 0x800;
-	public inline function new() {
-		this = 0;
+	public var FCullingPlansBits = 0x1000;
+	public var FCullingPlansBitsLast = 0x20000;
+	public inline function new(value) {
+		this = value;
 	}
 	public inline function toInt() return this;
 	public inline function has(f:ObjectFlags) return this & f.toInt() != 0;
@@ -111,6 +113,11 @@ class Object implements hxd.impl.Serializable {
 	**/
 	public var culled(get, set) : Bool;
 
+	/**
+		Six additional bits that are used for point lights culling. Tells in which planes the object should be culled
+	**/
+	public var cullingBits(get,set) : Int;
+
 	/**
 		When an object is not visible or culled, its animation does not get synchronized unless you set alwaysSync=true
 	**/
@@ -157,7 +164,7 @@ class Object implements hxd.impl.Serializable {
 		Create a new empty object, and adds it to the parent object if not null.
 	**/
 	public function new( ?parent : Object ) {
-		flags = new ObjectFlags();
+		flags = new ObjectFlags(0);
 		absPos = new h3d.Matrix();
 		absPos.identity();
 		x = 0; y = 0; z = 0; scaleX = 1; scaleY = 1; scaleZ = 1;
@@ -181,6 +188,7 @@ class Object implements hxd.impl.Serializable {
 	inline function get_ignoreCollide() return flags.has(FIgnoreCollide);
 	inline function get_allowSerialize() return !flags.has(FNoSerialize);
 	inline function get_ignoreParentTransform() return flags.has(FIgnoreParentTransform);
+	inline function get_cullingBits() return (flags.toInt() >> 12) & 63;
 	inline function set_posChanged(b) return flags.set(FPosChanged, b || follow != null);
 	inline function set_culled(b) return flags.set(FCulled, b);
 	inline function set_visible(b) return flags.set(FVisible,b);
@@ -193,6 +201,7 @@ class Object implements hxd.impl.Serializable {
 	inline function set_ignoreCollide(b) return flags.set(FIgnoreCollide, b);
 	inline function set_allowSerialize(b) return !flags.set(FNoSerialize, !b);
 	inline function set_ignoreParentTransform(b) return flags.set(FIgnoreParentTransform, b);
+	inline function set_cullingBits(b) { flags = new ObjectFlags((flags.toInt() & ~0x3F000) | (b << 12)); return b; }
 
 	/**
 		Create an animation instance bound to the object, set it as currentAnimation and play it.

+ 17 - 0
h3d/scene/pbr/LightSystem.hx

@@ -13,6 +13,23 @@ class LightSystem extends h3d.scene.LightSystem {
 		return shaders;
 	}
 
+	/**
+		This can be overriden in order to mark meshes as culled=true so their shadows
+		doesn't get drawn for this specific light.
+	**/
+	public function cullObjectsForLight( light : Light ) {
+	}
+
+	public function drawLight( light : Light, passes : h3d.pass.PassList ) {
+		light.shadows.setContext(ctx);
+		cullObjectsForLight(light);
+		passes.filter(function(p) return !p.obj.culled);
+		light.shadows.draw(passes);
+		for( p in passes.getFiltered() )
+			p.obj.culled = false;
+		passes.reset();
+	}
+
 	public function drawScreenLights( r : h3d.scene.Renderer, lightPass : h3d.pass.ScreenFx<Dynamic> ) {
 		var plight = @:privateAccess ctx.lights;
 		var currentTarget = ctx.engine.getCurrentTarget();

+ 6 - 23
h3d/scene/pbr/Renderer.hx

@@ -150,35 +150,18 @@ class Renderer extends h3d.scene.Renderer {
 		draw("overlay");
 	}
 
-	/**
-		This can be overriden in order to mark meshes as culled=true so their shadows
-		doesn't get drawn for this specific light.
-	**/
-	public function cullObjectsForLight( light : Light ) {
-	}
-
-	function drawShadows(){
+	function drawShadows( ls : LightSystem ) {
 		var light = @:privateAccess ctx.lights;
 		var passes = get("shadow");
-		if(!shadows)
-			passes = null;
+		if( !shadows )
+			passes.clear();
 		while( light != null ) {
 			var plight = Std.instance(light, h3d.scene.pbr.Light);
-			if( plight != null ) drawLightShadows(plight, passes);
+			if( plight != null ) ls.drawLight(plight, passes);
 			light = light.next;
 		}
 	}
 
-	function drawLightShadows( light : Light, passes : h3d.pass.PassList ) {
-		light.shadows.setContext(ctx);
-		for( p in passes )
-			p.obj.culled = false;
-		cullObjectsForLight(light);
-		passes.filter(function(p) return !p.obj.culled);
-		light.shadows.draw(passes);
-		passes.reset();
-	}
-
 	function apply( step : hxd.prefab.rfx.RendererFX.Step ) {
 		for( f in effects )
 			if( f.enabled )
@@ -212,8 +195,9 @@ class Renderer extends h3d.scene.Renderer {
 		ctx.setGlobal("occlusionMap",{ texture : pbr, channel : hxsl.Channel.B });
 		ctx.setGlobal("bloom",null);
 
+		var ls = Std.instance(getLightSystem(), LightSystem);
 		var count = ctx.engine.drawCalls;
-		drawShadows();
+		if( ls != null ) drawShadows(ls);
 		ctx.lightSystem.drawPasses = ctx.engine.drawCalls - count;
 
 		setTargets([albedo,normal,pbr,other]);
@@ -305,7 +289,6 @@ class Renderer extends h3d.scene.Renderer {
 
 		// Draw DirLight, screenShader
 		pbrProps.isScreen = true;
-		var ls = Std.instance(getLightSystem(), LightSystem);
 		if( ls != null ) {
 			var count = ctx.engine.drawCalls;
 			ls.drawScreenLights(this, lpass);

+ 44 - 7
samples/Lights.hx

@@ -1,9 +1,34 @@
 import hxd.Math;
 
+class CustomLS extends h3d.scene.pbr.LightSystem {
+
+	var s3d : h3d.scene.Object;
+
+	public function new(s3d) {
+		super();
+		this.s3d = s3d;
+	}
+
+	override function cullObjectsForLight(light:h3d.scene.pbr.Light) {
+		var pl = Std.instance(light, h3d.scene.pbr.PointLight);
+		if( pl != null )
+			for( o in s3d ) {
+				var dx = o.x - pl.x;
+				var dy = o.y - pl.y;
+				var dz = o.z - pl.z;
+				var d = dx*dx+dy*dy+dz*dz;
+				var r = pl.range + o.scaleX;
+				o.culled = d > r * r;
+				o.cullingBits = 16; // don't project any shadows on top plane of the light
+			}
+	}
+
+}
+
 class Lights extends SampleApp {
 
 	var lights : Array<h3d.scene.pbr.Light>;
-	var movingObjects : Array<{ m : h3d.scene.Mesh, pos : Float, ray : Float, speed : Float }> = [];
+	var movingObjects : Array<{ m : h3d.scene.Mesh, cx : Float, cy : Float, pos : Float, ray : Float, speed : Float }> = [];
 	var curLight : Int = 0;
 	var bitmap : h2d.Bitmap;
 	var inf : h2d.Text;
@@ -17,7 +42,7 @@ class Lights extends SampleApp {
 		var prim = new h3d.prim.Grid(100,100,1,1);
 		prim.addNormals();
 		prim.addUVs();
-		
+
 		var floor = new h3d.scene.Mesh(prim, s3d);
 		floor.material.castShadows = false;
 		floor.x = -50;
@@ -48,13 +73,15 @@ class Lights extends SampleApp {
 			m.material.color.normalize();
 			m.scale(0.5 + Math.random() * 4);
 			m.z = 2 + Math.random() * 5;
-			movingObjects.push({ m : m, pos : Math.random() * Math.PI * 2, ray : 8 + Math.random() * 50, speed : (0.5 + Math.random()) * 0.2 });
+			var cx = (Math.random() - 0.5) * 20;
+			var cy = (Math.random() - 0.5) * 20;
+			movingObjects.push({ m : m, pos : Math.random() * Math.PI * 2, cx : cx, cy : cy, ray : 8 + Math.random() * 50, speed : (0.5 + Math.random()) * 0.2 });
 		}
 
 		var pt = new h3d.scene.pbr.PointLight(s3d);
 		pt.setPosition(0,0,15);
-		pt.range = 70;
-		pt.color.scale3(10);
+		pt.range = 40;
+		pt.color.scale3(20);
 
 		var sp = new h3d.scene.pbr.SpotLight(s3d);
 		sp.setPosition(-30,-30,30);
@@ -95,6 +122,11 @@ class Lights extends SampleApp {
 				l.shadows.mode = modes[sh];
 		});
 
+		var baseLS = s3d.lightSystem;
+		addCheck("DynCulling", function() return s3d.lightSystem != baseLS, function(b) {
+			s3d.lightSystem = b ? new CustomLS(s3d) : baseLS;
+		});
+
 		bitmap = new h2d.Bitmap(null, s2d);
 		bitmap.scale(192 / 1024);
 		bitmap.filter = h2d.filter.ColorMatrix.grayed();
@@ -105,14 +137,19 @@ class Lights extends SampleApp {
 	override function update(dt:Float) {
 		for( m in movingObjects ) {
 			m.pos += m.speed / m.ray;
-			m.m.x = Math.cos(m.pos) * m.ray;
-			m.m.y = Math.sin(m.pos) * m.ray;
+			m.m.x = m.cx + Math.cos(m.pos) * m.ray;
+			m.m.y = m.cy + Math.sin(m.pos) * m.ray;
 		}
 		var light = lights[curLight];
 		var tex = light == null ? null : light.shadows.getShadowTex();
 		bitmap.tile = tex == null || tex.flags.has(Cube) ? null : h2d.Tile.fromTexture(tex);
 		bitmap.x = s2d.width - (bitmap.tile == null ? 0 : bitmap.tile.width) * bitmap.scaleX;
 		inf.text = "Shadows Draw calls: "+ s3d.lightSystem.drawPasses;
+
+		for( o in s3d ) {
+			o.culled = false;
+			o.cullingBits = 0;
+		}
 	}
 
 	static function main() {