ソースを参照

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;
 		@param var irrPower : Float;
 
 
 		function fragment() {
 		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 envBRDF = irrLut.get(vec2(roughness, NdV));
-			var specular = envSpec * (specularColor * envBRDF.x + envBRDF.y);
+			var specular = envSpec * (F * envBRDF.x + envBRDF.y);
 			/*
 			/*
 				// diffuse *= occlusion
 				// diffuse *= occlusion
 				Usually indirect diffuse is multiplied by occlusion, but since our occlusion mosly
 				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.
 				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;
 			pixelColor.rgb += indirect;
 		}
 		}
 
 
@@ -37,10 +42,8 @@ class Direct extends PropsDefinition {
 		@const var doDiscard : Bool = true;
 		@const var doDiscard : Bool = true;
 
 
 		function fragment() {
 		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 half = (pbrLightDirection + view).normalize();
 				var NdH = normal.dot(half).max(0.);
 				var NdH = normal.dot(half).max(0.);
@@ -51,7 +54,7 @@ class Direct extends PropsDefinition {
 
 
 				// ------------- DIRECT LIGHT -------------------------
 				// ------------- DIRECT LIGHT -------------------------
 
 
-				var F0 = metalness;
+				var F0 = pbrSpecularColor;
 				var diffuse = albedo * NdL / PI;
 				var diffuse = albedo * NdL / PI;
 
 
 				// General Cook-Torrance formula for microfacet BRDF
 				// General Cook-Torrance formula for microfacet BRDF
@@ -70,14 +73,18 @@ class Direct extends PropsDefinition {
 				// Schlick approx
 				// Schlick approx
 				// pow 5 optimized with Spherical Gaussian
 				// pow 5 optimized with Spherical Gaussian
 				// var F = F0 + (1 - F0) * pow(1 - v.dot(h).min(1.), 5.);
 				// 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
 				// G = geometric attenuation
 				// Schlick (modified for UE4 with k=alpha/2)
 				// 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;
 				direct *= occlusion;
 				pixelColor.rgb += direct;
 				pixelColor.rgb += direct;
 			} else if( doDiscard )
 			} else if( doDiscard )

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

@@ -9,7 +9,7 @@ class PropsDefinition extends hxsl.Shader {
 		var metalness : Float;
 		var metalness : Float;
 		var roughness : Float;
 		var roughness : Float;
 		var occlusion : Float;
 		var occlusion : Float;
-		var specularColor : Vec3;
+		var pbrSpecularColor : Vec3;
 		var transformedPosition : Vec3;
 		var transformedPosition : Vec3;
 
 
 		var view : 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 occlusion : Float;
 		var calculatedUV : Vec2;
 		var calculatedUV : Vec2;
 		var transformedPosition : Vec3;
 		var transformedPosition : Vec3;
-		var specularColor : Vec3;
+		var pbrSpecularColor : Vec3;
 		var screenUV : Vec2; // BaseMesh
 		var screenUV : Vec2; // BaseMesh
 
 
 		function fragment() {
 		function fragment() {
 			var uv = isScreen ? calculatedUV : screenUV;
 			var uv = isScreen ? calculatedUV : screenUV;
 			albedo = albedoTex.get(uv).rgb;
 			albedo = albedoTex.get(uv).rgb;
+			albedo *= albedo; // gamma correct
+
 			var normalDepth = normalTex.get(uv);
 			var normalDepth = normalTex.get(uv);
 			normal = normalDepth.xyz;
 			normal = normalDepth.xyz;
 			depth = normalDepth.w;
 			depth = normalDepth.w;
@@ -32,10 +34,7 @@ class PropsImport extends hxsl.Shader {
 			roughness = pbr.g;
 			roughness = pbr.g;
 			occlusion = pbr.b;
 			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
 			// this is the original object transformed position, not our current drawing object one
 			var uv2 = (uv - 0.5) * vec2(2, -2);
 			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() {
 		function fragment() {
 			var v = texture.get(calculatedUV);
 			var v = texture.get(calculatedUV);
 			output.metalness = v.r;
 			output.metalness = v.r;
-			output.roughness = 1 - v.g * v.g;
+			output.roughness = 1 - v.g;
 			output.occlusion = v.b;
 			output.occlusion = v.b;
 		}
 		}
 	}
 	}

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

@@ -6,21 +6,34 @@ class ToneMapping extends ScreenShader {
 	static var SRC = {
 	static var SRC = {
 
 
 		@param var hdrTexture : Sampler2D;
 		@param var hdrTexture : Sampler2D;
-		@param var exposure : Float;
+		@param var exposureExp : Float;
+		@const var isSRBG : Bool;
 
 
 		function fragment() {
 		function fragment() {
 			var color = hdrTexture.get(calculatedUV);
 			var color = hdrTexture.get(calculatedUV);
 
 
 			// reinhard tonemapping
 			// reinhard tonemapping
-			color.rgb *= exp(exposure);
+			color.rgb *= exposureExp;
 			color.rgb = color.rgb / (color.rgb + vec3(1.));
 			color.rgb = color.rgb / (color.rgb + vec3(1.));
 
 
 			// gamma correct
 			// gamma correct
-			color.rgb = color.rgb.pow(vec3(1 / 2.2));
+			if( !isSRBG ) color.rgb = color.rgb.sqrt();
 
 
 			pixelColor = color;
 			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;
+	}
+
 
 
 }
 }