소스 검색

Add SpotLight
Add MainLight

ShiroSmith 7 년 전
부모
커밋
43f7ef3c6c

+ 2 - 3
h3d/pass/DirShadowMap.hx

@@ -2,7 +2,6 @@ package h3d.pass;
 
 class DirShadowMap extends Shadows {
 
-	var lightCamera : h3d.Camera;
 	var customDepth : Bool;
 	var depth : h3d.mat.DepthBuffer;
 	var dshader : h3d.shader.DirShadow;
@@ -37,8 +36,8 @@ class DirShadowMap extends Shadows {
 		if( customDepth && depth != null ) depth.dispose();
 	}
 
-	function getShadowProj() {
-		return lightCamera.m;
+	public override function getShadowTex() {
+		return dshader.shadowMap;
 	}
 
 	public dynamic function calcShadowBounds( camera : h3d.Camera ) {

+ 2 - 3
h3d/pass/PointShadowMap.hx

@@ -2,7 +2,6 @@ package h3d.pass;
 
 class PointShadowMap extends Shadows {
 
-	var lightCamera : h3d.Camera;
 	var customDepth : Bool;
 	var depth : h3d.mat.DepthBuffer;
 	var pshader : h3d.shader.PointShadow;
@@ -43,8 +42,8 @@ class PointShadowMap extends Shadows {
 		return true;
 	}
 
-	function getShadowProj() {
-		return lightCamera.m;
+	override function getShadowTex() {
+		return pshader.shadowMap;
 	}
 
 	override function setGlobals() {

+ 9 - 0
h3d/pass/Shadows.hx

@@ -9,6 +9,7 @@ enum RenderMode {
 
 class Shadows extends Default {
 
+	var lightCamera : h3d.Camera;
 	var format : hxd.PixelFormat;
 	var staticTexture : h3d.mat.Texture;
 	var light : h3d.scene.Light;
@@ -50,6 +51,14 @@ class Shadows extends Default {
 		if( staticTexture != null ) staticTexture.dispose();
 	}
 
+	public function getShadowProj() {
+		return lightCamera.m;
+	}
+
+	public function getShadowTex() {
+		return null;
+	}
+
 	function isUsingWorldDist(){
 		return false;
 	}

+ 159 - 0
h3d/pass/SpotShadowMap.hx

@@ -0,0 +1,159 @@
+package h3d.pass;
+
+class SpotShadowMap extends Shadows {
+
+	var customDepth : Bool;
+	var depth : h3d.mat.DepthBuffer;
+	var sshader : h3d.shader.SpotShadow;
+	var border : Border;
+	var mergePass = new h3d.pass.ScreenFx(new h3d.shader.MinMaxShader());
+
+	public function new( light : h3d.scene.Light ) {
+		super(light);
+		lightCamera = new h3d.Camera();
+		lightCamera.screenRatio = 1.0;
+		shader = sshader = new h3d.shader.SpotShadow();
+		border = new Border(size, size);
+		customDepth = h3d.Engine.getCurrent().driver.hasFeature(AllocDepthBuffer);
+		if( !customDepth ) depth = h3d.mat.DepthBuffer.getDefault();
+	}
+
+	override function set_mode(m:Shadows.RenderMode) {
+		sshader.enable = m != None;
+		return mode = m;
+	}
+
+	override function set_size(s) {
+		if( border != null && size != s ) {
+			border.dispose();
+			border = new Border(s, s);
+		}
+		return super.set_size(s);
+	}
+
+	override function isUsingWorldDist(){
+		return false;
+	}
+
+	override function dispose() {
+		super.dispose();
+		if( customDepth && depth != null ) depth.dispose();
+	}
+
+	public override function getShadowTex() {
+		return sshader.shadowMap;
+	}
+
+	override function setGlobals() {
+		super.setGlobals();
+		cameraViewProj = getShadowProj();
+	}
+
+	function syncShader(texture) {
+		sshader.shadowMap = texture;
+		sshader.shadowMapChannel = format == h3d.mat.Texture.nativeFormat ? PackedFloat : R;
+		sshader.shadowBias = bias;
+		sshader.shadowPower = power;
+		sshader.shadowProj = getShadowProj();
+	}
+
+	override function saveStaticData() {
+		if( mode != Mixed && mode != Static )
+			return null;
+		if( staticTexture == null )
+			throw "Data not computed";
+		var bytes = haxe.zip.Compress.run(staticTexture.capturePixels().bytes,9);
+		var buffer = new haxe.io.BytesBuffer();
+		buffer.addInt32(staticTexture.width);
+		buffer.addInt32(bytes.length);
+		buffer.add(bytes);
+		return buffer.getBytes();
+	}
+
+	override function loadStaticData( bytes : haxe.io.Bytes ) {
+		if( (mode != Mixed && mode != Static) || bytes == null )
+			return false;
+		var buffer = new haxe.io.BytesInput(bytes);
+		var size = buffer.readInt32();
+		if( size != this.size )
+			return false;
+
+		var len = buffer.readInt32();
+		var pixels = new hxd.Pixels(size, size, haxe.zip.Uncompress.run(buffer.read(len)), format);
+		if( staticTexture != null ) staticTexture.dispose();
+		staticTexture = new h3d.mat.Texture(size, size, [Target], format);
+		staticTexture.uploadPixels(pixels);
+		syncShader(staticTexture);
+		return true;
+	}
+
+	override function draw( passes ) {
+
+		if( !ctx.computingStatic )
+			switch( mode ) {
+			case None:
+				return passes;
+			case Dynamic:
+				// nothing
+			case Static, Mixed:
+				if( staticTexture == null || staticTexture.isDisposed() )
+					staticTexture = h3d.mat.Texture.fromColor(0xFFFFFF);
+				if( mode == Static ) {
+					syncShader(staticTexture);
+					return passes;
+				}
+			}
+
+		passes = filterPasses(passes);
+
+		var texture = ctx.textures.allocTarget("shadowMap", size, size, false, format);
+		if( customDepth && (depth == null || depth.width != size || depth.height != size || depth.isDisposed()) ) {
+			if( depth != null ) depth.dispose();
+			depth = new h3d.mat.DepthBuffer(size, size);
+		}
+		texture.depthBuffer = depth;
+
+		if( mode != Mixed || ctx.computingStatic ) {
+			var absPos = light.getAbsPos();
+			var spotLight = cast(light, h3d.scene.pbr.SpotLight);
+			var ldir = absPos.front();
+			lightCamera.pos.set(absPos.tx, absPos.ty, absPos.tz);
+			lightCamera.target.set(absPos.tx + ldir.x, absPos.ty + ldir.y, absPos.tz + ldir.z);
+			lightCamera.fovY = spotLight.angle * 2.0;
+			lightCamera.zFar = spotLight.maxRange;
+			lightCamera.update();
+		}
+
+		ctx.engine.pushTarget(texture);
+		ctx.engine.clear(0xFFFFFF, 1);
+		passes = super.draw(passes);
+		if( border != null ) border.render();
+		ctx.engine.popTarget();
+
+		if( mode == Mixed && !ctx.computingStatic ) {
+			var merge = ctx.textures.allocTarget("shadowMap", size, size, false, format);
+			mergePass.shader.texA = texture;
+			mergePass.shader.texB = staticTexture;
+			ctx.engine.pushTarget(merge);
+			mergePass.render();
+			ctx.engine.popTarget();
+			texture = merge;
+		}
+
+		if( blur.radius > 0 && (mode != Mixed || !ctx.computingStatic) )
+			blur.apply(ctx, texture);
+
+		syncShader(texture);
+		return passes;
+	}
+
+	override function computeStatic( passes : h3d.pass.Object ) {
+		if( mode != Static && mode != Mixed )
+			return;
+		draw(passes);
+		var texture = sshader.shadowMap;
+		if( staticTexture != null ) staticTexture.dispose();
+		staticTexture = texture.clone();
+		sshader.shadowMap = staticTexture;
+	}
+}

+ 39 - 0
h3d/scene/SpotLight.hx

@@ -0,0 +1,39 @@
+package h3d.scene;
+
+class SpotLight extends Light {
+
+	var dshader : h3d.shader.SpotLight;
+
+	public function new(?dir: h3d.Vector, ?parent) {
+		dshader = new h3d.shader.SpotLight();
+		super(dshader, parent);
+		priority = 100;
+		if( dir != null ) setDirection(dir);
+	}
+
+	override function get_color() {
+		return dshader.color;
+	}
+
+	override function set_color(v) {
+		return dshader.color = v;
+	}
+
+	override function get_enableSpecular() {
+		return dshader.enableSpecular;
+	}
+
+	override function set_enableSpecular(b) {
+		return dshader.enableSpecular = b;
+	}
+
+	override function getShadowDirection() : h3d.Vector {
+		return absPos.front();
+	}
+
+	override function emit(ctx) {
+		dshader.direction.load(absPos.front());
+		dshader.direction.normalize();
+		super.emit(ctx);
+	}
+}

+ 13 - 0
h3d/scene/pbr/Light.hx

@@ -13,6 +13,7 @@ class Light extends h3d.scene.Light {
 	var primitive : h3d.prim.Primitive;
 	@:s public var power : Float = 1.;
 	public var shadows : h3d.pass.Shadows;
+	public var isMainLight = false;
 
 	function new(shader,?parent) {
 		super(shader,parent);
@@ -20,6 +21,18 @@ class Light extends h3d.scene.Light {
 		if( shadows == null ) shadows = new h3d.pass.Shadows(this);
 	}
 
+	override function sync(ctx) {
+		super.sync(ctx);
+		if(isMainLight){
+			ctx.setGlobal("mainLightColor", _color);
+			ctx.setGlobal("mainLightPower", power);
+			ctx.setGlobal("mainLightPos",new h3d.Vector(absPos.tx, absPos.ty, absPos.tz));
+			ctx.setGlobal("mainLightDir", absPos.front());
+			ctx.setGlobal("mainLightShadowMap", shadows.getShadowTex());
+			ctx.setGlobal("mainLightViewProj", shadows.getShadowProj());
+		}
+	}
+
 	override function get_color() {
 		return _color;
 	}

+ 59 - 0
h3d/scene/pbr/SpotLight.hx

@@ -0,0 +1,59 @@
+package h3d.scene.pbr;
+
+class SpotLight extends Light {
+
+	var pbr : h3d.shader.pbr.Light.SpotLight;
+
+	public var range : Float;
+	public var maxRange(get,set) : Float;
+	public var angle : Float;
+	public var fallOff : Float;
+
+
+	public function new(?parent) {
+		pbr = new h3d.shader.pbr.Light.SpotLight();
+		shadows = new h3d.pass.SpotShadowMap(this);
+		super(pbr,parent);
+		range = 10;
+		primitive = h3d.prim.Sphere.defaultUnitSphere();
+	}
+
+	function get_maxRange() {
+		return cullingDistance;
+	}
+
+	function set_maxRange(v:Float) {
+		setScale(v);
+		return cullingDistance = v;
+	}
+
+	override function getShadowDirection() : h3d.Vector {
+		return absPos.front();
+	}
+
+	override function draw(ctx) {
+		primitive.render(ctx.engine);
+	}
+
+	override function sync(ctx) {
+		super.sync(ctx);
+
+		pbr.lightColor.load(_color);
+		var power = power;
+		pbr.lightColor.scale3(power * power);
+		pbr.lightPos.set(absPos.tx, absPos.ty, absPos.tz);
+		pbr.spotDir.load(absPos.front());
+		pbr.angle = hxd.Math.cos(hxd.Math.degToRad(angle));
+		pbr.fallOff = hxd.Math.cos(hxd.Math.degToRad(hxd.Math.min(angle, fallOff)));
+		pbr.range = hxd.Math.min(range, maxRange);
+		pbr.invLightRange4 = 1 / (maxRange * maxRange * maxRange * maxRange);
+	}
+
+	override function emit(ctx:RenderContext) {
+		if( ctx.pbrLightPass == null )
+			throw "Rendering a pbr light require a PBR compatible scene renderer";
+
+		super.emit(ctx);
+		ctx.emitPass(ctx.pbrLightPass, this);
+	}
+}

+ 1 - 1
h3d/shader/PointShadow.hx

@@ -18,7 +18,7 @@ class PointShadow extends hxsl.Shader {
 		function fragment() {
 			if( enable ) {
 				var posToLight = transformedPosition.xyz - lightPos;
-				var dir = normalize(vec3(posToLight.x, posToLight.y, posToLight.z));
+				var dir = normalize(posToLight.xyz);
 				var depth = shadowMap.get(dir).r * zFar;
 				var zMax = length(posToLight);
 				var delta = (depth + shadowBias).min(zMax) - zMax;

+ 44 - 0
h3d/shader/SpotLight.hx

@@ -0,0 +1,44 @@
+package h3d.shader;
+
+class SpotLight extends hxsl.Shader {
+
+	static var SRC = {
+		@param var color : Vec3;
+		@param var direction : Vec3;
+		@const var enableSpecular : Bool;
+		@global var camera : {
+			var position : Vec3;
+		};
+
+		var lightColor : Vec3;
+		var lightPixelColor : Vec3;
+		var transformedNormal : Vec3;
+		var transformedPosition : Vec3;
+		var specPower : Float;
+		var specColor : Vec3;
+
+		function calcLighting() : Vec3 {
+			var diff = transformedNormal.dot(-direction).max(0.);
+			if( !enableSpecular )
+				return color * diff;
+			var r = reflect(direction, transformedNormal).normalize();
+			var specValue = r.dot((camera.position - transformedPosition).normalize()).max(0.);
+			return color * (diff + specColor * pow(specValue, specPower));
+		}
+
+		function vertex() {
+			lightColor.rgb += calcLighting();
+		}
+
+		function fragment() {
+			lightPixelColor.rgb += calcLighting();
+		}
+
+	}
+
+	public function new() {
+		super();
+		color.set(1, 1, 1);
+	}
+
+}

+ 28 - 0
h3d/shader/SpotShadow.hx

@@ -0,0 +1,28 @@
+package h3d.shader;
+
+class SpotShadow extends hxsl.Shader {
+
+	static var SRC = {
+
+		@const var enable : Bool;
+
+		@param var shadowMap : Channel;
+		@param var shadowProj : Mat4;
+		@param var shadowPower : Float;
+		@param var shadowBias : Float;
+
+		var transformedPosition : Vec3;
+		var shadow : Float;
+
+		function fragment() {
+			if( enable ) {
+				var shadowPos = vec4(transformedPosition, 1.0) * shadowProj;
+				var shadowScreenPos = shadowPos.xyz / shadowPos.w;
+				var depth = shadowMap.get(screenToUv(shadowScreenPos.xy));
+				var zMax = shadowScreenPos.z.saturate();
+				var delta = (depth + shadowBias).min(zMax) - zMax;
+				shadow = exp( shadowPower * delta ).saturate();
+			}
+		}
+	}
+}

+ 38 - 5
h3d/shader/pbr/Light.hx

@@ -1,7 +1,9 @@
 package h3d.shader.pbr;
 
 class Light extends hxsl.Shader {
+
 	static var SRC = {
+
 		var pbrLightDirection : Vec3;
 		var pbrLightColor : Vec3;
 		var transformedPosition : Vec3;
@@ -12,6 +14,42 @@ class Light extends hxsl.Shader {
 	};
 }
 
+class SpotLight extends Light {
+
+	static var SRC = {
+
+		@param var spotDir : Vec3;
+		@param var lightPos : Vec3;
+		@param var angle : Float;
+		@param var fallOff : Float;
+		@param var invLightRange4 : Float; // 1 / range^4
+		@param var range : Float;
+
+		function fragment() {
+			pbrLightDirection = normalize(lightPos - transformedPosition);
+
+			var theta = dot(pbrLightDirection, -spotDir);
+			var epsilon = fallOff - angle;
+			var intensity = clamp((theta - angle) / epsilon, 0.0, 1.0);
+
+			var delta = lightPos - transformedPosition;
+			/*
+				UE4 [Karis12] "Real Shading in Unreal Engine 4"
+				Modified with pointSize
+			*/
+			var dist = delta.dot(delta);
+			var falloff = saturate(1 - dist*dist * invLightRange4);
+			if( range > 0 ) {
+				dist = (dist.sqrt() - range).max(0.);
+				dist *= dist;
+			}
+			falloff *= falloff;
+			falloff *= 1 / (dist + 1);
+
+			pbrLightColor = lightColor * intensity * falloff;
+		}
+	}
+}
 
 class PointLight extends Light {
 
@@ -38,12 +76,9 @@ class PointLight extends Light {
 			falloff *= 1 / (dist + 1);
 			pbrLightColor = lightColor * falloff;
 		}
-
 	};
-
 }
 
-
 class DirLight extends Light {
 
 	static var SRC = {
@@ -54,7 +89,5 @@ class DirLight extends Light {
 			pbrLightDirection = lightDir;
 			pbrLightColor = lightColor;
 		}
-
 	};
-
 }