PDA

View Full Version : Cascade Shadow Mapping: artefacts via sampler2D array



nimelord
11-20-2017, 01:55 PM
Hi, All.

I,m trying to refactor shader for arrays instead several textures.

I have problem with artefacts on the shadow maps border when I'm using:



uniform sampler2D shadowMap[3];


instead:


uniform sampler2D shadowMap_0;
uniform sampler2D shadowMap_1;
uniform sampler2D shadowMap_2;


there is a screenshot: 2558

Have no idea where is the problem.

Regards.
Thanks, for ansver.

GClements
11-20-2017, 02:45 PM
How are you indexing the array, and which GLSL version are you using?

In GLSL 4.x, arrays of samplers may only be indexed with dynamically-uniform expressions (in a vertex shader the expression may only involve uniform variables, in a fragment shader it may only involve uniform variables and flat-qualified inputs).

In prior versions, arrays of samplers may only be indexed with constant expressions (you can't even use uniform variables; the expression must be capable of being evaluated during shader compilation).

If you try to index a sampler array with an expression which doesn't meet these constraints, the behaviour is undefined.

If you can't satisfy these constraints, then consider using an array texture instead.

nimelord
11-21-2017, 12:58 AM
I'm not sure what do you mean.

But I use indexing dynamically, calculating index directly in the shader, not via uniform.

shader sample:



#version 400

const int NUM_CASCADES = 3;

uniform sampler2D dlShadowMap[NUM_CASCADES];
uniform float cascadeFarPlanes[NUM_CASCADES];

....

float calcShadow(vec4 position, int idx, vec3 normal, vec3 lightNormal) {

vec3 projCoords = position.xyz;
projCoords = projCoords * 0.5 + 0.5;
float bias = 0.0002;

float shadowFactor = 0.0;
vec2 inc = 1.0 / textureSize(dlShadowMap[idx], 0);

for(int row = -1; row <= 1; ++row) {
for(int col = -1; col <= 1; ++col) {
float textDepth = texture(dlShadowMap[idx], projCoords.xy).r;
shadowFactor += projCoords.z - bias > textDepth ? 1.0 : 0.0;
}
}
shadowFactor /= 9.0;

if(projCoords.z > 1.0) {
shadowFactor = 1.0;
}

return 1 - shadowFactor;
}


void main() {

....

int idx;
for (int i=0; i<NUM_CASCADES; i++) {
if ( abs(mvVertexPos.z) < cascadeFarPlanes[i] ) {
idx = i;
break;
}
}


float shadow = calcShadow(mlightviewVertexPos[idx], idx, currNomal, normalize(directionalLight.direction));
fragColor = clamp(ambientC * vec4(ambientLight, 1) + directionalLightResult * shadow + aroundLight, 0, 1);
float distance = length(mvVertexPos);
fragColor = calcDistanceDiffusing(distance, fragColor, distanceDiffusing);
}



This is incorrect using, am i right?

GClements
11-21-2017, 01:52 AM
This is incorrect using, am i right?
It's incorrect.

In a vertex shader, the index expression can only use constants and uniforms, i.e. the index must be the same for all vertices in the same draw call.

nimelord
11-21-2017, 06:56 AM
I moved calculation of index to vertex shader from faragment one.
And I have the same result with artefacts.

vertex shader:



#version 400

const int DL_NUM_CASCADES = 3;

flat out int dlCascadeIndex;

uniform float cascadeFarPlanes[DL_NUM_CASCADES];


void main()
{
...
for (int i=0; i<DL_NUM_CASCADES; i++) {
if ( abs(mvVertexPos.z) < cascadeFarPlanes[i] ) {
dlCascadeIndex = i;
break;
}
}
....
}



fragment shader:


#version 400

const int NUM_CASCADES = 3;

flat in int dlCascadeIndex;

uniform sampler2D dlShadowMap[NUM_CASCADES];


.....

textDepth = texture(dlShadowMap[dlCascadeIndex], projCoords.xy + vec2(row, col) * inc).r;


......



Could you answer another one question, please: OpenGL make one vertex shader call per one frame shader call?

GClements
11-21-2017, 11:51 AM
I moved calculation of index to vertex shader from faragment one.
And I have the same result with artefacts.
Your updated code is valid for GLSL 4.x.


Could you answer another one question, please: OpenGL make one vertex shader call per one frame shader call?
No. The vertex shader is invoked once per vertex (if you're using e.g. glDrawElements() and a vertex index is repeated, the vertex shader may be executed again or cached results from a previous invocation may be used). The fragment shader is invoked once per fragment. The outputs from the vertex shader are interpolated across the primitive to obtain the values fed to the fragment shader (for flat-qualified and integer variables, the value computed for one of the vertices is used for all fragments).

nimelord
11-21-2017, 12:39 PM
I can have one part of the fragment in under one shadow map and other part of fragment under second shadow map.
That means one fragment should use different indexes, but because reasons you described it uses one.


It seems only one solution - array texture.

Dark Photon
11-21-2017, 07:24 PM
It seems only one solution - array texture.
There are others (atlas, cube map, etc.), but array textures are the best solution I know of.

By the way, when you get there, you may end up hitting artifacts on shadow map split boundaries (the shadow maps indexed by the indices referred to above). If you want the joy of figuring out the root cause yourself, don't read the rest of this message. But if not, check out this thread: Cascaded shadow map bug between splits (https://www.opengl.org/discussion_boards/showthread.php/176968-Cascaded-shadow-map-bug-between-splits).

nimelord
11-23-2017, 02:10 PM
Thank you, guys!

I did it via array texture. :)