Browse Source

RendererFX: starting support of renderer FX volume

lviguier 3 months ago
parent
commit
02f971c023
4 changed files with 137 additions and 0 deletions
  1. 1 0
      h3d/impl/RendererFX.hx
  2. 63 0
      h3d/impl/RendererFXVolume.hx
  3. 5 0
      h3d/scene/Renderer.hx
  4. 68 0
      h3d/scene/pbr/Renderer.hx

+ 1 - 0
h3d/impl/RendererFX.hx

@@ -18,4 +18,5 @@ interface RendererFX {
 	public function begin( r : h3d.scene.Renderer, step : Step ) : Void;
 	public function begin( r : h3d.scene.Renderer, step : Step ) : Void;
 	public function end( r : h3d.scene.Renderer, step : Step ) : Void;
 	public function end( r : h3d.scene.Renderer, step : Step ) : Void;
 	public function dispose() : Void;
 	public function dispose() : Void;
+	public function transition( r1 : RendererFX, r2 : RendererFX, t : Float ) : RendererFX;
 }
 }

+ 63 - 0
h3d/impl/RendererFXVolume.hx

@@ -0,0 +1,63 @@
+package h3d.impl;
+
+enum Shape {
+	Sphere(radius : Float);
+	Box(width : Float, height: Float);
+}
+
+class RendererFXVolume extends h3d.scene.Object {
+	public var priority : Int;
+	public var innerShape : Shape;
+	public var outerShape : Shape;
+	public var effects : Array<h3d.impl.RendererFX> = [];
+
+	var renderer : h3d.scene.Renderer;
+	var factor : Float = 0.;
+	var cam : h3d.Camera;
+
+	public function new(?parent : h3d.scene.Object) {
+		super(parent);
+		this.innerShape = Sphere(1);
+		this.outerShape = Sphere(1);
+	}
+
+	override function sync(ctx : h3d.scene.RenderContext) {
+		super.sync(ctx);
+
+		#if !editor
+		if (renderer == null)
+			this.renderer = ctx.scene.renderer;
+
+		if (effects == null) return;
+		var newFactor = getFactor();
+		if (factor <= 0 && newFactor > 0)
+			this.renderer.volumeEffects.push(this);
+
+		if (factor > 0 && newFactor <= 0)
+			this.renderer.volumeEffects.remove(this);
+
+		factor = newFactor;
+		#end
+	}
+
+	override function onRemove() {
+		super.onRemove();
+		if (this.renderer == null) return;
+		this.renderer.volumeEffects.remove(this);
+	}
+
+	public function getFactor() : Float {
+		if (cam == null)
+			cam = getScene().camera;
+		var distance = (getAbsPos().getPosition() - cam.pos).length();
+
+		switch ([innerShape, outerShape]) {
+			case [Sphere(r1), Sphere(r2)]:
+				if (distance < r1) return 1;
+				if (distance > r2) return 0;
+				return 1 - hxd.Math.clamp((distance - r1) / (r2 - r1), 0, 1);
+			default:
+				return 0.;
+		}
+	}
+}

+ 5 - 0
h3d/scene/Renderer.hx

@@ -34,6 +34,8 @@ class Renderer extends hxd.impl.AnyProps {
 	#end
 	#end
 
 
 	public var effects : Array<h3d.impl.RendererFX> = [];
 	public var effects : Array<h3d.impl.RendererFX> = [];
+	public var volumeEffects : Array<h3d.impl.RendererFXVolume> = [];
+	var toRemove : Array<h3d.impl.RendererFX> = [];
 
 
 	public var renderMode : RenderMode = Default;
 	public var renderMode : RenderMode = Default;
 
 
@@ -61,6 +63,9 @@ class Renderer extends hxd.impl.AnyProps {
 			p.dispose();
 			p.dispose();
 		for( f in effects )
 		for( f in effects )
 			f.dispose();
 			f.dispose();
+		for( v in volumeEffects )
+			for (e in v.effects)
+				e.dispose();
 		if ( ctx.lightSystem != null )
 		if ( ctx.lightSystem != null )
 			ctx.lightSystem.dispose();
 			ctx.lightSystem.dispose();
 		passObjects = new Map();
 		passObjects = new Map();

+ 68 - 0
h3d/scene/pbr/Renderer.hx

@@ -200,6 +200,71 @@ class Renderer extends h3d.scene.Renderer {
 		ctx.pbrLightPass = pbrLightPass;
 		ctx.pbrLightPass = pbrLightPass;
 	}
 	}
 
 
+	override function startEffects() {
+		if (volumeEffects.length == 1) {
+			for (e in volumeEffects[0].effects) {
+				toRemove.push(e);
+				this.effects.push(e);
+			}
+		}
+		else if (volumeEffects.length >= 2) {
+			// When there is more than 2 active volume effects, we take the top 2 prios and
+			// blend them (because blend with more than 2 values isn't commutative)
+			var r1 = volumeEffects[0];
+			var r2 = volumeEffects[1];
+			for (idx => v in volumeEffects) {
+				if (volumeEffects[idx].priority > hxd.Math.min(r1.priority, r2.priority)) {
+					if (r1.priority < volumeEffects[idx].priority)
+						r1 = volumeEffects[idx];
+					else
+						r2 = volumeEffects[idx];
+				}
+			}
+
+			function containsEffectType(volume : h3d.impl.RendererFXVolume, e : h3d.impl.RendererFX) {
+				var cl = Type.getClass(e);
+				for (effect in volume.effects)
+					if (Std.isOfType(effect, cl))
+						return true;
+				return false;
+			}
+
+			// Push unique renderer FX from volume 1 and volume 2
+			for (e in r1.effects) {
+				if (!containsEffectType(r2, e)) {
+					this.toRemove.push(e);
+					this.effects.push(e);
+				}
+			}
+			for (e in r2.effects) {
+				if (!containsEffectType(r1, e)) {
+					this.toRemove.push(e);
+					this.effects.push(e);
+				}
+			}
+
+			// Manage blending of renderer FX that are in volume 1 and volume 2
+			// Look for which direction the blend should be (r1 -> r2 or r2 -> r1)
+			var isR1toR2 = r1.priority < r2.priority;
+			var volume1 = isR1toR2 ? r1 : r2;
+			var volume2 = isR1toR2 ? r2 : r1;
+			for (e1 in volume1.effects) {
+				if (!containsEffectType(volume2, e1))
+					continue;
+
+				for (e2 in volume2.effects) {
+					var newEffect = isR1toR2 ? e1.transition(e1, e2, volume2.getFactor()) :  e2.transition(e2, e1, volume1.getFactor());
+					if (newEffect != null) {
+						this.toRemove.push(newEffect);
+						this.effects.push(newEffect);
+					}
+				}
+			}
+		}
+
+		super.startEffects();
+	}
+
 	inline function cullPasses( passes : h3d.pass.PassList, f : h3d.col.Collider -> Bool ) {
 	inline function cullPasses( passes : h3d.pass.PassList, f : h3d.col.Collider -> Bool ) {
 		var prevCollider = null;
 		var prevCollider = null;
 		var prevResult = true;
 		var prevResult = true;
@@ -736,6 +801,9 @@ class Renderer extends h3d.scene.Renderer {
 			hxd.Window.getInstance().removeEventTarget(onEvent);
 			hxd.Window.getInstance().removeEventTarget(onEvent);
 		}
 		}
 		mark("vsync");
 		mark("vsync");
+
+		for (e in toRemove)
+			effects.remove(e);
 	}
 	}
 
 
 	var debugPushPos : { x : Float, y : Float }
 	var debugPushPos : { x : Float, y : Float }