Shadow mapping qui déconne

Le problème exposé dans ce sujet a été résolu.

Hello !

Voilà cela fait quelques jours que je me bats avec les ombres dans Nazara, j'ai enfin réussi à en obtenir mais elles ne sont pas correctes.

Il y a divers bugs, notamment le fait que l'ombre ne semble pas vraiment correspondre, disparaît parfois, que le frustum de shadow mapping est plus petit que la lumière produite par le spotlight (et là j'avoue je suis toujours un peu wtf).

Voici une vidéo pour montrer tout ça (une image valant mille mots, cette vidéo vaut 3300 000 mots ! :D ): http://youtu.be/B-GXQSSr7_A

Alors côté implémentation, c'est du shadow mapping bête et méchant pour l'instant, avec la génération de la shadow map ici:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
    void RenderSystem::UpdateShadowMaps()
    {
        if (!m_shadowRT.IsValid())
            m_shadowRT.Create();

        for (const Ndk::EntityHandle& light : m_lights)
        {
            LightComponent& lightComponent = light->GetComponent<LightComponent>();
            NodeComponent& lightNode = light->GetComponent<NodeComponent>();

            if (!lightComponent.IsShadowCastingEnabled() || lightComponent.GetLightType() != nzLightType_Spot)
                continue;

            NzVector2ui shadowMapSize(lightComponent.GetShadowMap()->GetSize());

            m_shadowRT.AttachTexture(nzAttachmentPoint_Depth, 0, lightComponent.GetShadowMap());

            ///TODO: Cache the matrices in the light
            NzRenderer::SetMatrix(nzMatrixType_Projection, NzMatrix4f::Perspective(lightComponent.GetOuterAngle(), 1.f, 1.f, 1000.f));
            NzRenderer::SetMatrix(nzMatrixType_View, NzMatrix4f::ViewMatrix(lightNode.GetPosition(), lightNode.GetRotation()));
            NzRenderer::SetTarget(&m_shadowRT);
            NzRenderer::SetViewport(NzRecti(0, 0, shadowMapSize.x, shadowMapSize.y));

            NzAbstractRenderQueue* renderQueue = m_shadowTechnique.GetRenderQueue();
            renderQueue->Clear();

            for (const Ndk::EntityHandle& drawable : m_drawables)
            {
                GraphicsComponent& graphicsComponent = drawable->GetComponent<GraphicsComponent>();
                NodeComponent& drawableNode = drawable->GetComponent<NodeComponent>();

                graphicsComponent.AddToRenderQueue(renderQueue);
            }

            NzSceneData sceneData;
            sceneData.ambientColor = NzColor(0, 0, 0);
            sceneData.background = nullptr;
            sceneData.viewer = nullptr; //< Depth technique doesn't require any viewer

            m_shadowTechnique.Draw(sceneData);
        }
    }

Lors du rendu normal, les lumières sont ajoutées à la RenderQueue avec la shadow map et les matrices qui vont avec (oui il y a duplication, de toute façon mon objectif est surtout d'avoir un truc qui marche là :D ):

1
2
            light.viewMatrix = NzMatrix4f::ViewMatrix(transformMatrix.GetTranslation(), transformMatrix.GetRotation());
            light.projectionMatrix = NzMatrix4f::Perspective(m_outerAngle, 1.f, 1.f, 1000.f);

Qui sont envoyés un peu plus loin au shader:

1
2
3
4
5
6
7
8
9
                            NzTextureSampler sampler;
                            sampler.SetFilterMode(nzSamplerFilter_Bilinear);
                            sampler.SetWrapMode(nzSamplerWrap_Clamp);
                            NzRenderer::SetTexture(8, light.shadowMap);
                            NzRenderer::SetTextureSampler(8, sampler);

                            shader->SendMatrix(shader->GetUniformLocation("LightProjMatrix"), light.projectionMatrix);
                            shader->SendMatrix(shader->GetUniformLocation("LightViewMatrix"), light.viewMatrix);
                            shader->SendInteger(shader->GetUniformLocation("ShadowMap"), 8);

Le Vertex Shader s'occupe ensuite de refaire la projection:

1
    vLightSpacePos = LightProjMatrix * LightViewMatrix * WorldMatrix * vec4(VertexPosition, 1.0);

Et le Fragment Shader s'occupe ensuite de projeter la shadow map:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
    if (vLightSpacePos.w > 0.0)
    {
        // Perspective
        vec3 ProjectionCoords = vLightSpacePos.xyz / vLightSpacePos.w;

        // Bias
        vec2 UVCoords;
        UVCoords.x = 0.5 * ProjectionCoords.x + 0.5;
        UVCoords.y = 0.5 * ProjectionCoords.y + 0.5;

        if (UVCoords.x >= 0.0 && UVCoords.x < 1.0 &&
            UVCoords.y >= 0.0 && UVCoords.y < 1.0 &&
            ProjectionCoords.z >= 0.0 && ProjectionCoords.z < 1.0)
        {
            float Depth = texture(ShadowMap, UVCoords).x;
            if (Depth < ProjectionCoords.z - 0.005)
            {
                lightDiffuse = vec3(0.0);
                lightSpecular = vec3(0.0);
            }
        }
    }

Voilà, tout ceci donne le résultat en vidéo, je donne aussi le code responsable du calcul d'éclairage (j'ai renommé quelques variables pour rendre ça plus compréhensible):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
                case LIGHT_SPOT:
                {
                    vec3 lightDir = LightPos - vWorldPos;
                    float lightDirLength = length(lightDir);
                    lightDir /= lightDirLength; // Normalisation

                    float att = max(LightAttenuation - LightInvRadius*lightDirLength, 0.0);

                    // Ambient
                    lightAmbient += att * LightColor * LightAmbientFactor * (MaterialAmbient.rgb + SceneAmbient.rgb);

                    // Modification de l'atténuation pour gérer le spot
                    float curAngle = dot(LightDirection, -lightDir);
                    float innerMinusOuterAngle = LightInnerAngle - LightOuterAngle; // Ce sont des cosinus
                    att *= max((curAngle - LightOuterAngle) / innerMinusOuterAngle, 0.0);

                    // Diffuse
                    float lambert = max(dot(normal, lightDir), 0.0);

                    lightDiffuse += att * lambert * LightColor * LightDiffuseFactor;

                    // Specular
                    vec3 reflection = reflect(-lightDir, normal);
                    float specularFactor = max(dot(reflection, eyeVec), 0.0);
                    specularFactor = pow(specularFactor, MaterialShininess);

                    lightSpecular += att * specularFactor * LightColor;
                    break;
                }

Si quelqu'un pouvait me donner un coup de main pour le coup, ça m'arrangerait beaucoup, ce problème va me rendre fou.

Je poste aussi le code, si quelqu'un veut voir quelle fonction fait quoi: Dépôt Github (Branche shadow sans une partie du code plus haut). Matrix4.inl (algorithme de génération des matrices).

Pour le reste (RTT) c'est basiquement de l'OpenGL (m_shadowRT.AttachTexture fait appel à glFrameBufferTexture2D par exemple), voilà.

Si besoin de plus d'information, aucun souci n'hésitez pas à demander :)

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte