Bonjour.

Cela fait maintenant plusieurs jours que je tente désespérément de trouver le moyen de faire fonctionner la technique du shadow mapping en OpenGL. Je peine à trouver sur Internet un code source (en C/C++) ultra simple, très rudimentaire mais fonctionnel et montre clairement le déroulement des opérations : la création du Frame Buffer et de la texture qui contiendra les données de profondeur, comment les relier, etc, ainsi que les commandes à écrire lors des deux passes. Je ne cherche même pas à intégrer les effets de lumière, mais tout simplement à dessiner l'ombre des objets. Le truc vraiment très basique, donc.
Je ne tombe que sur de gros projets ou de simples bouts de code éparpillés au milieu d'explications, que j'ai essayé d'adapter à un code de base (en Qt) pour les faire tourner. Et je n'ai que des échecs. Je commence à m'arracher les cheveux...

Je suis vraiment dérouté par le fonctionnement interne d'OpenGL. Je ne suis clairement pas habitué aux machines à états. Mais bon, j'essaye de me soigner...

J'ai lu pas mal de théorie sur OpenGL (tutos, livres, etc), mais pour comprendre les choses un peu plus pointues j'ai vraiment besoin de m'appuyer sur du code source fonctionnel et de bidouiller, de tester, afin de mieux comprendre. Et dans le domaine du shadow mapping, on trouve énormément de ressources en ce qui concerne les explications, mais au niveau code c'est soit des vieilles versions d'OpenGL complètement dépassées ou bien des gros projets ou au contraire des miettes que je peine à adapter.

J'aimerais parvenir à du code qui fonctionne et qui me permette de bien comprendre le déroulement des choses.

A tout hasard, voici un bout de code que j'ai écrit après avoir récupéré des bouts de codes ici ou là, que j'ai tenté d'adapter. La fenêtre est une classe dérivée de QOpenGLWidget et de QOpenGLFunctions_4_5_Core.

Code : Sélectionner tout - Visualiser dans une fenêtre à part
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
Méthode initializeGL() :
 
// Création des vertices et des couleurs, contenus dans un object QVector<QVector3D>
// ...
 
// Création et linkage des shaders
    m_program_depth.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/vertex_depth.glsl");
    m_program_depth.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/fragment_depth.glsl");
    m_program_depth.link();
 
    m_program_rendu.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/vertex_rendu.glsl");
    m_program_rendu.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/fragment_rendu.glsl");
    m_program_rendu.link();
 
 
// Création des VBO
    VBO_vertex.create();
    VBO_vertex.bind();
    VBO_vertex.setUsagePattern(QOpenGLBuffer::StaticDraw);
    VBO_vertex.allocate(static_cast<GLvoid*>(m_vertices.data()), sizeof(QVector3D) * m_vertices.size());
    VBO_vertex.release();
 
    VBO_couleur.create();
    VBO_couleur.bind();
    VBO_couleur.setUsagePattern(QOpenGLBuffer::StaticDraw);
    VBO_couleur.allocate(static_cast<GLvoid*>(m_couleur.data()), sizeof(QVector3D) * m_couleur.size());
    VBO_couleur.release();
 
// Création des textures (code repiqué sur le net, je n'ai pas bien compris pourquoi toutes ces textures...)
    renderTexture = createTexture(640,480);
    depthTexture = createTexture(640,480,true);
    shadowMap = createTexture(shadowMapWidth, shadowMapHeight, true); // 640*8 pixels et 480*8 pixels
 
// Création du FBO (code repiqué également...)
    glGenFramebuffers(1, &shadowFramebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, shadowFramebuffer);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, renderTexture, 0);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT , GL_TEXTURE_2D, depthTexture , 0);
 
    int i=glCheckFramebufferStatus(GL_FRAMEBUFFER);
    if(i!=GL_FRAMEBUFFER_COMPLETE)
    {
        qDebug() << "Framebuffer is not OK, status=" << i;
    }
    glBindFramebuffer(GL_FRAMEBUFFER,0);
 
 
Méthode paintGL() :
 
// Création des matrices
	QMatrix4x4 vueProjMatrix;
    vueProjMatrix.perspective(60.0f, width() / height(), 1.0f, 1000.0f);
 
    QMatrix4x4 vueLookAtMatrix;
    vueLookAtMatrix.lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0f, 0.0f, -100.0f), QVector3D(0.0f, 1.0f, 0.0f));
 
    QVector3D light_position(10.0f, 30.0f, 10.0f);
    QVector3D light_direction(-3.5f, -3.5f, -10.0f);
 
    QMatrix4x4 lightLookAtMatrix;
    lightLookAtMatrix.lookAt(light_position, light_direction, QVector3D(0.0f, 1.0f, 0.0f));
 
    QMatrix4x4 lightProjMatrix;
    lightProjMatrix = vueProjMatrix;
 
    glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
    glClearDepth(1.0);
    glEnable(GL_DEPTH_TEST);
    glDisable(GL_COLOR_BUFFER_BIT);
    glDepthFunc(GL_LESS);
 
 
// 1ère passe :
 
    glViewport(0, 0, shadowMapWidth, shadowMapHeight);
 
    glClearColor(0.4f, 0.4f, 0.4f,1.0f);
    glClear(GL_COLOR_BUFFER_BIT |GL_DEPTH_BUFFER_BIT );
    glEnable(GL_CULL_FACE);
    glCullFace(GL_FRONT);
    glEnable(GL_DEPTH_TEST);
 
    m_program_depth.bind();
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, shadowFramebuffer);
    glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
    glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT , GL_TEXTURE_2D, shadowMap, 0);
 
    VBO_vertex.bind();
    m_program_depth.enableAttributeArray("pos_vertex_depth");
    m_program_depth.setAttributeBuffer("pos_vertex_depth", GL_FLOAT, 0, 3);
    VBO_vertex.release();
 
    m_program_depth.setUniformValue("modelViewProjectionMatrix_depth", lightProjMatrix * lightLookAtMatrix);
 
    m_program_depth.setUniformValue("modelMatrix_depth", QMatrix4x4());
    glDrawArrays(GL_TRIANGLES, 0, m_vertices.size());
    m_program_depth.disableAttributeArray("pos_vertex_depth");
 
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
 
    m_program_depth.release();
 
    glDisable(GL_CULL_FACE);
 
 
// 2ème passe :
 
    glViewport(0, 0, width(), height());
    glClearColor(0.4f, 0.4f, 0.4f,1.0f);
 
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_COLOR_BUFFER_BIT);
    glClear(GL_COLOR_BUFFER_BIT |GL_DEPTH_BUFFER_BIT );
 
    m_program_rendu.bind();
 
    VBO_vertex.bind();
    m_program_rendu.enableAttributeArray("pos_vertex_rendu");
    m_program_rendu.setAttributeBuffer("pos_vertex_rendu", GL_FLOAT, 0, 3);
    VBO_vertex.release();
 
    VBO_couleur.bind();
    m_program_rendu.enableAttributeArray("color_vertex_rendu");
    m_program_rendu.setAttributeBuffer("color_vertex_rendu", GL_FLOAT, 0, 3);
    VBO_couleur.release();
 
    QMatrix4x4 scaleBiasMatrix;
    scaleBiasMatrix.scale(QVector3D(0.5f, 0.5f, 0.5f));
    scaleBiasMatrix.translate(QVector3D(0.5f, 0.5f, 0.5f));
 
    m_program_rendu.setUniformValue("modelViewProjectionMatrix_rendu", vueProjMatrix * vueLookAtMatrix);
    m_program_rendu.setUniformValue("lightModelViewProjectionMatrix_rendu", scaleBiasMatrix * lightProjMatrix * lightLookAtMatrix);
 
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, shadowMap);
    m_program_rendu.setUniformValue("shadowMap", 0);
    m_program_rendu.setUniformValue("modelMatrix_rendu", QMatrix4x4());
    glDrawArrays(GL_TRIANGLES, 0, m_vertices.size());
 
    glBindTexture(GL_TEXTURE_2D, 0);
    m_program_rendu.disableAttributeArray("pos_vertex_rendu");
    m_program_rendu.disableAttributeArray("color_vertex_rendu");
    m_program_rendu.release();
 
 
// Méthode createTexture(int w, int h, bool isDepth = false) // Code repiqué sur le net...
{
    GLuint textureId;
    glGenTextures(1, &textureId);
    glBindTexture(GL_TEXTURE_2D, textureId);
    glTexImage2D(GL_TEXTURE_2D,0,(!isDepth ? GL_RGBA8 : GL_DEPTH_COMPONENT),w,h,0,isDepth ? GL_DEPTH_COMPONENT : GL_RGBA,GL_FLOAT,NULL);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_BORDER);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_BORDER);
 
    int i;
    i=glGetError();
    if(i!=0)
    {
        qDebug() << "Error happened while loading the texture: " << i;
    }
    glBindTexture(GL_TEXTURE_2D, 0);
    return textureId;
}	
 
 
// Vertex Shader du programme 1 (génère la texture de profondeur) :
#version 330 core
 
attribute vec3 pos_vertex_depth;
 
uniform mat4 modelViewProjectionMatrix_depth;
uniform mat4 modelMatrix_depth;
 
void main()
{
    gl_Position = modelViewProjectionMatrix_depth * modelMatrix_depth * vec4(pos_vertex_depth, 1.0);
}
 
 
// Fragment Shader du programme 1 (génère la texture de profondeur) :
#version 330 core
 
void main()
{
    gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
 
 
// Vertex Shader du programme 2 (génère l'image finale) :
 
#version 330 core
attribute vec3 pos_vertex_rendu;
attribute vec3 color_vertex_rendu;
 
uniform mat4 modelMatrix_rendu;
uniform mat4 modelViewProjectionMatrix_rendu;
uniform mat4 lightModelViewProjectionMatrix_rendu;
 
out vec4 lightVertexPosition_rendu;
out vec3 color_rendu;
 
void main()
{
	gl_Position = modelViewProjectionMatrix_rendu * modelMatrix_rendu * vec4(pos_vertex_rendu, 1.0);
	lightVertexPosition_rendu = lightModelViewProjectionMatrix_rendu * modelMatrix_rendu * vec4(pos_vertex_rendu, 1.0);
 
	color_rendu = color_vertex_rendu;
}
 
 
// Fragment Shader du programme 2 (génère l'image finale) :
 
#version 330 core
 
uniform sampler2D shadowMap;
in vec4 lightVertexPosition_rendu;
in vec3 color_rendu;
 
out vec4 outColor_rendu;
 
void main() // POUR LE MOMENT JE NE RENVOIE QUE LA COULEUR DU FRAGMENT (TEST)
{
#if 0
	float shadowValue = 0.0;
	vec4 lightVertexPosition2 = lightVertexPosition_rendu;
	lightVertexPosition2 /= lightVertexPosition2.w;
 
	for(float x=-0.001;x<=0.001;x+=0.0005)
	{
		for(float y=-0.001;y<=0.001;y+=0.0005)
		{
			if(texture2D(shadowMap, lightVertexPosition2.xy + vec2(x,y)).r >= lightVertexPosition2.z)
				shadowValue+=1.0;
		}
	}
	shadowValue /= 16.0;
	//gl_FragColor = vec4(outColor * (shadowValue+0.05), 1.0);
	//gl_FragColor = vec4(outColor, 1.0);
#endif
	outColor_rendu = vec4(color_rendu, 1.0);
}
Comme cela ne fonctionne pas, et pour des raisons de tests, mon Fragment Shader 2 ne fait que renvoyer la couleur du fragment.

J'ai constaté une bizarrerie également : lorsque je supprime la première passe (en entourant avec un #if 0), la seconde m'affiche bien quelque chose à l'écran. Et si je la laisse, la seconde ne m'affiche plus rien !
Après coup, j'ai vu qu'un simple "glBindFramebuffer(GL_FRAMEBUFFER, 0);" empêchait la seconde passe de m'afficher quelque chose à l'écran. Pourtant, cela oblige OpenGL de passer au FBO par défaut (celui qui affiche à l'écran et non dans une texture), non ? Comment cela se fait ??