瀏覽代碼

Light: add rectangle light

lviguier 2 月之前
父節點
當前提交
169da4785b
共有 2 個文件被更改,包括 289 次插入10 次删除
  1. 186 0
      h3d/scene/pbr/RectangleLight.hx
  2. 103 10
      h3d/shader/pbr/Light.hx

+ 186 - 0
h3d/scene/pbr/RectangleLight.hx

@@ -0,0 +1,186 @@
+package h3d.scene.pbr;
+
+import h3d.prim.Cube;
+
+class RectangleLight extends Light {
+
+	var pbr : h3d.shader.pbr.Light.RectangleLight;
+	public var width(default, set) : Float = 0.5;
+	public var height(default, set) : Float = 0.5;
+	public var verticalAngle(default, set) : Float = 0.5;
+	public var horizontalAngle(default, set) : Float = 0.5;
+	public var range(default, set) : Float = 1;
+	public var fallOff : Float = 1;
+
+	public function new(?parent) {
+		pbr = new h3d.shader.pbr.Light.RectangleLight();
+		shadows = new h3d.pass.CapsuleShadowMap(this, true);
+		super(pbr,parent);
+		range = 10;
+	}
+
+	public function set_width(v) {
+		width = v;
+		updatePrim();
+		return width;
+	}
+
+	public function set_height(v) {
+		height = v;
+		updatePrim();
+		return height;
+	}
+
+	public function set_verticalAngle(v) {
+		verticalAngle = v;
+		updatePrim();
+		return verticalAngle;
+	}
+
+	public function set_horizontalAngle(v) {
+		horizontalAngle = v;
+		updatePrim();
+		return horizontalAngle;
+	}
+
+	function set_range(v:Float) {
+		range = v;
+		updatePrim();
+		return v;
+	}
+
+	public override function clone( ?o : h3d.scene.Object ) : h3d.scene.Object {
+		var rec = o == null ? new RectangleLight(null) : cast o;
+		super.clone(rec);
+		rec.width = width;
+		rec.height = height;
+		rec.horizontalAngle = horizontalAngle;
+		rec.verticalAngle = verticalAngle;
+		rec.range = range;
+		rec.fallOff = fallOff;
+		return rec;
+	}
+
+	override function draw(ctx:RenderContext) {
+		primitive.render(ctx.engine);
+	}
+
+	override function sync(ctx) {
+		super.sync(ctx);
+
+		pbr.lightColor.load(_color);
+		var power = power;
+		pbr.lightColor.scale(power * power);
+		pbr.lightPos.set(absPos.tx, absPos.ty, absPos.tz);
+		pbr.width = width;
+		pbr.height = height;
+		pbr.p0.load(new h3d.Vector(0, -width / 2, -height / 2).transformed(getAbsPos()));
+		pbr.p1.load(new h3d.Vector(0, width / 2, -height / 2).transformed(getAbsPos()));
+		pbr.p2.load(new h3d.Vector(0, -width / 2, height / 2).transformed(getAbsPos()));
+		pbr.p3.load(new h3d.Vector(0, width / 2, height / 2).transformed(getAbsPos()));
+		pbr.lightDir.load(absPos.front());
+		pbr.verticalAngle = hxd.Math.cos(hxd.Math.degToRad(verticalAngle/2.0));
+		pbr.verticalFallOff = hxd.Math.cos(hxd.Math.degToRad(hxd.Math.min(verticalAngle/2.0, fallOff)));
+		pbr.horizontalAngle = hxd.Math.cos(hxd.Math.degToRad(horizontalAngle/2.0));
+		pbr.horizontalFallOff = hxd.Math.cos(hxd.Math.degToRad(hxd.Math.min(horizontalAngle/2.0, fallOff)));
+		pbr.range = range;
+		pbr.invLightRange4 = 1 / (range * range * range * range);
+		pbr.occlusionFactor = occlusionFactor;
+	}
+
+	var s = new h3d.col.Sphere();
+	var d = new h3d.Vector();
+	override function emit(ctx:RenderContext) {
+		if( ctx.computingStatic ) {
+			super.emit(ctx);
+			return;
+		}
+
+		if( ctx.pbrLightPass == null )
+			throw "Rendering a pbr light require a PBR compatible scene renderer";
+
+		d.load(absPos.front());
+		d.scale(range / 2.0);
+		s.x = absPos.tx + d.x;
+		s.y = absPos.ty + d.y;
+		s.z = absPos.tz + d.z;
+		s.r = range / 2.0;
+
+		if( !inFrustum(ctx.camera.frustum) )
+			return;
+
+		super.emit(ctx);
+		ctx.emitPass(ctx.pbrLightPass, this);
+	}
+
+	function updatePrim() {
+		if ( primitive != null )
+			primitive.dispose();
+
+		var p1 = new h3d.col.Point(0,-width / 2,-height / 2);
+		var p2 = new h3d.col.Point(0, width / 2, -height / 2);
+		var p3 = new h3d.col.Point(0,-width / 2,height / 2);
+		var p4 = new h3d.col.Point(0, width / 2, height / 2);
+		var p5 = p1 + new h3d.col.Point(range,-hxd.Math.sin(hxd.Math.degToRad(horizontalAngle / 2)) * range,-hxd.Math.sin(hxd.Math.degToRad(verticalAngle / 2)) * range);
+		var p6 =  p2 + new h3d.col.Point(range,hxd.Math.sin(hxd.Math.degToRad(horizontalAngle / 2)) * range,-hxd.Math.sin(hxd.Math.degToRad(verticalAngle / 2)) * range);
+		var p7 =  p3 + new h3d.col.Point(range,-hxd.Math.sin(hxd.Math.degToRad(horizontalAngle / 2)) * range,hxd.Math.sin(hxd.Math.degToRad(verticalAngle / 2)) * range);
+		var p8 =  p4 + new h3d.col.Point(range,hxd.Math.sin(hxd.Math.degToRad(horizontalAngle / 2)) * range,hxd.Math.sin(hxd.Math.degToRad(verticalAngle / 2)) * range);
+
+		var points = new Array<h3d.col.Point>();
+
+		// Back
+		points.push(p1);
+		points.push(p3);
+		points.push(p4);
+		points.push(p4);
+		points.push(p2);
+		points.push(p1);
+
+		// Front
+		points.push(p6);
+		points.push(p8);
+		points.push(p7);
+		points.push(p7);
+		points.push(p5);
+		points.push(p6);
+
+		// Left
+		points.push(p1);
+		points.push(p5);
+		points.push(p7);
+		points.push(p7);
+		points.push(p3);
+		points.push(p1);
+
+		// Top
+		points.push(p3);
+		points.push(p7);
+		points.push(p8);
+		points.push(p8);
+		points.push(p4);
+		points.push(p3);
+
+		// Right
+		points.push(p4);
+		points.push(p8);
+		points.push(p6);
+		points.push(p4);
+		points.push(p6);
+		points.push(p2);
+
+		// Bot
+		points.push(p2);
+		points.push(p6);
+		points.push(p5);
+		points.push(p5);
+		points.push(p1);
+		points.push(p2);
+
+		primitive = new h3d.prim.Polygon(points);
+		return primitive;
+	}
+
+	override function inFrustum(frustum : h3d.col.Frustum) {
+		return frustum.hasSphere(s);
+	}
+}

+ 103 - 10
h3d/shader/pbr/Light.hx

@@ -36,8 +36,6 @@ class LightEvaluation extends hxsl.Shader {
 
 			return vec2(falloff, angleFalloff);
 		}
-
-
 	};
 }
 
@@ -90,7 +88,7 @@ class SpotLight extends Light {
 				pbrLightColor *= cookie.rgb * cookie.a;
 			}
 			else
-				pbrLightColor *= fallOffInfoAngle;
+			pbrLightColor *= fallOffInfoAngle;
 		}
 	}
 }
@@ -151,13 +149,6 @@ class CapsuleLight extends Light {
 		@param var halfLength : Float;
 		@param var left : Vec3;
 
-
-		function closestPointOnLine(a : Vec3, b : Vec3 , c : Vec3) : Vec3 {
-			var ab = b - a;
-			var t = dot(c - a, ab) / dot(ab, ab);
-			return a + t * ab ;
-		}
-
 		function closestPointOnSegment( a : Vec3, b : Vec3, c : Vec3) : Vec3 {
 			var ab = b - a;
 			var t = dot(c - a, ab) / dot(ab, ab);
@@ -197,3 +188,105 @@ class CapsuleLight extends Light {
 		}
 	};
 }
+
+class RectangleLight extends Light {
+	static var SRC = {
+		@param var lightDir : Vec3;
+		@param var lightPos : Vec3;
+		@param var p0 : Vec3;
+		@param var p1 : Vec3;
+		@param var p2 : Vec3;
+		@param var p3 : Vec3;
+		@param var width : Float;
+		@param var height : Float;
+		@param var horizontalAngle : Float;
+		@param var verticalAngle : Float;
+		@param var verticalFallOff : Float;
+		@param var horizontalFallOff : Float;
+		@param var range : Float;
+		@param var invLightRange4 : Float; // 1 / range^4
+
+		var view : Vec3;
+		var normal : Vec3;
+
+		function getIntersectionPoint(rayOrigin : Vec3, rayDirection : Vec3, p0 : Vec3, p1 : Vec3, p2 : Vec3) : Vec3 {
+			var planeNormal = cross(p1 - p0, p2 - p1).normalize();
+			return tracePlane(rayOrigin, rayDirection, p0, planeNormal);
+		}
+
+		function closestPointOnSegment( a : Vec3, b : Vec3, c : Vec3) : Vec3 {
+			var ab = b - a;
+			var t = dot(c - a, ab) / dot(ab, ab);
+			return a + saturate(t) * ab;
+		}
+
+		function tracePlane(rayOrigin : Vec3, rayDirection : Vec3, planeOrigin : Vec3, planeNormal : Vec3) : Vec3 {
+			var distanceToPlane = dot(planeNormal, (planeOrigin - rayOrigin) / dot(planeNormal, rayDirection));
+			return rayOrigin + rayDirection * distanceToPlane;
+		}
+
+		function traceTriangle(rayOrigin : Vec3, rayDirection : Vec3, p0 : Vec3, p1 : Vec3, p2 : Vec3) : Bool {
+			var p = getIntersectionPoint(rayOrigin, rayDirection, p0, p1, p2);
+			var n1 = cross(p1 - p0, p - p1).normalize();
+			var n2 = cross(p2 - p1, p - p2).normalize();
+			var n3 = cross(p0 - p2, p - p0).normalize();
+			var d0 = dot(n1, n2);
+			var d1 = dot(n2, n3);
+			return (d0 > 0.1) && (d1 > 0.1);
+		}
+
+		function getClosestPointOnRectangle(rayOrigin : Vec3, rayDirection : Vec3, p0 : Vec3, p1 : Vec3, p2 : Vec3, p3 : Vec3) : Vec3 {
+			var int = getIntersectionPoint(rayOrigin, rayDirection, p0, p1, p3);
+			var closestPoint = vec3(0, 0, 0);
+			if (traceTriangle(rayOrigin, rayDirection, p0, p3, p2)) {
+				closestPoint = getIntersectionPoint(rayOrigin, rayDirection, p0, p3, p2);
+			}
+			else if (traceTriangle(rayOrigin, rayDirection, p0, p1, p3)) {
+				closestPoint = getIntersectionPoint(rayOrigin, rayDirection, p0, p1, p3);
+			}
+			else {
+				var p = int - lightPos;
+				var right = (p1 - p0).normalize();
+				var up = (p2 - p0).normalize();
+				var intRight = clamp(dot(p, right), -width * 0.5, width * 0.5) * right;
+				var intUp = clamp(dot(p, up), -height * 0.5, height * 0.5) * up;
+				closestPoint = lightPos + intRight + intUp;
+			}
+
+			return closestPoint;
+		}
+
+		function fragment() {
+			pbrOcclusionFactor = occlusionFactor;
+
+			var right = (p1 - p0).normalize();
+			var up = (p2 - p0).normalize();
+			var invLightDir = -lightDir;
+
+			// Diffuse
+			var closestPointDiffuse = getClosestPointOnRectangle(transformedPosition, normal, p0, p1, p2, p3);
+			var delta = closestPointDiffuse - transformedPosition;
+			var epsilon = horizontalFallOff - horizontalAngle;
+			pbrLightDirection = normalize(delta);
+
+			var xyLightDir = invLightDir - dot(invLightDir, up) * up;
+			var xyDelta = delta - dot(delta, up) * up;
+			var xyTheta = dot(xyDelta.normalize(), xyLightDir.normalize());
+			var horizontalFalloff = clamp((xyTheta - horizontalAngle) / epsilon, 0.0, 1.0);
+
+			var xzLightDir = invLightDir - dot(invLightDir, right) * right;
+			var xzDelta = delta - dot(delta, right) * right;
+			var xzTheta = dot(xzDelta.normalize(), xzLightDir.normalize());
+			var verticalFalloff = clamp((xzTheta - verticalAngle) / epsilon, 0.0, 1.0);
+
+			var falloff = verticalFalloff * horizontalFalloff * pointLightIntensity(delta, range, invLightRange4);
+			pbrLightColor = falloff * lightColor;
+
+			// Specular
+			var r = reflect(-view, normal);
+			var closestPointSpec = getClosestPointOnRectangle(transformedPosition, r, p0, p1, p2, p3);
+			var delta = normalize(closestPointSpec - transformedPosition);
+			pbrSpecularLightDirection = delta;
+		}
+	}
+}