浏览代码

fixed PBR rendering and gamma correction

ncannasse 7 年之前
父节点
当前提交
94862adaf4

+ 21 - 14
h3d/shader/pbr/Lighting.hx

@@ -11,16 +11,21 @@ class Indirect extends PropsDefinition {
 		@param var irrPower : Float;
 
 		function fragment() {
-			var diffuse = irrDiffuse.get(normal).rgb.pow(vec3(2.2)) * albedo;
-			var envSpec = textureCubeLod(irrSpecular, reflect(-view,normal), roughness * irrSpecularLevels).rgb.pow(vec3(2.2));
+			if( normal.dot(normal) <= 0 ) discard;
+
+			var F0 = pbrSpecularColor;
+			var F = F0 + (max(vec3(1 - roughness), F0) - F0) * exp2( ( -5.55473 * NdV - 6.98316) * NdV );
+
+			var diffuse = irrDiffuse.get(normal).rgb * albedo;
+			var envSpec = textureCubeLod(irrSpecular, reflect(-view,normal), roughness * irrSpecularLevels).rgb;
 			var envBRDF = irrLut.get(vec2(roughness, NdV));
-			var specular = envSpec * (specularColor * envBRDF.x + envBRDF.y);
+			var specular = envSpec * (F * envBRDF.x + envBRDF.y);
 			/*
 				// diffuse *= occlusion
 				Usually indirect diffuse is multiplied by occlusion, but since our occlusion mosly
 				comes from shadow map, we want to keep the diffuse term for colored shadows here.
 			*/
-			var indirect = (diffuse + specular) * irrPower;
+			var indirect = (diffuse * (1 - metalness) * (1. - F) + specular) * irrPower;
 			pixelColor.rgb += indirect;
 		}
 
@@ -37,10 +42,8 @@ class Direct extends PropsDefinition {
 		@const var doDiscard : Bool = true;
 
 		function fragment() {
-			if( pbrLightColor.dot(pbrLightColor) > 0.0001 ) {
-				var NdL = normal.dot(pbrLightDirection).max(0.);
-
-				if( NdL <= 0 && doDiscard ) discard;
+			var NdL = normal.dot(pbrLightDirection).max(0.);
+			if( pbrLightColor.dot(pbrLightColor) > 0.0001 && NdL > 0 ) {
 
 				var half = (pbrLightDirection + view).normalize();
 				var NdH = normal.dot(half).max(0.);
@@ -51,7 +54,7 @@ class Direct extends PropsDefinition {
 
 				// ------------- DIRECT LIGHT -------------------------
 
-				var F0 = metalness;
+				var F0 = pbrSpecularColor;
 				var diffuse = albedo * NdL / PI;
 
 				// General Cook-Torrance formula for microfacet BRDF
@@ -70,14 +73,18 @@ class Direct extends PropsDefinition {
 				// Schlick approx
 				// pow 5 optimized with Spherical Gaussian
 				// var F = F0 + (1 - F0) * pow(1 - v.dot(h).min(1.), 5.);
-				var F = F0 + (1.01 - F0) * exp2( ( -5.55473 * VdH - 6.98316) * VdH );
+				var F = F0 + (1. - F0) * exp2( ( -5.55473 * VdH - 6.98316) * VdH );
 
 				// G = geometric attenuation
 				// Schlick (modified for UE4 with k=alpha/2)
-				var G = calcG(pbrLightDirection) * calcG(view);
-
-				var specular = if( NdL > 0.1 && NdV > 0.1 ) (D * F * G / (4 * NdL * NdV)).max(0.) else 0.;
-				direct += (diffuse + specular) * pbrLightColor;
+				// k = (rough + 1)² / 8
+				var k = (roughness + 1);
+				k *= k;
+				k *= 0.125;
+				var G = (NdV / (NdV * (1 - k) + k)) * (NdL / (NdL * (1 - k) + k));
+
+				var specular = (D * F * G / (4 * NdL * NdV)).max(0.);
+				direct += mix(diffuse * (1 - metalness), specular, F) * pbrLightColor;
 				direct *= occlusion;
 				pixelColor.rgb += direct;
 			} else if( doDiscard )

+ 1 - 7
h3d/shader/pbr/PropsDefinition.hx

@@ -9,7 +9,7 @@ class PropsDefinition extends hxsl.Shader {
 		var metalness : Float;
 		var roughness : Float;
 		var occlusion : Float;
-		var specularColor : Vec3;
+		var pbrSpecularColor : Vec3;
 		var transformedPosition : Vec3;
 
 		var view : Vec3;
@@ -26,12 +26,6 @@ class PropsDefinition extends hxsl.Shader {
 			}
 		}
 
-		function calcG(v:Vec3) : Float {
-			var k = (roughness + 1);
-			k *= k;
-			return NdV / (NdV * (1 - k) + k);
-		}
-
 	}
 
 }

+ 4 - 5
h3d/shader/pbr/PropsImport.hx

@@ -18,12 +18,14 @@ class PropsImport extends hxsl.Shader {
 		var occlusion : Float;
 		var calculatedUV : Vec2;
 		var transformedPosition : Vec3;
-		var specularColor : Vec3;
+		var pbrSpecularColor : Vec3;
 		var screenUV : Vec2; // BaseMesh
 
 		function fragment() {
 			var uv = isScreen ? calculatedUV : screenUV;
 			albedo = albedoTex.get(uv).rgb;
+			albedo *= albedo; // gamma correct
+
 			var normalDepth = normalTex.get(uv);
 			normal = normalDepth.xyz;
 			depth = normalDepth.w;
@@ -32,10 +34,7 @@ class PropsImport extends hxsl.Shader {
 			roughness = pbr.g;
 			occlusion = pbr.b;
 
-			// specular is calculated in two ways:
-			//    - for metalic surfaces, we use the albedo for reflection
-			//    - for other surfaces, we use the glossiness as a gray input value
-			specularColor = metalness < 0.3 ? vec3(1 - roughness) : albedo;
+			pbrSpecularColor = mix(vec3(0.04),albedo,metalness);
 
 			// this is the original object transformed position, not our current drawing object one
 			var uv2 = (uv - 0.5) * vec2(2, -2);

+ 1 - 1
h3d/shader/pbr/PropsTexture.hx

@@ -12,7 +12,7 @@ class PropsTexture extends hxsl.Shader {
 		function fragment() {
 			var v = texture.get(calculatedUV);
 			output.metalness = v.r;
-			output.roughness = 1 - v.g * v.g;
+			output.roughness = 1 - v.g;
 			output.occlusion = v.b;
 		}
 	}

+ 16 - 3
h3d/shader/pbr/ToneMapping.hx

@@ -6,21 +6,34 @@ class ToneMapping extends ScreenShader {
 	static var SRC = {
 
 		@param var hdrTexture : Sampler2D;
-		@param var exposure : Float;
+		@param var exposureExp : Float;
+		@const var isSRBG : Bool;
 
 		function fragment() {
 			var color = hdrTexture.get(calculatedUV);
 
 			// reinhard tonemapping
-			color.rgb *= exp(exposure);
+			color.rgb *= exposureExp;
 			color.rgb = color.rgb / (color.rgb + vec3(1.));
 
 			// gamma correct
-			color.rgb = color.rgb.pow(vec3(1 / 2.2));
+			if( !isSRBG ) color.rgb = color.rgb.sqrt();
 
 			pixelColor = color;
 		}
 	}
 
+	public var exposure(default,set) : Float;
+
+	public function new() {
+		super();
+		exposure = 0;
+	}
+
+	function set_exposure(v) {
+		exposureExp = Math.exp(v);
+		return exposure = v;
+	}
+
 
 }