Ver código fonte

Renderer: Improve SSAO

Panagiotis Christopoulos Charitos 9 anos atrás
pai
commit
f460138eaa

+ 1 - 1
CMakeLists.txt

@@ -45,7 +45,7 @@ endif()
 
 macro(defineStaticExternalLibrary extProj extLib extLibFname)
 	add_library(${extLib} STATIC IMPORTED GLOBAL)
-	set_property(TARGET ${extLib} PROPERTY IMPORTED_LOCATION ${extLibFname})
+	set_target_properties(${extLib} PROPERTIES IMPORTED_LOCATION ${extLibFname})
 	add_dependencies(${extLib} ${extProj})
 endmacro()
 

+ 1 - 1
sandbox/Main.cpp

@@ -10,7 +10,7 @@
 
 using namespace anki;
 
-#define PLAYER 1
+#define PLAYER 0
 #define MOUSE 1
 
 class MyApp : public App

+ 2 - 0
shaders/ClusterLightCommon.glsl

@@ -17,6 +17,8 @@ struct LightingUniforms
 	mat4 viewMat;
 	mat3 invViewRotation;
 	uvec4 tileCount;
+	mat4 invViewProjMat;
+	mat4 prevViewProjMat;
 };
 
 // Point light

+ 3 - 0
shaders/Common.glsl

@@ -33,6 +33,9 @@ const uint UBO_MAX_SIZE = 16384u;
 
 const uint MAX_U32 = 0xFFFFFFFFu;
 
+#define UV_TO_NDC(x_) ((x_)*2.0 - 1.0)
+#define NDC_TO_UV(x_) ((x_)*0.5 + 0.5)
+
 // Common locations
 #define POSITION_LOCATION 0
 #define TEXTURE_COORDINATE_LOCATION 1

+ 0 - 9
shaders/FsUpscale.frag.glsl

@@ -13,9 +13,6 @@ layout(location = 0) out vec4 out_color;
 layout(ANKI_TEX_BINDING(0, 0)) uniform sampler2D u_depthFullTex;
 layout(ANKI_TEX_BINDING(0, 1)) uniform sampler2D u_depthHalfTex;
 layout(ANKI_TEX_BINDING(0, 2)) uniform sampler2D u_fsRt;
-#if SSAO_ENABLED
-layout(ANKI_TEX_BINDING(0, 3)) uniform sampler2D u_ssaoTex;
-#endif
 
 const float DEPTH_THRESHOLD = 1.0 / 4000.0;
 
@@ -34,11 +31,5 @@ void main()
 		bilateralUpsample(u_depthFullTex, u_depthHalfTex, u_fsRt, 1.0 / vec2(SRC_SIZE), in_uv, u_linearizeCfPad2.xy);
 #endif
 
-#if SSAO_ENABLED
-	float ssao = textureLod(u_ssaoTex, in_uv, 0.0).r;
-	out_color = color;
-	out_color.a *= ssao;
-#else
 	out_color = color;
-#endif
 }

+ 23 - 4
shaders/Is.frag.glsl

@@ -22,6 +22,7 @@ layout(ANKI_TEX_BINDING(0, 3)) uniform sampler2D u_msDepthRt;
 
 layout(ANKI_TEX_BINDING(1, 0)) uniform sampler2D u_diffDecalTex;
 layout(ANKI_TEX_BINDING(1, 1)) uniform sampler2D u_normalRoughnessDecalTex;
+layout(ANKI_TEX_BINDING(1, 2)) uniform sampler2D u_ssaoTex;
 
 layout(location = 0) in vec2 in_uv;
 layout(location = 1) flat in int in_instanceId;
@@ -34,10 +35,8 @@ const uint TILE_COUNT = TILE_COUNT_X * TILE_COUNT_Y;
 const float SUBSURFACE_MIN = 0.05;
 
 // Return frag pos in view space
-vec3 getFragPosVSpace()
+vec3 getFragPosVSpace(float depth)
 {
-	float depth = texture(u_msDepthRt, in_uv, 0.0).r;
-
 	vec3 fragPos;
 	fragPos.z = u_lightingUniforms.projectionParams.z / (u_lightingUniforms.projectionParams.w + depth);
 	fragPos.xy = in_projectionParams * fragPos.z;
@@ -126,10 +125,26 @@ void readIndirect(in uint idxOffset,
 	}
 }
 
+float readSsao(float depth, vec2 ndc)
+{
+	vec4 worldPos4 = u_lightingUniforms.invViewProjMat * vec4(ndc, UV_TO_NDC(depth), 1.0);
+	worldPos4 = worldPos4 / worldPos4.w;
+
+	// Project to get old ndc
+	vec4 oldNdc4 = u_lightingUniforms.prevViewProjMat * worldPos4;
+	vec2 oldNdc = oldNdc4.xy / oldNdc4.w;
+
+	vec2 oldUv = NDC_TO_UV(oldNdc);
+
+	return texture(u_ssaoTex, oldUv).r;
+}
+
 void main()
 {
+	float depth = texture(u_msDepthRt, in_uv, 0.0).r;
+
 	// Get frag pos in view space
-	vec3 fragPos = getFragPosVSpace();
+	vec3 fragPos = getFragPosVSpace(depth);
 	vec3 viewDir = normalize(-fragPos);
 
 	// Decode GBuffer
@@ -151,6 +166,10 @@ void main()
 	subsurface = max(gbuffer.subsurface, SUBSURFACE_MIN);
 	emission = gbuffer.emission;
 
+	// Get SSAO
+	float ssao = readSsao(depth, UV_TO_NDC(in_uv));
+	diffCol *= ssao;
+
 	// Get counts and offsets
 	uint clusterIdx =
 		computeClusterK(

+ 9 - 9
shaders/Pps.frag.glsl

@@ -149,21 +149,21 @@ void main()
 
 	out_color = colorGrading(out_color);
 
+#if BLUE_NOISE
+	vec3 blueNoise = texture(u_blueNoise, vec3(FB_SIZE / vec2(64.0) * uv, u_blueNoiseLayerPad3.x), 0.0).rgb;
+	blueNoise = blueNoise * 2.0 - 1.0;
+	blueNoise = sign(blueNoise) * (1.0 - sqrt(1.0 - abs(blueNoise)));
+
+	out_color += blueNoise / 255.0;
+#endif
+
 #if 0
 	{
-		out_color = textureLod(u_smaaBlendTex, uv, 0.0).rgb;
+		out_color = vec3(textureLod(u_isRt, uv, 0.0).r);
 	}
 #endif
 
 #if DBG_ENABLED
 	out_color += textureLod(u_dbgRt, uv, 0.0).rgb;
 #endif
-
-#if BLUE_NOISE
-	vec3 blueNoise = texture(u_blueNoise, vec3(FB_SIZE / vec2(64.0) * uv, u_blueNoiseLayerPad3.x), 0.0).rgb;
-	blueNoise = blueNoise * 2.0 - 1.0;
-	blueNoise = sign(blueNoise) * (1.0 - sqrt(1.0 - abs(blueNoise)));
-
-	out_color += blueNoise / 255.0;
-#endif
 }

+ 41 - 47
shaders/Ssao.frag.glsl

@@ -8,23 +8,16 @@
 #include "shaders/Pack.glsl"
 #include "shaders/Functions.glsl"
 
-const vec3 KERNEL[KERNEL_SIZE] = KERNEL_ARRAY; // This will be appended in C++
+const vec2 KERNEL[SAMPLES] = KERNEL_ARRAY; // This will be appended in C++
+const float BIAS = 0.3;
 
-const float RANGE_CHECK_RADIUS = RADIUS * 2.0;
-
-// Initial is 1.0 but the bigger it is the more darker the SSAO factor gets
-const float DARKNESS_MULTIPLIER = 1.9;
-
-// The algorithm will chose the number of samples depending on the distance
-const float MAX_DISTANCE = 40.0;
-
-layout(location = 0) in vec2 in_texCoords;
+layout(location = 0) in vec2 in_uv;
 
 layout(location = 0) out float out_color;
 
 layout(ANKI_UBO_BINDING(0, 0), std140, row_major) uniform _blk
 {
-	vec4 u_projectionParams;
+	vec4 u_unprojectionParams;
 	vec4 u_projectionMat;
 };
 
@@ -41,19 +34,17 @@ vec3 readNormal(in vec2 uv)
 }
 
 // Read the noise tex
-vec3 readRandom(in vec2 uv)
+float readRandom(in vec2 uv)
 {
 	const vec2 tmp = vec2(float(WIDTH) / float(NOISE_MAP_SIZE), float(HEIGHT) / float(NOISE_MAP_SIZE));
-
-	vec3 noise = texture(u_noiseMap, vec3(tmp * uv, 0.0)).xyz;
-	return noise;
+	return texture(u_noiseMap, vec3(tmp * uv, 0.0)).r;
 }
 
 // Returns the Z of the position in view space
 float readZ(in vec2 uv)
 {
 	float depth = texture(u_mMsDepthRt, uv).r;
-	float z = u_projectionParams.z / (u_projectionParams.w + depth);
+	float z = u_unprojectionParams.z / (u_unprojectionParams.w + depth);
 	return z;
 }
 
@@ -63,48 +54,51 @@ vec3 readPosition(in vec2 uv)
 	vec3 fragPosVspace;
 	fragPosVspace.z = readZ(uv);
 
-	fragPosVspace.xy = (2.0 * uv - 1.0) * u_projectionParams.xy * fragPosVspace.z;
+	fragPosVspace.xy = (2.0 * uv - 1.0) * u_unprojectionParams.xy * fragPosVspace.z;
 
 	return fragPosVspace;
 }
 
-void main(void)
+vec4 project(vec4 point)
 {
-	vec3 origin = readPosition(in_texCoords);
-
-	vec3 normal = readNormal(in_texCoords);
-	vec3 rvec = readRandom(in_texCoords);
-
-	vec3 tangent = normalize(rvec - normal * dot(rvec, normal));
-	vec3 bitangent = cross(normal, tangent);
-	mat3 tbn = mat3(tangent, bitangent, normal);
+	return projectPerspective(point, u_projectionMat.x, u_projectionMat.y, u_projectionMat.z, u_projectionMat.w);
+}
 
-	// Iterate kernel
+void main(void)
+{
+	vec2 ndc = in_uv * 2.0 - 1.0;
+	vec3 origin = readPosition(in_uv);
+	vec3 normal = readNormal(in_uv);
+
+	float randFactor = readRandom(in_uv);
+	float randAng = randFactor * PI * 2.0;
+	float cosAng = cos(randAng);
+	float sinAng = sin(randAng);
+
+	// Find the projected radius
+	vec3 sphereLimit = origin + vec3(RADIUS, 0.0, 0.0);
+	vec4 projSphereLimit = project(vec4(sphereLimit, 1.0));
+	vec2 projSphereLimit2 = projSphereLimit.xy / projSphereLimit.w;
+	float projRadius = length(projSphereLimit2 - ndc);
+
+	// Integrate
 	float factor = 0.0;
-	for(uint i = 0U; i < KERNEL_SIZE; ++i)
+	for(uint i = 0U; i < SAMPLES; ++i)
 	{
-		vec3 hemispherePoint = tbn * KERNEL[i];
-		hemispherePoint = hemispherePoint * RADIUS + origin;
-
-		// project sample position:
-		vec4 projHemiPoint = projectPerspective(
-			vec4(hemispherePoint, 1.0), u_projectionMat.x, u_projectionMat.y, u_projectionMat.z, u_projectionMat.w);
-		projHemiPoint.xy = projHemiPoint.xy / (2.0 * projHemiPoint.w) + 0.5; // persp div & to NDC -> [0, 1]
-
-		// get sample depth:
-		float sampleZ = readZ(projHemiPoint.xy);
+		// Rotate the disk point
+		vec2 diskPoint = KERNEL[i] * projRadius;
+		vec2 rotDiskPoint;
+		rotDiskPoint.x = diskPoint.x * cosAng - diskPoint.y * sinAng;
+		rotDiskPoint.y = diskPoint.y * cosAng + diskPoint.x * sinAng;
+		vec2 finalDiskPoint = ndc + rotDiskPoint;
 
-		// Range check
-		float rangeCheck = abs(origin.z - sampleZ) / RANGE_CHECK_RADIUS;
-		rangeCheck = 1.0 - clamp(rangeCheck, 0.0, 1.0);
+		vec3 s = readPosition(NDC_TO_UV(finalDiskPoint));
+		vec3 u = s - origin;
 
-		// Accumulate
-		const float ADVANCE = DARKNESS_MULTIPLIER / float(KERNEL_SIZE);
-		float f = ceil(sampleZ - hemispherePoint.z);
-		f = clamp(f, 0.0, 1.0) * ADVANCE;
+		float f = max(dot(normal, u) + BIAS, 0.0) / max(dot(u, u), EPSILON);
 
-		factor += f * rangeCheck;
+		factor += f;
 	}
 
-	out_color = 1.0 - factor;
+	out_color = 1.0 - factor / float(SAMPLES);
 }

+ 0 - 2
src/anki/renderer/FsUpscale.cpp

@@ -8,7 +8,6 @@
 #include <anki/renderer/Ms.h>
 #include <anki/renderer/Is.h>
 #include <anki/renderer/Fs.h>
-#include <anki/renderer/Ssao.h>
 #include <anki/renderer/Volumetric.h>
 #include <anki/renderer/DepthDownscale.h>
 #include <anki/scene/FrustumComponent.h>
@@ -74,7 +73,6 @@ void FsUpscale::run(RenderingContext& ctx)
 	cmdb->bindTexture(0, 0, m_r->getMs().m_depthRt);
 	cmdb->bindTextureAndSampler(0, 1, m_r->getDepthDownscale().m_hd.m_depthRt, m_nearestSampler);
 	cmdb->bindTexture(0, 2, m_r->getFs().getRt());
-	cmdb->bindTexture(0, 3, m_r->getSsao().getRt());
 
 	cmdb->setBlendFactors(0, BlendFactor::ONE, BlendFactor::SRC_ALPHA);
 

+ 7 - 0
src/anki/renderer/Is.cpp

@@ -6,6 +6,7 @@
 #include <anki/renderer/Is.h>
 #include <anki/renderer/Renderer.h>
 #include <anki/renderer/Sm.h>
+#include <anki/renderer/Ssao.h>
 #include <anki/renderer/Ir.h>
 #include <anki/renderer/Ms.h>
 #include <anki/renderer/LightBin.h>
@@ -25,6 +26,8 @@ public:
 	Mat4 m_viewMat;
 	Mat3x4 m_invViewRotation;
 	UVec4 m_tileCount;
+	Mat4 m_invViewProjMat;
+	Mat4 m_prevViewProjMat;
 };
 
 enum class ShaderVariantBit : U8
@@ -187,6 +190,7 @@ void Is::run(RenderingContext& ctx)
 
 	cmdb->bindTexture(1, 0, (ctx.m_is.m_diffDecalTex) ? ctx.m_is.m_diffDecalTex : m_dummyTex);
 	cmdb->bindTexture(1, 1, (ctx.m_is.m_normRoughnessDecalTex) ? ctx.m_is.m_normRoughnessDecalTex : m_dummyTex);
+	cmdb->bindTexture(1, 2, m_r->getSsao().getRt());
 
 	bindUniforms(cmdb, 0, 0, ctx.m_is.m_commonToken);
 	bindUniforms(cmdb, 0, 1, ctx.m_is.m_pointLightsToken);
@@ -218,6 +222,9 @@ void Is::updateCommonBlock(RenderingContext& ctx)
 	blk->m_rendererSizeTimePad1 = Vec4(m_r->getWidth(), m_r->getHeight(), HighRezTimer::getCurrentTime(), 0.0);
 
 	blk->m_tileCount = UVec4(m_clusterCounts[0], m_clusterCounts[1], m_clusterCounts[2], m_clusterCount);
+
+	blk->m_invViewProjMat = fr.getViewProjectionMatrix().getInverse();
+	blk->m_prevViewProjMat = ctx.m_prevViewProjMat;
 }
 
 void Is::setPreRunBarriers(RenderingContext& ctx)

+ 3 - 9
src/anki/renderer/Renderer.cpp

@@ -31,15 +31,6 @@
 namespace anki
 {
 
-/// See shader for documentation
-class RendererCommonUniforms
-{
-public:
-	Vec4 m_projectionParams;
-	Vec4 m_nearFarLinearizeDepth;
-	Mat4 m_projectionMatrix;
-};
-
 static Bool threadWillDoWork(
 	const RenderingContext& ctx, VisibilityGroupType typeOfWork, U32 threadId, PtrSize threadCount)
 {
@@ -169,6 +160,8 @@ Error Renderer::render(RenderingContext& ctx)
 
 	ANKI_ASSERT(ctx.m_frustumComponent->getFrustum().getType() == FrustumType::PERSPECTIVE);
 
+	ctx.m_prevViewProjMat = m_prevViewProjMat;
+
 	// Check if resources got loaded
 	if(m_prevLoadRequestCount != m_resources->getLoadingRequestCount()
 		|| m_prevAsyncTasksCompleted != m_resources->getAsyncTaskCompletedCount())
@@ -279,6 +272,7 @@ Error Renderer::render(RenderingContext& ctx)
 	ANKI_CHECK(m_pps->run(ctx));
 
 	++m_frameCount;
+	m_prevViewProjMat = ctx.m_frustumComponent->getViewProjectionMatrix();
 
 	return ErrorCode::NONE;
 }

+ 3 - 0
src/anki/renderer/Renderer.h

@@ -34,6 +34,7 @@ class RenderingContext
 public:
 	/// Active frustum.
 	FrustumComponent* m_frustumComponent ANKI_DBG_NULLIFY_PTR;
+	Mat4 m_prevViewProjMat;
 
 	CommandBufferPtr m_commandBuffer; ///< Primary command buffer.
 
@@ -406,6 +407,8 @@ private:
 
 	Bool8 m_willDrawToDefaultFbo = false;
 
+	Mat4 m_prevViewProjMat = Mat4::getIdentity();
+
 	ANKI_USE_RESULT Error initInternal(const ConfigSet& initializer);
 
 	ANKI_USE_RESULT Error buildCommandBuffers(RenderingContext& ctx);

+ 22 - 13
src/anki/renderer/Ssao.cpp

@@ -15,8 +15,8 @@
 namespace anki
 {
 
-const F32 HEMISPHERE_RADIUS = 1.1; // In game units
-const U KERNEL_SIZE = 8;
+const F32 HEMISPHERE_RADIUS = 3.0; // In game units
+const U SAMPLES = 8;
 const PixelFormat Ssao::RT_PIXEL_FORMAT(ComponentFormat::R8, TransformFormat::UNORM);
 
 template<typename TVec>
@@ -37,6 +37,18 @@ static void genHemisphere(TVec* ANKI_RESTRICT arr, TVec* ANKI_RESTRICT arrEnd)
 	} while(++arr != arrEnd);
 }
 
+template<typename TVec>
+static void genDisk(TVec* ANKI_RESTRICT arr, TVec* ANKI_RESTRICT arrEnd)
+{
+	ANKI_ASSERT(arr && arrEnd && arr != arrEnd);
+
+	do
+	{
+		arr->x() = randRange(0.1, 1.0) * 2.0 - 1.0;
+		arr->y() = randRange(0.1, 1.0) * 2.0 - 1.0;
+	} while(++arr != arrEnd);
+}
+
 Error Ssao::createFb(FramebufferPtr& fb, TexturePtr& rt)
 {
 	// Set to bilinear because the blurring techniques take advantage of that
@@ -85,17 +97,14 @@ Error Ssao::initInternal(const ConfigSet& config)
 	// Kernel
 	//
 	StringAuto kernelStr(getAllocator());
-	Array<Vec3, KERNEL_SIZE> kernel;
+	Array<Vec2, SAMPLES> kernel;
 
-	genHemisphere(kernel.begin(), kernel.end());
-	kernelStr.create("vec3[](");
+	genDisk(kernel.begin(), kernel.end());
+	kernelStr.create("vec2[](");
 	for(U i = 0; i < kernel.size(); i++)
 	{
 		StringAuto tmp(getAllocator());
-
-		tmp.sprintf(
-			"vec3(%f, %f, %f) %s", kernel[i].x(), kernel[i].y(), kernel[i].z(), (i != kernel.size() - 1) ? ", " : ")");
-
+		tmp.sprintf("vec2(%f, %f)%s", kernel[i].x(), kernel[i].y(), (i != kernel.getSize() - 1) ? ", " : ")");
 		kernelStr.append(tmp);
 	}
 
@@ -109,13 +118,13 @@ Error Ssao::initInternal(const ConfigSet& config)
 		"#define NOISE_MAP_SIZE %u\n"
 		"#define WIDTH %u\n"
 		"#define HEIGHT %u\n"
-		"#define KERNEL_SIZE %u\n"
+		"#define SAMPLES %u\n"
 		"#define KERNEL_ARRAY %s\n"
 		"#define RADIUS float(%f)\n",
 		m_noiseTex->getWidth(),
 		m_width,
 		m_height,
-		KERNEL_SIZE,
+		SAMPLES,
 		&kernelStr[0],
 		HEMISPHERE_RADIUS));
 
@@ -129,7 +138,7 @@ Error Ssao::initInternal(const ConfigSet& config)
 		"#define HPASS\n"
 		"#define COL_R\n"
 		"#define TEXTURE_SIZE vec2(%f, %f)\n"
-		"#define KERNEL_SIZE 13\n",
+		"#define KERNEL_SIZE 11\n",
 		F32(m_width),
 		F32(m_height)));
 
@@ -141,7 +150,7 @@ Error Ssao::initInternal(const ConfigSet& config)
 		"#define VPASS\n"
 		"#define COL_R\n"
 		"#define TEXTURE_SIZE vec2(%f, %f)\n"
-		"#define KERNEL_SIZE 11\n",
+		"#define KERNEL_SIZE 9\n",
 		F32(m_width),
 		F32(m_height)));