Browse Source

Add stochastic SS reflections

Panagiotis Christopoulos Charitos 4 years ago
parent
commit
6dbc21ea88

+ 4 - 1
AnKi/Renderer/ConfigVars.defs.h

@@ -22,8 +22,11 @@ ANKI_CONFIG_VAR_F32(RVolumetricLightingAccumulationQualityZ, 4.0f, 1.0f, 16.0f,
 ANKI_CONFIG_VAR_U32(RVolumetricLightingAccumulationFinalZSplit, 26, 1, 256,
 					"Final cluster split that will recieve volumetric lights")
 
-ANKI_CONFIG_VAR_U32(RSsrMaxSteps, 64, 1, 256, "Max SSR raymarching steps")
+// SSR
+ANKI_CONFIG_VAR_U32(RSsrFirstStepPixels, 32, 1, 256, "The 1st step in ray marching")
 ANKI_CONFIG_VAR_U32(RSsrDepthLod, 2, 0, 1000, "Texture LOD of the depth texture that will be raymarched")
+ANKI_CONFIG_VAR_U32(RSsrMaxSteps, 64, 1, 256, "Max SSR raymarching steps")
+ANKI_CONFIG_VAR_BOOL(RSsrStochastic, false, "Stochastic reflections")
 
 ANKI_CONFIG_VAR_U32(RIndirectDiffuseSsgiSampleCount, 8, 1, 1024, "SSGI sample count")
 ANKI_CONFIG_VAR_F32(RIndirectDiffuseSsgiRadius, 2.0f, 0.1f, 100.0f, "SSGI radius in meters")

+ 1 - 1
AnKi/Renderer/ProbeReflections.cpp

@@ -50,7 +50,7 @@ Error ProbeReflections::initInternal()
 	ANKI_CHECK(initShadowMapping());
 
 	// Load split sum integration LUT
-	ANKI_CHECK(getResourceManager().loadResource("EngineAssets/SplitSumIntegration.png", m_integrationLut));
+	ANKI_CHECK(getResourceManager().loadResource("EngineAssets/IblDfg.png", m_integrationLut));
 
 	SamplerInitInfo sinit;
 	sinit.m_minMagFilter = SamplingFilter::LINEAR;

+ 6 - 4
AnKi/Renderer/Ssr.cpp

@@ -32,9 +32,8 @@ Error Ssr::initInternal()
 {
 	const U32 width = m_r->getInternalResolution().x() / 2;
 	const U32 height = m_r->getInternalResolution().y() / 2;
-	m_firstStepPixels = 32;
 
-	ANKI_CHECK(getResourceManager().loadResource("EngineAssets/BlueNoise_Rgba8_16x16.png", m_noiseImage));
+	ANKI_CHECK(getResourceManager().loadResource("EngineAssets/BlueNoise_Rgba8_64x64.png", m_noiseImage));
 
 	// Create RT
 	m_rtDescr = m_r->create2DRenderTargetDescription(width, height, Format::R16G16B16A16_SFLOAT, "SSR");
@@ -47,8 +46,11 @@ Error Ssr::initInternal()
 	ANKI_CHECK(getResourceManager().loadResource(
 		(getConfig().getRPreferCompute()) ? "Shaders/SsrCompute.ankiprog" : "Shaders/SsrRaster.ankiprog", m_prog));
 
+	ShaderProgramResourceVariantInitInfo variantInit(m_prog);
+	variantInit.addMutation("EXTRA_REJECTION", false);
+	variantInit.addMutation("STOCHASTIC", getConfig().getRSsrStochastic());
 	const ShaderProgramResourceVariant* variant;
-	m_prog->getOrCreateVariant(variant);
+	m_prog->getOrCreateVariant(variantInit, variant);
 	m_grProg = variant->getProgram();
 
 	return Error::NONE;
@@ -115,7 +117,7 @@ void Ssr::run(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx)
 	unis->m_depthMipCount = m_r->getDepthDownscale().getMipmapCount();
 	unis->m_maxSteps = getConfig().getRSsrMaxSteps();
 	unis->m_lightBufferMipCount = m_r->getDownscaleBlur().getMipmapCount();
-	unis->m_firstStepPixels = m_firstStepPixels;
+	unis->m_firstStepPixels = getConfig().getRSsrFirstStepPixels();
 	unis->m_prevViewProjMatMulInvViewProjMat =
 		ctx.m_prevMatrices.m_viewProjection * ctx.m_matrices.m_viewProjectionJitter.getInverse();
 	unis->m_projMat = ctx.m_matrices.m_projectionJitter;

+ 0 - 2
AnKi/Renderer/Ssr.h

@@ -43,8 +43,6 @@ private:
 
 	ImageResourcePtr m_noiseImage;
 
-	U32 m_firstStepPixels = 16;
-
 	class
 	{
 	public:

+ 55 - 2
AnKi/Shaders/LightFunctions.glsl

@@ -150,10 +150,10 @@ ANKI_RP Vec3 specularIsotropicLobe(GbufferInfo gbuffer, Vec3 viewDir, Vec3 frag2
 	return F * (V * D);
 }
 
-Vec3 envBRDF(Vec3 specular, F32 roughness, texture2D integrationLut, sampler integrationLutSampler, F32 NoV)
+Vec3 specularDFG(Vec3 F0, F32 roughness, texture2D integrationLut, sampler integrationLutSampler, F32 NoV)
 {
 	const Vec2 envBRDF = textureLod(integrationLut, integrationLutSampler, Vec2(roughness, NoV), 0.0).xy;
-	return specular * envBRDF.x + min(1.0, 50.0 * specular.g) * envBRDF.y;
+	return mix(envBRDF.xxx, envBRDF.yyy, F0);
 }
 
 ANKI_RP F32 computeSpotFactor(ANKI_RP Vec3 l, ANKI_RP F32 outerCos, ANKI_RP F32 innerCos, ANKI_RP Vec3 spotDir)
@@ -399,3 +399,56 @@ U32 computeShadowCascadeIndex(F32 distance, F32 p, F32 effectiveShadowDistance,
 	idx = min(idx, shadowCascadeCountf - 1.0f);
 	return U32(idx);
 }
+
+/// To play with it use https://www.shadertoy.com/view/sttSDf
+/// http://jcgt.org/published/0007/04/01/paper.pdf by Eric Heitz
+/// Input v: view direction
+/// Input alphaX, alphaY: roughness parameters
+/// Input u1, u2: uniform random numbers
+/// Output: normal sampled with PDF D_Ve(nE) = G1(v) * max(0, dot(v, nE)) * D(nE) / v.z
+Vec3 sampleGgxVndf(Vec3 v, F32 alphaX, F32 alphaY, F32 u1, F32 u2)
+{
+	// Section 3.2: transforming the view direction to the hemisphere configuration
+	const Vec3 vH = normalize(Vec3(alphaX * v.x, alphaY * v.y, v.z));
+
+	// Section 4.1: orthonormal basis (with special case if cross product is zero)
+	const F32 lensq = vH.x * vH.x + vH.y * vH.y;
+	const Vec3 tangent1 = (lensq > 0.0) ? Vec3(-vH.y, vH.x, 0) * inversesqrt(lensq) : Vec3(1.0, 0.0, 0.0);
+	const Vec3 tangent2 = cross(vH, tangent1);
+
+	// Section 4.2: parameterization of the projected area
+	const F32 r = sqrt(u1);
+	const F32 phi = 2.0 * PI * u2;
+	const F32 t1 = r * cos(phi);
+	F32 t2 = r * sin(phi);
+	const F32 s = 0.5 * (1.0 + vH.z);
+	t2 = (1.0 - s) * sqrt(1.0 - t1 * t1) + s * t2;
+
+	// Section 4.3: reprojection onto hemisphere
+	const Vec3 nH = t1 * tangent1 + t2 * tangent2 + sqrt(max(0.0, 1.0 - t1 * t1 - t2 * t2)) * vH;
+
+	// Section 3.4: transforming the normal back to the ellipsoid configuration
+	const Vec3 nE = normalize(Vec3(alphaX * nH.x, alphaY * nH.y, max(0.0, nH.z)));
+
+	return nE;
+}
+
+/// Calculate the reflection vector based on roughness.
+Vec3 sampleReflectionVector(Vec3 viewDir, Vec3 normal, F32 roughness, Vec2 uniformRandom)
+{
+	const Mat3 tbn = rotationFromDirection(normal);
+	const Mat3 tbnT = transpose(tbn);
+	const Vec3 viewDirTbn = tbnT * (-viewDir);
+
+	Vec3 sampledNormalTbn = sampleGgxVndf(viewDirTbn, roughness, roughness, uniformRandom.x, uniformRandom.y);
+	const Bool perfectReflection = false; // For debugging
+	if(perfectReflection)
+	{
+		sampledNormalTbn = Vec3(0.0, 0.0, 1.0);
+	}
+
+	const Vec3 reflectedDirTbn = reflect(-viewDirTbn, sampledNormalTbn);
+
+	// Transform reflected_direction back to the initial space.
+	return tbn * reflectedDirTbn;
+}

+ 2 - 2
AnKi/Shaders/LightShading.ankiprog

@@ -224,8 +224,8 @@ void main()
 		const Vec3 finalSpecIndirect = specIndirect * ssr.a + ssr.rgb;
 
 		// Compute env BRDF
-		const F32 NoV = max(EPSILON, dot(gbuffer.m_normal, viewDir));
-		const Vec3 env = envBRDF(gbuffer.m_f0, gbuffer.m_roughness, u_integrationLut, u_trilinearClampSampler, NoV);
+		const F32 NoV = max(0.0, dot(gbuffer.m_normal, viewDir));
+		const Vec3 env = specularDFG(gbuffer.m_f0, gbuffer.m_roughness, u_integrationLut, u_trilinearClampSampler, NoV);
 
 		out_color += finalSpecIndirect * env;
 	}

+ 19 - 9
AnKi/Shaders/Ssr.glsl

@@ -3,9 +3,10 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#define EXTRA_REJECTION 0
+#pragma anki mutator STOCHASTIC 0 1
+#pragma anki mutator EXTRA_REJECTION 0 1
 
-#include <AnKi/Shaders/Functions.glsl>
+#include <AnKi/Shaders/LightFunctions.glsl>
 #include <AnKi/Shaders/PackFunctions.glsl>
 #include <AnKi/Shaders/Include/SsrTypes.h>
 #include <AnKi/Shaders/TonemappingFunctions.glsl>
@@ -24,7 +25,7 @@ layout(set = 0, binding = 5) uniform texture2D u_lightBufferRt;
 
 layout(set = 0, binding = 6) uniform sampler u_trilinearRepeatSampler;
 layout(set = 0, binding = 7) uniform texture2D u_noiseTex;
-const Vec2 NOISE_TEX_SIZE = Vec2(16.0);
+const Vec2 NOISE_TEX_SIZE = Vec2(64.0);
 
 #if defined(ANKI_COMPUTE_SHADER)
 const UVec2 WORKGROUP_SIZE = UVec2(8, 8);
@@ -55,6 +56,11 @@ void main()
 	// Get depth
 	const F32 depth = textureLod(u_depthRt, u_trilinearClampSampler, uv, 0.0).r;
 
+	// Rand idx
+	const Vec2 noiseUv = Vec2(u_unis.m_framebufferSize) / NOISE_TEX_SIZE * uv;
+	const Vec3 noise =
+		animateBlueNoise(textureLod(u_noiseTex, u_trilinearRepeatSampler, noiseUv, 0.0).rgb, u_unis.m_frameCount % 8u);
+
 	// Get view pos
 	const Vec4 viewPos4 = u_unis.m_invProjMat * Vec4(UV_TO_NDC(uv), depth, 1.0);
 	const Vec3 viewPos = viewPos4.xyz / viewPos4.w;
@@ -62,12 +68,11 @@ void main()
 	// Compute refl vector
 	const Vec3 viewDir = normalize(viewPos);
 	const Vec3 viewNormal = u_unis.m_normalMat * worldNormal;
+#if STOCHASTIC
+	const Vec3 reflVec = sampleReflectionVector(viewDir, viewNormal, roughness, noise.xy);
+#else
 	const Vec3 reflVec = reflect(viewDir, viewNormal);
-
-	// Rand idx
-	const Vec2 noiseUv = Vec2(u_unis.m_framebufferSize) / NOISE_TEX_SIZE * uv;
-	const Vec2 noiseShift = 1.0 / NOISE_TEX_SIZE * F32(u_unis.m_frameCount % 4u);
-	const F32 noise = textureLod(u_noiseTex, u_trilinearRepeatSampler, noiseUv + noiseShift, 0.0).r;
+#endif
 
 	// Do the heavy work
 	Vec3 hitPoint;
@@ -78,7 +83,7 @@ void main()
 	const F32 minStepf = stepf / 4.0;
 	raymarchGroundTruth(viewPos, reflVec, uv, depth, u_unis.m_projMat, u_unis.m_maxSteps, u_depthRt,
 						u_trilinearClampSampler, F32(lod), u_unis.m_depthBufferSize, step,
-						U32((stepf - minStepf) * noise + minStepf), hitPoint, hitAttenuation);
+						U32((stepf - minStepf) * noise.x + minStepf), hitPoint, hitAttenuation);
 
 #if EXTRA_REJECTION
 	// Reject backfacing
@@ -118,8 +123,13 @@ void main()
 		const Vec4 v4 = u_unis.m_prevViewProjMatMulInvViewProjMat * Vec4(UV_TO_NDC(hitPoint.xy), hitPoint.z, 1.0);
 		hitPoint.xy = NDC_TO_UV(v4.xy / v4.w);
 
+#if STOCHASTIC
+		// LOD stays 0
+		const F32 lod = 0.0;
+#else
 		// Compute the LOD based on the roughness
 		const F32 lod = F32(u_unis.m_lightBufferMipCount - 1u) * roughness;
+#endif
 
 		// Read the light buffer
 		outColor.rgb = textureLod(u_lightBufferRt, u_trilinearClampSampler, hitPoint.xy, lod).rgb;

BIN
EngineAssets/IblDfg.png


BIN
EngineAssets/SplitSumIntegration.png