Browse Source

Add film grain

Panagiotis Christopoulos Charitos 3 years ago
parent
commit
0c33f5c6b5

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

@@ -73,12 +73,14 @@ ANKI_CONFIG_VAR_U32(RProbeReflectionIrradianceResolution, 16, 4, 2048, "Reflecti
 ANKI_CONFIG_VAR_U32(RProbeRefectionMaxCachedProbes, 32, 4, 256, "Max cached number of reflection probes")
 ANKI_CONFIG_VAR_U32(RProbeReflectionShadowMapResolution, 64, 4, 2048, "Reflection probe shadow resolution")
 
+// Final composite
+ANKI_CONFIG_VAR_U32(RMotionBlurSamples, 32, 1, 2048, "Max motion blur samples")
+ANKI_CONFIG_VAR_F32(RFilmGrainStrength, 16.0f, 0.0f, 250.0f, "Film grain strength")
+
 // Lens flare
 ANKI_CONFIG_VAR_U8(RLensFlareMaxSpritesPerFlare, 8, 4, 255, "Max sprites per lens flare")
 ANKI_CONFIG_VAR_U8(RLensFlareMaxFlares, 16, 8, 255, "Max flare count")
 
-ANKI_CONFIG_VAR_U32(RMotionBlurSamples, 32, 1, 2048, "Max motion blur samples")
-
 ANKI_CONFIG_VAR_F32(RBloomThreshold, 2.5f, 0.0f, 256.0f, "Bloom threshold")
 ANKI_CONFIG_VAR_F32(RBloomScale, 2.5f, 0.0f, 256.0f, "Bloom scale")
 

+ 6 - 9
AnKi/Renderer/FinalComposite.cpp

@@ -37,13 +37,11 @@ Error FinalComposite::initInternal()
 	m_fbDescr.m_colorAttachmentCount = 1;
 	m_fbDescr.bake();
 
-	ANKI_CHECK(getResourceManager().loadResource("EngineAssets/BlueNoise_Rgba8_64x64.png", m_blueNoise));
-
 	// Progs
 	ANKI_CHECK(getResourceManager().loadResource("ShaderBinaries/FinalComposite.ankiprogbin", m_prog));
 
 	ShaderProgramResourceVariantInitInfo variantInitInfo(m_prog);
-	variantInitInfo.addMutation("BLUE_NOISE", 1);
+	variantInitInfo.addMutation("FILM_GRAIN", (getConfig().getRFilmGrainStrength() > 0.0) ? 1 : 0);
 	variantInitInfo.addMutation("BLOOM_ENABLED", 1);
 	variantInitInfo.addConstant("LUT_SIZE", U32(LUT_SIZE));
 	variantInitInfo.addConstant("FB_SIZE", m_r->getPostProcessResolution());
@@ -157,18 +155,17 @@ void FinalComposite::run(RenderingContext& ctx, RenderPassWorkContext& rgraphCtx
 
 		rgraphCtx.bindColorTexture(0, 4, m_r->getBloom().getRt());
 		cmdb->bindTexture(0, 5, m_lut->getTextureView());
-		cmdb->bindTexture(0, 6, m_blueNoise->getTextureView());
-		rgraphCtx.bindColorTexture(0, 7, m_r->getMotionVectors().getMotionVectorsRt());
-		rgraphCtx.bindTexture(0, 8, m_r->getGBuffer().getDepthRt(),
+		rgraphCtx.bindColorTexture(0, 6, m_r->getMotionVectors().getMotionVectorsRt());
+		rgraphCtx.bindTexture(0, 7, m_r->getGBuffer().getDepthRt(),
 							  TextureSubresourceInfo(DepthStencilAspectBit::DEPTH));
 
 		if(dbgEnabled)
 		{
-			rgraphCtx.bindColorTexture(0, 9, m_r->getDbg().getRt());
+			rgraphCtx.bindColorTexture(0, 8, m_r->getDbg().getRt());
 		}
 
-		const UVec4 frameCount(m_r->getFrameCount() & MAX_U32);
-		cmdb->setPushConstants(&frameCount, sizeof(frameCount));
+		const UVec4 pc(0, 0, floatBitsToUint(getConfig().getRFilmGrainStrength()), m_r->getFrameCount() & MAX_U32);
+		cmdb->setPushConstants(&pc, sizeof(pc));
 	}
 	else
 	{

+ 0 - 1
AnKi/Renderer/FinalComposite.h

@@ -40,7 +40,6 @@ private:
 	ShaderProgramPtr m_defaultVisualizeRenderTargetGrProg;
 
 	ImageResourcePtr m_lut; ///< Color grading lookup texture.
-	ImageResourcePtr m_blueNoise;
 
 	Error initInternal();
 

+ 9 - 14
AnKi/Shaders/FinalComposite.ankiprog

@@ -3,7 +3,7 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
-#pragma anki mutator BLUE_NOISE 0 1
+#pragma anki mutator FILM_GRAIN 0 1
 #pragma anki mutator BLOOM_ENABLED 0 1
 #pragma anki mutator DBG_ENABLED 0 1
 
@@ -27,16 +27,16 @@ layout(set = 0, binding = 2) uniform sampler u_trilinearRepeatSampler;
 layout(set = 0, binding = 3) uniform ANKI_RP texture2D u_lightShadingRt;
 layout(set = 0, binding = 4) uniform ANKI_RP texture2D u_ppsBloomLfRt;
 layout(set = 0, binding = 5) uniform ANKI_RP texture3D u_lut;
-layout(set = 0, binding = 6) uniform ANKI_RP texture2D u_blueNoise;
-layout(set = 0, binding = 7) uniform texture2D u_motionVectorsRt;
-layout(set = 0, binding = 8) uniform texture2D u_depthRt;
+layout(set = 0, binding = 6) uniform texture2D u_motionVectorsRt;
+layout(set = 0, binding = 7) uniform texture2D u_depthRt;
 #if DBG_ENABLED
-layout(set = 0, binding = 9) uniform ANKI_RP texture2D u_dbgOutlineRt;
+layout(set = 0, binding = 8) uniform ANKI_RP texture2D u_dbgOutlineRt;
 #endif
 
 layout(push_constant, std140) uniform b_pc
 {
-	Vec3 u_padding0;
+	Vec2 u_padding0;
+	F32 u_filmGrainStrength;
 	U32 u_frameCount;
 };
 
@@ -74,14 +74,9 @@ void main()
 
 	out_color = colorGrading(out_color);
 
-#if BLUE_NOISE
-	const Vec2 bnUvw = Vec2(FB_SIZE) / Vec2(64.0) * uv;
-	ANKI_RP Vec3 blueNoise = textureLod(u_blueNoise, u_trilinearRepeatSampler, bnUvw, 0.0).rgb;
-	blueNoise = animateBlueNoise(blueNoise, u_frameCount);
-	blueNoise = blueNoise * 2.0 - 1.0;
-	blueNoise = sign(blueNoise) * (1.0 - sqrt(1.0 - abs(blueNoise)));
-
-	out_color += blueNoise / 255.0;
+#if FILM_GRAIN
+	const ANKI_RP F32 dt = 1.0;
+	out_color = filmGrain(out_color, uv, u_filmGrainStrength, F32(u_frameCount % 0xFFFFu) * dt);
 #endif
 
 #if DBG_ENABLED

+ 7 - 0
AnKi/Shaders/Functions.glsl

@@ -677,3 +677,10 @@ Vec3 sRgbToLinear(Vec3 sRgb)
 	const Vec3 lower = sRgb / 12.92;
 	return mix(higher, lower, cutoff);
 }
+
+ANKI_RP Vec3 filmGrain(ANKI_RP Vec3 color, Vec2 uv, ANKI_RP F32 strength, ANKI_RP F32 time)
+{
+	const F32 x = (uv.x + 4.0) * (uv.y + 4.0) * time;
+	const F32 grain = 1.0 - (mod((mod(x, 13.0) + 1.0) * (mod(x, 123.0) + 1.0), 0.01) - 0.005) * strength;
+	return color * grain;
+}

+ 14 - 13
AnKi/Shaders/TonemappingFunctions.glsl

@@ -54,26 +54,27 @@ ANKI_RP Vec3 tonemapUncharted2(ANKI_RP Vec3 color)
 	return ((color * (A * color + C * B) + D * E) / (color * (A * color + B) + D * F)) - E / F;
 }
 
+const ANKI_RP F32 kACESA = 2.51;
+const ANKI_RP F32 kACESB = 0.03;
+const ANKI_RP F32 kACESC = 2.43;
+const ANKI_RP F32 kACESD = 0.59;
+const ANKI_RP F32 kACESE = 0.14;
+
+// See ACES in action and its inverse at https://www.desmos.com/calculator/n1lkpc6hwq
 ANKI_RP Vec3 tonemapACESFilm(ANKI_RP Vec3 x)
 {
-	const ANKI_RP F32 a = 2.51;
-	const ANKI_RP F32 b = 0.03;
-	const ANKI_RP F32 c = 2.43;
-	const ANKI_RP F32 d = 0.59;
-	const ANKI_RP F32 e = 0.14;
-
-	return saturate((x * (a * x + b)) / (x * (c * x + d) + e));
+	return saturate((x * (kACESA * x + kACESB)) / (x * (kACESC * x + kACESD) + kACESE));
 }
 
+// https://www.desmos.com/calculator/n1lkpc6hwq
 ANKI_RP Vec3 invertTonemapACESFilm(ANKI_RP Vec3 x)
 {
-	const ANKI_RP F32 a = 2.51;
-	const ANKI_RP F32 b = 0.03;
-	const ANKI_RP F32 c = 2.43;
-	const ANKI_RP F32 d = 0.59;
-	const ANKI_RP F32 e = 0.14;
+	ANKI_RP Vec3 res = kACESD * x - kACESB;
+	res += sqrt(x * x * (kACESD * kACESD - 4.0 * kACESE * kACESC) + x * (4.0 * kACESE * kACESA - 2.0 * kACESB * kACESD)
+				+ kACESB * kACESB);
+	res /= 2.0 * kACESA - 2.0 * kACESC * x;
 
-	return (-0.59 * x + 0.03 - sqrt(-1.0127 * x * x + 1.3702 * x + 0.0009)) / (2.0 * (2.43 * x - 2.51));
+	return res;
 }
 
 ANKI_RP Vec3 tonemap(ANKI_RP Vec3 color, ANKI_RP F32 exposure)

+ 16 - 0
AnKi/Util/Functions.h

@@ -346,6 +346,22 @@ inline void splitThreadedProblem(U32 threadId, U32 threadCount, U32 problemSize,
 	end = (threadId == threadCount - 1) ? problemSize : (threadId + 1u) * div;
 	ANKI_ASSERT(!(threadId == threadCount - 1 && end != problemSize));
 }
+
+/// Just copy the memory of a float to a uint.
+inline U64 floatBitsToUint(F64 f)
+{
+	U64 out;
+	memcpy(&out, &f, sizeof(out));
+	return out;
+}
+
+/// Just copy the memory of a float to a uint.
+inline U32 floatBitsToUint(F32 f)
+{
+	U32 out;
+	memcpy(&out, &f, sizeof(out));
+	return out;
+}
 /// @}
 
 } // end namespace anki