فهرست منبع

More tweaks to cubemap importance sampling

BearishSun 8 سال پیش
والد
کامیت
d516e39cc3

+ 13 - 6
Data/Raw/Engine/Includes/ReflectionCubemapCommon.bslinc

@@ -35,13 +35,20 @@ Technique : base("ReflectionCubemapCommon") =
 			float mapRoughnessToMipLevel(float roughness, int numMips)
 			float mapRoughnessToMipLevel(float roughness, int numMips)
 			{
 			{
 				// We use the following equation:
 				// We use the following equation:
-				//    mipLevel = log10(roughness) / log10(dropPercent)
+				//    mipLevel = log10(1 - roughness) / log10(dropPercent)
 				//
 				//
 				// Where dropPercent represent by what % to drop the roughness with each mip level.
 				// Where dropPercent represent by what % to drop the roughness with each mip level.
-				// We convert to log2 and a assume a drop percent value of 0.6. This gives us:
-				//    mipLevel = -1.35692 * log2( roughness);
-
-				return max(0, (float)numMips - 1.0f - (-1.35692f * log2(roughness)));
+				// We convert to log2 and a assume a drop percent value of 0.7. This gives us:
+				//    mipLevel = -2.8 * log2(1 - roughness);
+				
+				// Note: Another value that could be used is drop 0.6, which yields a multiply by -1.35692. 
+				// This more accurately covers the mip range, but early mip levels end up being too smooth,
+				// and benefits from our cubemap importance sampling strategy seem to be lost as most samples
+				// fall within one pixel, resulting in same effect as just trivially downsampling. With 0.7 drop
+				// the roughness increases too early and higher mip levels don't cover the full [0, 1] range. Which
+				// is better depends on what looks better.
+				
+				return max(0, -2.8f * log2(1.0f - roughness));
 			}
 			}
 			
 			
 			/**
 			/**
@@ -54,7 +61,7 @@ Technique : base("ReflectionCubemapCommon") =
 			float mapMipLevelToRoughness(int mipLevel, int numMips)
 			float mapMipLevelToRoughness(int mipLevel, int numMips)
 			{
 			{
 				// mapRoughnessToMipLevel() solved for roughness
 				// mapRoughnessToMipLevel() solved for roughness
-				return saturate(exp2(((float)mipLevel - (float)numMips + 1.0f) / 1.35692));
+				return 1 - exp2((float)mipLevel / -2.8f);
 			}	
 			}	
 		};
 		};
 	};
 	};

+ 26 - 7
Data/Raw/Engine/Includes/ReflectionCubemapSampling.bslinc

@@ -23,7 +23,7 @@ Technique : base("ReflectionCubemapSampling") =
 			TextureCube gSkyCubemapTex;
 			TextureCube gSkyCubemapTex;
 			SamplerState gSkyCubemapSamp;
 			SamplerState gSkyCubemapSamp;
 			
 			
-			TextureCubeArray gReflProbeCubmaps;
+			TextureCubeArray gReflProbeCubemaps;
 			SamplerState gReflProbeSamp;
 			SamplerState gReflProbeSamp;
 			
 			
 			Texture2D gPreintegratedEnvBRDF;
 			Texture2D gPreintegratedEnvBRDF;
@@ -171,10 +171,10 @@ Technique : base("ReflectionCubemapSampling") =
 							correctedDir = getLookupForBoxProxy(worldPos, dir, probeData.position, probeData.boxExtents, probeData.invBoxTransform, probeData.transitionDistance, contribution);
 							correctedDir = getLookupForBoxProxy(worldPos, dir, probeData.position, probeData.boxExtents, probeData.invBoxTransform, probeData.transitionDistance, contribution);
 						}
 						}
 						
 						
-						float4 sample = gReflProbeCubmaps.SampleLevel(gReflProbeSamp, float4(correctedDir, probeData.cubemapIdx), mipLevel);
+						float4 sample = gReflProbeCubemaps.SampleLevel(gReflProbeSamp, float4(correctedDir, probeData.cubemapIdx), mipLevel);
 						sample *= contribution;
 						sample *= contribution;
 						
 						
-						output += sample * leftoverContribution; 
+						output += sample.rgb * leftoverContribution; 
 						leftoverContribution *= (1.0f - contribution);
 						leftoverContribution *= (1.0f - contribution);
 					}
 					}
 				}
 				}
@@ -184,7 +184,7 @@ Technique : base("ReflectionCubemapSampling") =
 					float skyMipLevel = mapRoughnessToMipLevel(roughness, gSkyCubemapNumMips);
 					float skyMipLevel = mapRoughnessToMipLevel(roughness, gSkyCubemapNumMips);
 					float4 sample = gSkyCubemapTex.SampleLevel(gSkyCubemapSamp, dir, skyMipLevel);
 					float4 sample = gSkyCubemapTex.SampleLevel(gSkyCubemapSamp, dir, skyMipLevel);
 					
 					
-					output += sample * leftoverContribution; 
+					output += sample.rgb * leftoverContribution; 
 				}
 				}
 						
 						
 				return output;
 				return output;
@@ -192,6 +192,24 @@ Technique : base("ReflectionCubemapSampling") =
 				#endif
 				#endif
 			}
 			}
 			
 			
+			half3 EnvBRDFApprox( half3 SpecularColor, half Roughness, half NoV )
+			{
+				// [ Lazarov 2013, "Getting More Physical in Call of Duty: Black Ops II" ]
+				// Adaptation to fit our G term.
+				const half4 c0 = { -1, -0.0275, -0.572, 0.022 };
+				const half4 c1 = { 1, 0.0425, 1.04, -0.04 };
+				half4 r = Roughness * c0 + c1;
+				half a004 = min( r.x * r.x, exp2( -9.28 * NoV ) ) * r.x + r.y;
+				half2 AB = half2( -1.04, 1.04 ) * a004 + r.zw;
+
+				// Anything less than 2% is physically impossible and is instead considered to be shadowing
+				// In ES2 this is skipped for performance as the impact can be small
+				// Note: this is needed for the 'specular' show flag to work, since it uses a SpecularColor of 0
+				AB.y *= saturate( 50.0 * SpecularColor.g );
+
+				return SpecularColor * AB.x + AB.y;
+			}			
+			
 			float3 getImageBasedSpecular(float3 worldPos, float3 V, SurfaceData surfaceData)
 			float3 getImageBasedSpecular(float3 worldPos, float3 V, SurfaceData surfaceData)
 			{
 			{
 				// See C++ code for generation of gPreintegratedEnvBRDF to see why this code works as is
 				// See C++ code for generation of gPreintegratedEnvBRDF to see why this code works as is
@@ -203,11 +221,12 @@ Technique : base("ReflectionCubemapSampling") =
 				float3 specularColor = lerp(float3(0.04f, 0.04f, 0.04f), surfaceData.albedo.rgb, surfaceData.metalness);
 				float3 specularColor = lerp(float3(0.04f, 0.04f, 0.04f), surfaceData.albedo.rgb, surfaceData.metalness);
 				
 				
 				float3 R = 2 * dot(V, N) * N - V;
 				float3 R = 2 * dot(V, N) * N - V;
-				float radiance = gatherReflectionRadiance(worldPos, R, surfaceData.roughness, 0, 0);
+				float3 radiance = gatherReflectionRadiance(worldPos, R, surfaceData.roughness, 0, 0);
 				
 				
-				float2 envBRDF = gPreintegratedEnvBRDF.SampleLevel(gPreintegratedEnvBRDFSamp, float2(NoV, surfaceData.roughness), 0).rg;
+				//float2 envBRDF = gPreintegratedEnvBRDF.SampleLevel(gPreintegratedEnvBRDFSamp, float2(NoV, surfaceData.roughness), 0).rg;
 				
 				
-				return radiance * (specularColor * envBRDF.x + envBRDF.y);
+				//return radiance * (specularColor * envBRDF.x + envBRDF.y);
+				return radiance * EnvBRDFApprox(specularColor, surfaceData.roughness, NoV);
 			}		
 			}		
 		};
 		};
 	};
 	};

+ 9 - 2
Data/Raw/Engine/Shaders/ReflectionCubeImportanceSample.bsl

@@ -126,8 +126,15 @@ Technique
 					// Note: Adding +1 bias as it looks better
 					// Note: Adding +1 bias as it looks better
 					mipLevel++;
 					mipLevel++;
 					
 					
-					// sum += H * GGX / PDF. In GGX/PDF most factors cancel out and we're left with 1/sin*cos
-					sum += gInputTex.SampleLevel(gInputSamp, H, mipLevel) * cosTheta;
+					// We need a light direction to properly evaluate the NoL term of the evaluation integral
+					//  Li(u) * brdf(u, v) * (u.n) / pdf(u, v)
+					// which we don't have, so we assume a viewing direction is equal to normal and calculate lighting dir from it and half-vector
+					float3 L = 2 * dot(N, H) * H - N;
+					float NoL = saturate(dot(N, L));
+					
+					// sum += radiance * GGX(h, roughness) * NoL / PDF. In GGX/PDF most factors cancel out and we're left with 1/cos (sine factor of the PDF only needed for the integral (I think), so we don't include it)
+					if(NoL > 0)
+						sum += gInputTex.SampleLevel(gInputSamp, H, mipLevel) * NoL / cosTheta;
 				}
 				}
 				
 				
 				return sum / NUM_SAMPLES;
 				return sum / NUM_SAMPLES;

+ 1 - 1
Source/BansheeCore/Include/BsSamplerState.h

@@ -23,7 +23,7 @@ namespace bs
 	struct BS_CORE_EXPORT SAMPLER_STATE_DESC
 	struct BS_CORE_EXPORT SAMPLER_STATE_DESC
 	{
 	{
 		SAMPLER_STATE_DESC()
 		SAMPLER_STATE_DESC()
-			: minFilter(FO_LINEAR), magFilter(FO_LINEAR), mipFilter(FO_POINT), 
+			: minFilter(FO_LINEAR), magFilter(FO_LINEAR), mipFilter(FO_LINEAR),
 			maxAniso(0), mipmapBias(0), mipMin(-FLT_MAX), mipMax(FLT_MAX),
 			maxAniso(0), mipmapBias(0), mipMin(-FLT_MAX), mipMax(FLT_MAX),
 			borderColor(Color::White), comparisonFunc(CMPF_ALWAYS_PASS)
 			borderColor(Color::White), comparisonFunc(CMPF_ALWAYS_PASS)
 		{ }
 		{ }