I'm still a bit unsure about the implementation of cascaded shadow mapping.
This is what it looks like at the moment:


At the top left are the 4 cascades - All of them encompass the same area for testing purposes.
This is without GL_COMPARE_REF_TO_TEXTURE, so all I'm doing is a depth lookup into the shadow map and multiplying the result with the diffuse fragment color. (Hence the different shadow intensities)

If I activate the comparison by setting the compare mode to GL_COMPARE_REF_TO_TEXTURE, this is the result:


This works, however, as you can see in the screenshot, there are several shadows that obviously don't belong there.

Here are my shaders:

Vertex Shader:
Code :
#version 330 core
 
layout(location = 0) in vec4 vertPos;
layout(location = 1) in vec2 vertexUV;
 
layout(std140) uniform ViewProjection
{
	mat4 M;
	mat4 V;
	mat4 P;
	mat4 MVP;
};
 
out vec4 Position_worldspace;
out vec4 Position_cameraspace;
 
out vec2 UV;
 
void main()
{
	gl_Position = MVP *vertPos;
	Position_worldspace = M *vertPos;
	Position_cameraspace = V *M *vertPos;
 
	UV = vertexUV;
}

Fragment Shader:
Code :
#version 330 core
 
layout(std140) uniform CSM
{
	vec4 csmFard;
	mat4 csmVP[4];
	int numCascades;
};
 
uniform sampler2D diffuseMap;
uniform sampler2DArrayShadow csmTextureArray;
 
in vec4 Position_worldspace;
in vec4 Position_cameraspace;
 
in vec2 UV;
 
out vec4 color;
 
float GetShadowTerm(sampler2DArrayShadow shadowMap)
{
	int index = numCascades -1;
	mat4 vp;
	for(int i=0;i<numCascades;i++)
	{
		if(gl_FragCoord.z < csmFard[i])
		{
			vp = csmVP[i];
			index = i;
			break;
		}
	}
	vec4 shadowCoord = vp *Position_worldspace;
 
	shadowCoord.w = shadowCoord.z;
	shadowCoord.z = float(index);
	shadowCoord.x = shadowCoord.x *0.5f +0.5f;
	shadowCoord.y = shadowCoord.y *0.5f +0.5f;
	return shadow2DArray(shadowMap,shadowCoord).x;
}
 
void main()
{
	color = texture2D(diffuseMap,UV).rgba;
	color.rgb *= GetShadowTerm(shadowMap);
}

This is essentially the same implementation as described in this document.
The only note-worthy difference, as far as I can tell, is in their shadow-map-lookup function:
Code :
float shadowCoef()
{
	int index = 3;
	// find the appropriate depth map to look up in
	// based on the depth of this fragment
	if(gl_FragCoord.z < far_d.x)
		index = 0;
	else if(gl_FragCoord.z < far_d.y)
		index = 1;
	else if(gl_FragCoord.z < far_d.z)
		index = 2;
 
	// transform this fragment's position from view space to
	// scaled light clip space such that the xy coordinates
	// lie in [0;1]. Note that there is no need to divide by w
	// for othogonal light sources
	vec4 shadow_coord = gl_TextureMatrix[index]*vPos;
	// set the current depth to compare with
	shadow_coord.w = shadow_coord.z;
 
	// tell glsl in which layer to do the look up
	shadow_coord.z = float(index);
 
	// let the hardware do the comparison for us
	return shadow2DArray(stex, shadow_coord).x;
}

More specifically, this line in particular:
Code :
vec4 shadow_coord = gl_TextureMatrix[index]*vPos;
They're using the view-space position, where I'm using the world-space position. In my case it only looks "right" with the world-space position, I can only guess that means my shadow matrices are incorrect?
The projection matrix for all cascades is currently calculated like this:
Code :
glm::vec3 min(-1024.f,-512.f,-1024.f); // Area, in which all shadow casters are located
glm::vec3 max(1024.f,512.f,1024.f);
glm::mat4 matProj = glm::ortho(min.z,max.z,min.x,max.x,-max.y,-min.y);

As for the view matrix:
Code :
glm::vec3 pos = glm::vec3(176.f,432.f,-390.f);
glm::vec3 dir = glm::vec3(0.f,-1.f,0.f);
glm::mat4 matView = glm::lookAt(
	pos,
	pos +dir,
	glm::vec3(1.f,0.f,0.f)
);
'pos' being the origin of the light (Which shouldn't matter(?), since we're using an orthographic projection), and 'dir' is its direction (Straight down).

What am I missing?