Browse Source

renamed Irradiance -> Environment
added cubemap from equirect texture

ncannasse 7 years ago
parent
commit
89fda21354
5 changed files with 159 additions and 85 deletions
  1. 12 12
      h3d/mat/PbrMaterialSetup.hx
  2. 2 3
      h3d/scene/Renderer.hx
  3. 95 37
      h3d/scene/pbr/Environment.hx
  4. 32 18
      h3d/scene/pbr/Renderer.hx
  5. 18 15
      samples/Pbr.hx

+ 12 - 12
h3d/mat/PbrMaterialSetup.hx

@@ -22,28 +22,28 @@ private typedef DefaultProps = {
 
 class PbrMaterialSetup extends MaterialSetup {
 
-	public var irrad(get,set) : h3d.scene.pbr.Irradiance;
+	public var env(get,set) : h3d.scene.pbr.Environment;
 
 	public function new(?name="PBR") {
 		super(name);
 	}
 
-	function get_irrad() : h3d.scene.pbr.Irradiance {
-		return @:privateAccess h3d.Engine.getCurrent().resCache.get(h3d.scene.pbr.Irradiance);
+	function get_env() : h3d.scene.pbr.Environment {
+		return @:privateAccess h3d.Engine.getCurrent().resCache.get(h3d.scene.pbr.Environment);
 	}
 
-	function set_irrad( i : h3d.scene.pbr.Irradiance ) {
+	function set_env( i : h3d.scene.pbr.Environment ) {
 		var cache = @:privateAccess h3d.Engine.getCurrent().resCache;
 		if( i == null )
-			cache.remove(h3d.scene.pbr.Irradiance);
+			cache.remove(h3d.scene.pbr.Environment);
 		else
-			cache.set(h3d.scene.pbr.Irradiance, i);
+			cache.set(h3d.scene.pbr.Environment, i);
 		return i;
 	}
 
 	override function createRenderer() : h3d.scene.Renderer {
-		var irrad = irrad;
-		if( irrad == null ) {
+		var env = env;
+		if( env == null ) {
 			var envMap = new h3d.mat.Texture(4,4,[Cube]);
 			var pix = hxd.Pixels.alloc(envMap.width, envMap.height, RGBA);
 			var COLORS = [0xC08080,0xA08080,0x80C080,0x80A080,0x8080C0,0x808080];
@@ -51,11 +51,11 @@ class PbrMaterialSetup extends MaterialSetup {
 				pix.clear(COLORS[i]);
 				envMap.uploadPixels(pix,0,i);
 			}
-			irrad = new h3d.scene.pbr.Irradiance(envMap);
-			irrad.compute();
-			this.irrad = irrad;
+			env = new h3d.scene.pbr.Environment(envMap);
+			env.compute();
+			this.env = env;
 		}
-		return new h3d.scene.pbr.Renderer(irrad);
+		return new h3d.scene.pbr.Renderer(env);
 	}
 
 	override function createLightSystem() {

+ 2 - 3
h3d/scene/Renderer.hx

@@ -88,9 +88,8 @@ class Renderer {
 		ctx.engine.clear(color, depth, stencil);
 	}
 
-	// for legacy purposes
-	inline function allocTarget( name : String, size = 0, depth = true ) {
-		return ctx.textures.allocTarget(name, ctx.engine.width >> size, ctx.engine.height >> size, depth);
+	inline function allocTarget( name : String, size = 0, depth = true, ?format ) {
+		return ctx.textures.allocTarget(name, ctx.engine.width >> size, ctx.engine.height >> size, depth, format);
 	}
 
 	function copy( from, to, ?blend ) {

+ 95 - 37
h3d/scene/pbr/Irradiance.hx → h3d/scene/pbr/Environment.hx

@@ -43,10 +43,11 @@ class IrradShader extends IrradBase {
 
 	static var SRC = {
 
-		@const var face : Int;
+		@param var faceMatrix : Mat3;
 		@param var envMap : SamplerCube;
 
 		@const var isSpecular : Bool;
+		@const var isSRGB : Bool;
 		@param var roughness : Float;
 
 		@param var cubeSize : Float;
@@ -70,19 +71,11 @@ class IrradShader extends IrradBase {
 				// https://seblagarde.wordpress.com/2012/06/10/amd-cubemapgen-for-physically-based-rendering/
 				d += cubeScaleFactor * (d * d * d);
 			}
-			#if hldx
-			d.y *= -1;
-			#end
-			var n : Vec3;
-			switch( face ) {
-			case 0: n = vec3(1, d.y, -d.x);
-			case 1: n = vec3(-1, d.y, d.x);
-			case 2: n = vec3(d.x, 1, -d.y);
-			case 3: n = vec3(d.x, -1, d.y);
-			case 4: n = vec3(d.x, d.y, 1);
-			default: n = vec3(-d.x, d.y,-1);
-			}
-			return n.normalize();
+			return (vec3(d,1.) * faceMatrix).normalize();
+		}
+
+		function gammaCorrect( color : Vec3 ) : Vec3 {
+			return isSRGB ? color : color.pow(vec3(2.2));
 		}
 
 		function fragment() {
@@ -102,11 +95,12 @@ class IrradShader extends IrradBase {
 				}
 				var amount = n.dot(l).saturate();
 				if( amount > 0 ) {
-					color += envMap.get(l).rgb.pow(vec3(2.2)) * amount;
+					color += gammaCorrect(envMap.get(l).rgb) * amount;
 					totalWeight += amount;
 				}
 			}
-			output.color = vec4((color / totalWeight).pow(vec3(1/2.2)), 1.);
+			// store the envMap in linear space (don't revert gamma correction)
+			output.color = vec4(color / totalWeight, 1.);
 		}
 
 	}
@@ -159,7 +153,31 @@ class IrradLut extends IrradBase {
 
 }
 
-class Irradiance  {
+class IrradEquiProj extends h3d.shader.ScreenShader {
+
+	static var SRC = {
+
+		@param var texture : Sampler2D;
+		@param var faceMatrix : Mat3;
+
+		function getNormal() : Vec3 {
+			var d = input.uv * 2. - 1.;
+			return (vec3(d,1.) * faceMatrix).normalize();
+		}
+
+		function fragment() {
+			var n = getNormal();
+			var uv = vec2(atan(n.y, n.x), asin(-n.z));
+    		uv *= vec2(0.1591, 0.3183);
+    		uv += 0.5;
+			pixelColor = texture.get(uv);
+		}
+
+	};
+
+}
+
+class Environment  {
 
 	public var sampleBits : Int;
 	public var diffSize : Int;
@@ -168,20 +186,49 @@ class Irradiance  {
 
 	public var ignoredSpecLevels : Int = 1;
 
-	public var envMap : h3d.mat.Texture;
+	public var source : h3d.mat.Texture;
+	public var env : h3d.mat.Texture;
 	public var lut : h3d.mat.Texture;
 	public var diffuse : h3d.mat.Texture;
 	public var specular : h3d.mat.Texture;
 
 	public var power : Float = 1.;
 
-	public function new(envMap) {
-		this.envMap = envMap;
+	/*
+		Source can be cube map already prepared or a 2D equirectangular map that
+		will be turned into a cube map.
+	*/
+	public function new(src:h3d.mat.Texture) {
+		this.source = src;
+		if( src.flags.has(Loading) )
+			throw "Source is not ready";
+		if( src.flags.has(Cube) ) {
+			this.env = src;
+		} else {
+			if( src.width != src.height*2 )
+				throw "Unrecognized environment map format";
+			env = new h3d.mat.Texture(src.height, src.height, [Cube, Target]);
+			var pass = new h3d.pass.ScreenFx(new IrradEquiProj());
+			var engine = h3d.Engine.getCurrent();
+			pass.shader.texture = src;
+			for( i in 0...6 ) {
+				engine.pushTarget(env,i);
+				pass.shader.faceMatrix = getCubeMatrix(i);
+				pass.render();
+				engine.popTarget();
+			}
+		}
 		diffSize = 64;
 		specSize = 256;
 		sampleBits = 10;
 	}
 
+	public function dispose() {
+		env.dispose();
+		lut.dispose();
+		diffuse.dispose();
+		specular.dispose();
+	}
 
 	public function compute() {
 
@@ -197,6 +244,30 @@ class Irradiance  {
 		computeIrradiance();
 	}
 
+	function getCubeMatrix( face : Int ) {
+		var y = #if hldx -1 #else 1 #end;
+		return h3d.Matrix.L(switch( face ) {
+			case 0: [0,0,-1,0,
+					 0,y,0,0,
+					 1,0,0,0];
+			case 1: [0,0,1,0,
+					 0,y,0,0,
+					-1,0,0,0];
+			case 2: [1,0,0,0,
+					 0,0,-y,0,
+					 0,1,0,0];
+			case 3: [1,0,0,0,
+					 0,0,y,0,
+					 0,-1,0,0];
+			case 4: [1,0,0,0,
+					 0,y,0,0,
+					 0,0,1,0];
+			default: [-1,0,0,0,
+					   0,y,0,0,
+					   0,0,-1,0];
+		});
+	}
+
 	function computeIrradLut() {
 		var screen = new h3d.pass.ScreenFx(new IrradLut());
 		screen.shader.samplesBits = sampleBits;
@@ -208,30 +279,17 @@ class Irradiance  {
 		screen.dispose();
 	}
 
-	function _reversebits( i : Int ) : Int {
-		var r = (i << 16) | (i >>> 16);
-		r = ((r & 0x00ff00ff) << 8) | ((r & 0xff00ff00) >>> 8);
-		r = ((r & 0x0f0f0f0f) << 4) | ((r & 0xf0f0f0f0) >>> 4);
-		r = ((r & 0x33333333) << 2) | ((r & 0xcccccccc) >>> 2);
-		r = ((r & 0x55555555) << 1) | ((r & 0xaaaaaaaa) >>> 1);
-		return r;
-	}
-
-	function hammersley( i : Int, max : Int ) : h3d.Vector {
-		var ri = _reversebits(i) * 2.3283064365386963e-10;
-		return new h3d.Vector(i / max, ri);
-	}
-
 	function computeIrradiance() {
 
 		var screen = new h3d.pass.ScreenFx(new IrradShader());
 		screen.shader.samplesBits = sampleBits;
-		screen.shader.envMap = envMap;
+		screen.shader.envMap = env;
+		screen.shader.isSRGB = env.isSRGB();
 
 		var engine = h3d.Engine.getCurrent();
 
 		for( i in 0...6 ) {
-			screen.shader.face = i;
+			screen.shader.faceMatrix = getCubeMatrix(i);
 			engine.driver.setRenderTarget(diffuse, i);
 			screen.render();
 		}
@@ -245,7 +303,7 @@ class Irradiance  {
 		specLevels = mipLevels - ignoredSpecLevels;
 
 		for( i in 0...6 ) {
-			screen.shader.face = i;
+			screen.shader.faceMatrix = getCubeMatrix(i);
 			for( j in 0...mipLevels ) {
 				var size = specular.width >> j;
 				screen.shader.cubeSize = size;

+ 32 - 18
h3d/scene/pbr/Renderer.hx

@@ -1,8 +1,21 @@
 package h3d.scene.pbr;
 
 enum DisplayMode {
+	/*
+		Full PBR display
+	*/
 	Pbr;
+	/*
+		Set Albedo = 0x808080
+	*/
+	Env;
+	/*
+		Set Albedo = 0x808080, Roughness = 0, Metalness = 1
+	*/
 	MatCap;
+	/*
+		Debug slides
+	*/
 	Slides;
 }
 
@@ -20,7 +33,7 @@ class Renderer extends h3d.scene.Renderer {
 	var pbrProps = new h3d.shader.pbr.PropsImport();
 
 	public var displayMode : DisplayMode = Pbr;
-	public var irrad : Irradiance;
+	public var env : Environment;
 	public var exposure(get,set) : Float;
 
 	var output = new h3d.pass.Output("mrt",[
@@ -29,9 +42,9 @@ class Renderer extends h3d.scene.Renderer {
 		Vec4([Value("output.metalness"), Value("output.roughness"), Value("output.occlusion"), Const(0)]),
 	]);
 
-	public function new(irrad) {
+	public function new(env) {
 		super();
-		this.irrad = irrad;
+		this.env = env;
 		shadows.bias = 0.0;
 		shadows.power = 1000;
 		shadows.blur.passes = 1;
@@ -47,10 +60,6 @@ class Renderer extends h3d.scene.Renderer {
 	inline function get_exposure() return tonemap.shader.exposure;
 	inline function set_exposure(v:Float) return tonemap.shader.exposure = v;
 
-	function allocFTarget( name : String, size = 0, depth = true ) {
-		return ctx.textures.allocTarget(name, ctx.engine.width >> size, ctx.engine.height >> size, depth, RGBA32F);
-	}
-
 	override function debugCompileShader(pass:h3d.mat.Pass) {
 		output.setContext(this.ctx);
 		return output.compileShader(pass);
@@ -79,8 +88,8 @@ class Renderer extends h3d.scene.Renderer {
 
 		shadows.draw(get("shadow"));
 
-		var albedo = allocFTarget("albedo");
-		var normal = allocFTarget("normal",0,false);
+		var albedo = allocTarget("albedo");
+		var normal = allocTarget("normalDepth",0,false,RGBA32F);
 		var pbr = allocTarget("pbr",0,false);
 		setTargets([albedo,normal,pbr]);
 		clear(0, 1);
@@ -89,13 +98,16 @@ class Renderer extends h3d.scene.Renderer {
 		setTarget(albedo);
 		draw("albedo");
 
+		if( displayMode == Env )
+			clear(0xFF404040);
+
 		if( displayMode == MatCap ) {
-			clear(0x808080); // gray albedo
+			clear(0xFFFFFFFF);
 			setTarget(pbr);
-			clear(0xFF00FF); // metal=1, rough=0, occlusion=1
+			clear(0x00D030FF);
 		}
 
-		var output = allocTarget("hdrOutput", 0, true);
+		var output = allocTarget("hdrOutput", 0, true, RGBA16F);
 		setTarget(output);
 		if( ctx.engine.backgroundColor != null )
 			clear(ctx.engine.backgroundColor);
@@ -106,11 +118,11 @@ class Renderer extends h3d.scene.Renderer {
 
 		pbrDirect.cameraPosition.load(ctx.camera.pos);
 		pbrOut.shader.cameraPosition.load(ctx.camera.pos);
-		pbrOut.shader.irrPower = irrad.power;
-		pbrOut.shader.irrLut = irrad.lut;
-		pbrOut.shader.irrDiffuse = irrad.diffuse;
-		pbrOut.shader.irrSpecular = irrad.specular;
-		pbrOut.shader.irrSpecularLevels = irrad.specLevels;
+		pbrOut.shader.irrPower = env.power;
+		pbrOut.shader.irrLut = env.lut;
+		pbrOut.shader.irrDiffuse = env.diffuse;
+		pbrOut.shader.irrSpecular = env.specular;
+		pbrOut.shader.irrSpecularLevels = env.specLevels;
 
 		var ls = getLightSystem();
 		if( ls.shadowLight == null ) {
@@ -129,7 +141,9 @@ class Renderer extends h3d.scene.Renderer {
 		}
 
 		pbrOut.setGlobals(ctx);
+		pbrDirect.doDiscard = false;
 		pbrOut.render();
+		pbrDirect.doDiscard = true;
 
 		var ls = Std.instance(ls, LightSystem);
 		var lpass = screenLightPass;
@@ -157,7 +171,7 @@ class Renderer extends h3d.scene.Renderer {
 
 		switch( displayMode ) {
 
-		case Pbr, MatCap:
+		case Pbr, Env, MatCap:
 			fxaa.apply(ldr);
 
 		case Slides:

+ 18 - 15
samples/Pbr.hx

@@ -7,8 +7,7 @@ class Pbr extends SampleApp {
 	var sphere : h3d.scene.Mesh;
 	var grid : h3d.scene.Object;
 
-	var envMap : h3d.mat.Texture;
-	var irrad : h3d.scene.pbr.Irradiance;
+	var env : h3d.scene.pbr.Environment;
 	var renderer : h3d.scene.pbr.Renderer;
 
 	function new() {
@@ -40,7 +39,7 @@ class Pbr extends SampleApp {
 		fui.verticalSpacing = 5;
 		fui.isVertical = true;
 
-		envMap = new h3d.mat.Texture(512, 512, [Cube]);
+		var envMap = new h3d.mat.Texture(512, 512, [Cube]);
 		envMap.name = "envMap";
 		inline function set(face:Int, res:hxd.res.Image) {
 			var pix = res.getPixels();
@@ -53,6 +52,8 @@ class Pbr extends SampleApp {
 		set(4, hxd.Res.top);
 		set(5, hxd.Res.bottom);
 
+		envMap = hxd.Res.defaultEnv.toTexture();
+
 		var axis = new h3d.scene.Graphics(s3d);
 		axis.lineStyle(2, 0xFF0000);
 		axis.lineTo(2, 0, 0);
@@ -67,22 +68,22 @@ class Pbr extends SampleApp {
 		axis.visible = false;
 		axis.material.mainPass.depthWrite = true;
 
-		irrad = new h3d.scene.pbr.Irradiance(envMap);
-		irrad.compute();
+		env = new h3d.scene.pbr.Environment(envMap);
+		env.compute();
 
 		renderer = cast(s3d.renderer, h3d.scene.pbr.Renderer);
-		renderer.irrad = irrad;
+		renderer.env = env;
 
-		var cubeShader = bg.material.mainPass.addShader(new h3d.shader.pbr.CubeLod(envMap));
+		var cubeShader = bg.material.mainPass.addShader(new h3d.shader.pbr.CubeLod(env.env));
 		var light = new h3d.scene.pbr.PointLight(s3d);
 		light.setPos(30, 10, 40);
 		light.range = 100;
 		light.power = 2;
 
 		var pbrValues = new h3d.shader.pbr.PropsValues(0.2,0.5,0);
-		hue = 0.2;
-		saturation = 0.2;
-		brightness = 0;
+		hue = 0;
+		saturation = 0;
+		brightness = 0.2;
 
 		function addSphere(x,y) {
 			var sphere = new h3d.scene.Mesh(sp, s3d);
@@ -95,21 +96,21 @@ class Pbr extends SampleApp {
 		sphere.material.mainPass.addShader(pbrValues);
 
 		grid = new h3d.scene.Object(s3d);
-		var max = 5;
+		var max = 8;
 		for( x in 0...max )
 			for( y in 0...max ) {
 				var s = addSphere(x - (max - 1) * 0.5, y - (max - 1) * 0.5);
 				grid.addChild(s);
 				s.scale(0.4);
-				s.material.mainPass.addShader(new h3d.shader.pbr.PropsValues( Math.pow(x / (max - 1), 1), Math.pow(y / (max - 1), 1)));
+				s.material.mainPass.addShader(new h3d.shader.pbr.PropsValues( 1 - x / (max - 1), 1 - y / (max - 1) ));
 			}
 		grid.visible = false;
 
 		// camera
 		addSlider("Exposure", function() return renderer.exposure, function(v) renderer.exposure = v, -3, 3);
+		addSlider("Sky", function() return env.power, function(v) env.power = v, 0, 3);
 		addSlider("Light", function() return light.power, function(v) light.power = v, 0, 10);
 
-
 		// material color
 		color = new h2d.Bitmap(h2d.Tile.fromColor(0xFFFFFF, 30, 30), fui);
 		fui.getProperties(color).paddingLeft = 150;
@@ -136,8 +137,10 @@ class Pbr extends SampleApp {
 		var prims : Array<h3d.prim.Primitive> = [sp, cube];
 		addChoice("Prim", ["Sphere","Cube"], function(i) sphere.primitive = prims[i], prims.indexOf(sphere.primitive));
 
-		addChoice("EnvMap", ["Default", "IDiff", "ISpec"], function(i) cubeShader.texture = [envMap, irrad.diffuse, irrad.specular][i]);
-		addSlider("EnvLod", function() return cubeShader.lod, function(v) cubeShader.lod = v, 0, irrad.specLevels);
+		addChoice("EnvMap", ["Default", "IDiff", "ISpec"], function(i) {
+			cubeShader.texture = [env.env, env.diffuse, env.specular][i];
+		});
+		addSlider("EnvLod", function() return cubeShader.lod, function(v) cubeShader.lod = v, 0, env.specLevels);
 
 	}