Browse Source

Add indirect diffuse class

Panagiotis Christopoulos Charitos 4 years ago
parent
commit
70958249ef

+ 114 - 0
AnKi/Renderer/IndirectDiffuse.cpp

@@ -0,0 +1,114 @@
+// Copyright (C) 2009-2021, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <AnKi/Renderer/IndirectDiffuse.h>
+#include <AnKi/Renderer/Renderer.h>
+#include <AnKi/Renderer/DepthDownscale.h>
+#include <AnKi/Renderer/GBuffer.h>
+#include <AnKi/Renderer/DownscaleBlur.h>
+#include <AnKi/Renderer/MotionVectors.h>
+#include <AnKi/Core/ConfigSet.h>
+#include <AnKi/Shaders/Include/SsgiTypes.h>
+
+namespace anki
+{
+
+IndirectDiffuse::~IndirectDiffuse()
+{
+}
+
+Error IndirectDiffuse::init(const ConfigSet& cfg)
+{
+	const Error err = initInternal(cfg);
+	if(err)
+	{
+		ANKI_R_LOGE("Failed to initialize indirect diffuse pass");
+	}
+	return err;
+}
+
+Error IndirectDiffuse::initInternal(const ConfigSet& cfg)
+{
+	const UVec2 size = m_r->getInternalResolution() / 2;
+	ANKI_ASSERT((m_r->getInternalResolution() % 2) == UVec2(0u) && "Needs to be dividable for proper upscaling");
+
+	m_main.m_maxSteps = cfg.getNumberU32("r_ssgiMaxSteps");
+	m_main.m_depthLod = min(cfg.getNumberU32("r_ssgiDepthLod"), m_r->getDepthDownscale().getMipmapCount() - 1);
+	m_main.m_firstStepPixels = 32;
+
+	ANKI_CHECK(getResourceManager().loadResource("EngineAssets/BlueNoise_Rgba8_16x16.png", m_main.m_noiseImage));
+
+	// Init SSGI
+	{
+		m_main.m_rtDescr =
+			m_r->create2DRenderTargetDescription(size.x(), size.y(), Format::R16G16B16A16_SFLOAT, "IndirectDiffuse");
+		m_main.m_rtDescr.bake();
+
+		ANKI_CHECK(getResourceManager().loadResource("Shaders/IndirectDiffuse.ankiprog", m_main.m_prog));
+
+		const ShaderProgramResourceVariant* variant;
+		m_main.m_prog->getOrCreateVariant(variant);
+		m_main.m_grProg = variant->getProgram();
+	}
+
+	return Error::NONE;
+}
+
+void IndirectDiffuse::populateRenderGraph(RenderingContext& ctx)
+{
+	RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
+
+	// SSGI+probes
+	{
+		// Create RTs
+		m_runCtx.m_ssgiRtHandle = rgraph.newRenderTarget(m_main.m_rtDescr);
+
+		// Create pass
+		ComputeRenderPassDescription& rpass = rgraph.newComputeRenderPass("IndirectDiffuse");
+
+		TextureSubresourceInfo hizSubresource;
+		hizSubresource.m_firstMipmap = m_main.m_depthLod;
+		rpass.newDependency(RenderPassDependency(m_r->getDepthDownscale().getHiZRt(), TextureUsageBit::SAMPLED_COMPUTE,
+												 hizSubresource));
+		rpass.newDependency(RenderPassDependency(m_r->getGBuffer().getColorRt(2), TextureUsageBit::SAMPLED_COMPUTE));
+		rpass.newDependency(RenderPassDependency(m_r->getDownscaleBlur().getRt(), TextureUsageBit::SAMPLED_COMPUTE));
+		rpass.newDependency(RenderPassDependency(m_runCtx.m_ssgiRtHandle, TextureUsageBit::IMAGE_COMPUTE_WRITE));
+
+		rpass.setWork([this, &ctx](RenderPassWorkContext& rgraphCtx) {
+			CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
+			cmdb->bindShaderProgram(m_main.m_grProg);
+
+			rgraphCtx.bindImage(0, 0, m_runCtx.m_ssgiRtHandle, TextureSubresourceInfo());
+
+			// Bind uniforms
+			SsgiUniforms* unis = allocateAndBindUniforms<SsgiUniforms*>(sizeof(SsgiUniforms), cmdb, 0, 1);
+			unis->m_depthBufferSize =
+				UVec2(m_r->getInternalResolution().x(), m_r->getInternalResolution().y()) >> (m_main.m_depthLod + 1);
+			unis->m_framebufferSize = m_r->getInternalResolution() / 2u;
+			unis->m_invProjMat = ctx.m_matrices.m_projectionJitter.getInverse();
+			unis->m_projMat = ctx.m_matrices.m_projectionJitter;
+			unis->m_prevViewProjMatMulInvViewProjMat =
+				ctx.m_prevMatrices.m_viewProjection * ctx.m_matrices.m_viewProjectionJitter.getInverse();
+			unis->m_normalMat = Mat3x4(Vec3(0.0f), ctx.m_matrices.m_view.getRotationPart());
+			unis->m_frameCount = m_r->getFrameCount() & MAX_U32;
+			unis->m_maxSteps = m_main.m_maxSteps;
+			unis->m_firstStepPixels = m_main.m_firstStepPixels;
+
+			// Bind the rest
+			cmdb->bindSampler(0, 2, m_r->getSamplers().m_trilinearClamp);
+			rgraphCtx.bindColorTexture(0, 3, m_r->getGBuffer().getColorRt(2));
+
+			TextureSubresourceInfo hizSubresource;
+			hizSubresource.m_firstMipmap = m_main.m_depthLod;
+			rgraphCtx.bindTexture(0, 4, m_r->getDepthDownscale().getHiZRt(), hizSubresource);
+			rgraphCtx.bindColorTexture(0, 5, m_r->getDownscaleBlur().getRt());
+
+			// Dispatch
+			dispatchPPCompute(cmdb, 8, 8, m_r->getInternalResolution().x() / 2, m_r->getInternalResolution().y() / 2);
+		});
+	}
+}
+
+} // end namespace anki

+ 66 - 0
AnKi/Renderer/IndirectDiffuse.h

@@ -0,0 +1,66 @@
+// Copyright (C) 2009-2021, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <AnKi/Renderer/RendererObject.h>
+#include <AnKi/Resource/ImageResource.h>
+#include <AnKi/Gr.h>
+
+namespace anki
+{
+
+/// @addtogroup renderer
+/// @{
+
+/// Global illumination.
+class IndirectDiffuse : public RendererObject
+{
+public:
+	IndirectDiffuse(Renderer* r)
+		: RendererObject(r)
+	{
+		registerDebugRenderTarget("IndirectDiffuse");
+	}
+
+	~IndirectDiffuse();
+
+	ANKI_USE_RESULT Error init(const ConfigSet& cfg);
+
+	void populateRenderGraph(RenderingContext& ctx);
+
+	void getDebugRenderTarget(CString rtName, RenderTargetHandle& handle,
+							  ShaderProgramPtr& optionalShaderProgram) const override
+	{
+		ANKI_ASSERT(rtName == "IndirectDiffuse");
+		handle = m_runCtx.m_ssgiRtHandle;
+	}
+
+private:
+	class
+	{
+	public:
+		ShaderProgramResourcePtr m_prog;
+		ShaderProgramPtr m_grProg;
+		RenderTargetDescription m_rtDescr;
+		ImageResourcePtr m_noiseImage;
+		U32 m_maxSteps = 32;
+		U32 m_firstStepPixels = 16;
+		U32 m_depthLod = 0;
+	} m_main;
+
+	class
+	{
+	public:
+		RenderTargetHandle m_ssgiRtHandle;
+	} m_runCtx;
+
+	ANKI_USE_RESULT Error initInternal(const ConfigSet& cfg);
+
+	void run(const RenderingContext& ctx, RenderPassWorkContext& rgraphCtx);
+};
+/// @}
+
+} // namespace anki

+ 5 - 0
AnKi/Renderer/Renderer.cpp

@@ -42,6 +42,7 @@
 #include <AnKi/Renderer/MotionVectors.h>
 #include <AnKi/Renderer/ClusterBinning.h>
 #include <AnKi/Renderer/Scale.h>
+#include <AnKi/Renderer/IndirectDiffuse.h>
 
 namespace anki
 {
@@ -208,6 +209,9 @@ Error Renderer::initInternal(const ConfigSet& config)
 	m_scale.reset(m_alloc.newInstance<Scale>(this));
 	ANKI_CHECK(m_scale->init(config));
 
+	m_indirectDiffuse.reset(m_alloc.newInstance<IndirectDiffuse>(this));
+	ANKI_CHECK(m_indirectDiffuse->init(config));
+
 	if(getGrManager().getDeviceCapabilities().m_rayTracingEnabled && config.getBool("scene_rayTracedShadows"))
 	{
 		m_accelerationStructureBuilder.reset(m_alloc.newInstance<AccelerationStructureBuilder>(this));
@@ -360,6 +364,7 @@ Error Renderer::populateRenderGraph(RenderingContext& ctx)
 	m_lensFlare->populateRenderGraph(ctx);
 	m_ssr->populateRenderGraph(ctx);
 	m_ssgi->populateRenderGraph(ctx);
+	m_indirectDiffuse->populateRenderGraph(ctx);
 	m_lightShading->populateRenderGraph(ctx);
 	m_temporalAA->populateRenderGraph(ctx);
 	m_scale->populateRenderGraph(ctx);

+ 1 - 0
AnKi/Renderer/RendererObjectDefs.h

@@ -31,3 +31,4 @@ ANKI_RENDERER_OBJECT_DEF(AccelerationStructureBuilder, accelerationStructureBuil
 ANKI_RENDERER_OBJECT_DEF(MotionVectors, motionVectors)
 ANKI_RENDERER_OBJECT_DEF(ClusterBinning, clusterBinning)
 ANKI_RENDERER_OBJECT_DEF(Scale, scale)
+ANKI_RENDERER_OBJECT_DEF(IndirectDiffuse, indirectDiffuse)

+ 121 - 0
AnKi/Shaders/IndirectDiffuse.ankiprog

@@ -0,0 +1,121 @@
+// Copyright (C) 2009-2021, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+// Dose SSGI and GI probe sampling
+
+#pragma anki start comp
+#include <AnKi/Shaders/SsRaymarching.glsl>
+#include <AnKi/Shaders/Functions.glsl>
+#include <AnKi/Shaders/PackFunctions.glsl>
+#include <AnKi/Shaders/ImportanceSampling.glsl>
+#include <AnKi/Shaders/TonemappingFunctions.glsl>
+#include <AnKi/Shaders/Include/SsgiTypes.h>
+
+const UVec2 WORKGROUP_SIZE = UVec2(8, 8);
+layout(local_size_x = WORKGROUP_SIZE.x, local_size_y = WORKGROUP_SIZE.y) in;
+
+layout(set = 0, binding = 0, rgba16f) uniform image2D out_img;
+
+layout(set = 0, binding = 1, row_major, std140) uniform b_unis
+{
+	SsgiUniforms u_unis;
+};
+
+layout(set = 0, binding = 2) uniform sampler u_trilinearClampSampler;
+layout(set = 0, binding = 3) uniform texture2D u_gbufferRt2;
+layout(set = 0, binding = 4) uniform texture2D u_depthRt;
+layout(set = 0, binding = 5) uniform texture2D u_lightBufferRt;
+
+void main()
+{
+	const UVec2 fixedGlobalInvocationId = min(gl_GlobalInvocationID.xy, u_unis.m_framebufferSize);
+	const Vec2 uv = (Vec2(fixedGlobalInvocationId.xy) + 0.5) / Vec2(u_unis.m_framebufferSize);
+	const Vec2 ndc = UV_TO_NDC(uv);
+
+	// Get normal
+	const Vec3 worldNormal = readNormalFromGBuffer(u_gbufferRt2, u_trilinearClampSampler, uv);
+	const Vec3 viewNormal = u_unis.m_normalMat * worldNormal;
+
+	// Get depth
+	const F32 depth = textureLod(u_depthRt, u_trilinearClampSampler, uv, 0.0).r;
+
+	// Compute view pos
+	const Vec4 viewPos4 = u_unis.m_invProjMat * Vec4(ndc, depth, 1.0);
+	const Vec3 viewPos = viewPos4.xyz / viewPos4.w;
+
+	// Get a random point inside the hemisphere. Use hemisphereSampleCos to avoid perpendicular vecs to viewNormal
+	const UVec2 random = rand3DPCG16(UVec3(fixedGlobalInvocationId, u_unis.m_frameCount)).xy;
+	Vec2 randomCircle = hammersleyRandom16(0u, 0xFFFFu, random);
+	randomCircle.x *= 0.9; // Reduce the cone angle a bit to avoid self-collisions
+	randomCircle.x = pow(randomCircle.x, 4.0); // Get more samples closer to the normal
+	const Vec3 randomHemisphere = rotationFromDirection(viewNormal) * hemisphereSampleCos(randomCircle);
+
+	// Trace
+	Vec3 hitPoint;
+	F32 hitAttenuation;
+	const U32 lod = 0u;
+	const F32 minStepf = 4.0;
+	const F32 noise = F32(random.x) * (1.0 / 65536.0);
+	raymarchGroundTruth(viewPos, randomHemisphere, uv, depth, u_unis.m_projMat, u_unis.m_maxSteps, u_depthRt,
+						u_trilinearClampSampler, F32(lod), u_unis.m_depthBufferSize, u_unis.m_firstStepPixels,
+						U32(mix(minStepf, F32(u_unis.m_firstStepPixels), noise)), hitPoint, hitAttenuation);
+
+	// Reject backfacing
+	ANKI_BRANCH if(hitAttenuation > 0.0)
+	{
+		const Vec3 hitNormal =
+			u_unis.m_normalMat * readNormalFromGBuffer(u_gbufferRt2, u_trilinearClampSampler, hitPoint.xy);
+		F32 backFaceAttenuation;
+		rejectBackFaces(randomHemisphere, hitNormal, backFaceAttenuation);
+
+		hitAttenuation *= backFaceAttenuation;
+	}
+
+	// Read the light buffer
+	Vec3 outColor;
+	ANKI_BRANCH if(hitAttenuation > 0.0)
+	{
+		// Reproject the UV because you are reading the previous frame
+		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);
+
+		// Read the light buffer
+		outColor = textureLod(u_lightBufferRt, u_trilinearClampSampler, hitPoint.xy, 100.0).rgb;
+		outColor = clamp(outColor, 0.0, FLT_MAX); // Fix the value just in case
+		outColor *= hitAttenuation;
+
+#if 0
+		// Compute a new normal based on the new hit point
+		const F32 depth = textureLod(u_depthRt, u_trilinearClampSampler, hitPoint.xy, 0.0).r;
+		const Vec4 viewPos4 = u_unis.m_invProjMat * Vec4(UV_TO_NDC(hitPoint.xy), depth, 1.0);
+		const Vec3 hitViewPos = viewPos4.xyz / viewPos4.w;
+		const Vec3 newViewNormal = normalize(hitViewPos - viewPos);
+#else
+		const Vec3 newViewNormal = viewNormal;
+#endif
+
+		// Modulate
+		const F32 NoL = max(0.0, dot(randomHemisphere, newViewNormal));
+		outColor *= NoL;
+		outColor *= 2.0 * PI;
+	}
+	else
+	{
+		outColor = Vec3(0.0, 0.0, 0.0);
+	}
+
+	// Remove fireflies
+	{
+		const F32 lum = computeLuminance(outColor) + 0.001;
+		const F32 averageLum = (subgroupAdd(lum) / F32(gl_SubgroupSize)) * 2.0;
+		const F32 newLum = min(lum, averageLum);
+		outColor *= newLum / lum;
+	}
+
+	// Store
+	imageStore(out_img, IVec2(fixedGlobalInvocationId), Vec4(outColor, 1.0));
+}
+
+#pragma anki end

+ 2 - 1
Samples/Common/Framework.cpp

@@ -74,7 +74,8 @@ Error SampleApp::userMainLoop(Bool& quit, Second elapsedTime)
 
 	if(in.getKey(KeyCode::U) == 1)
 	{
-		renderer.setCurrentDebugRenderTarget((renderer.getCurrentDebugRenderTarget() == "SSGI") ? "" : "SSGI");
+		renderer.setCurrentDebugRenderTarget(
+			(renderer.getCurrentDebugRenderTarget() == "IndirectDiffuse") ? "" : "IndirectDiffuse");
 	}
 
 	if(in.getKey(KeyCode::I) == 1)