Tout d'abord, je tiens à dire que j'ai lu beaucoup de messages sur la cartographie des ombres à l'aide de cartes de profondeur et de cubemaps et je comprends comment ils fonctionnent et j'ai également une expérience de travail avec eux en utilisant OpenGL, mais j'ai un problème de mise en œuvre Technique omnidirectionnelle de mappage d'ombres utilisant une source de lumière ponctuelle dans mon moteur graphique 3D nommé "EZ3". Mon moteur utilise WebGL comme API graphique 3D et JavaScript comme langage de programmation, c'est pour ma thèse de licence en informatique.
Fondamentalement, c'est ainsi que j'ai mis en œuvre mon algorithme de mappage d'ombres, mais je ne me concentrerai que sur le cas des lumières ponctuelles, car avec elles, je peux archiver le mappage d'ombres omnidirectionnel.
Tout d'abord, j'active l'abattage de la face avant comme ceci:
if (this.state.faceCulling !== Material.FRONT) {
if (this.state.faceCulling === Material.NONE)
gl.enable(gl.CULL_FACE);
gl.cullFace(gl.FRONT);
this.state.faceCulling = Material.FRONT;
}
Deuxièmement, je crée un programme de profondeur afin d'enregistrer des valeurs de profondeur pour chaque face cubemap, voici mon code de programme de profondeur dans GLSL 1.0:
Vertex Shader:
precision highp float;
attribute vec3 position;
uniform mat4 uModelView;
uniform mat4 uProjection;
void main() {
gl_Position = uProjection * uModelView * vec4(position, 1.0);
}
Fragment Shader:
precision highp float;
vec4 packDepth(const in float depth) {
const vec4 bitShift = vec4(256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0);
const vec4 bitMask = vec4(0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0);
vec4 res = mod(depth * bitShift * vec4(255), vec4(256)) / vec4(255);
res -= res.xxyz * bitMask;
return res;
}
void main() {
gl_FragData[0] = packDepth(gl_FragCoord.z);
}
Troisièmement, c'est le corps de ma fonction JavaScript qui «archive» le mappage omnidirectionnel des ombres
program.bind(gl);
for (i = 0; i < lights.length; i++) {
light = lights[i];
// Updates pointlight's projection matrix
light.updateProjection();
// Binds point light's depth framebuffer
light.depthFramebuffer.bind(gl);
// Updates point light's framebuffer in order to create it
// or if it's resolution changes, it'll be created again.
light.depthFramebuffer.update(gl);
// Sets viewport dimensions with depth framebuffer's dimensions
this.viewport(new Vector2(), light.depthFramebuffer.size);
if (light instanceof PointLight) {
up = new Vector3();
view = new Matrix4();
origin = new Vector3();
target = new Vector3();
for (j = 0; j < 6; j++) {
// Check in which cubemap's face we are ...
switch (j) {
case Cubemap.POSITIVE_X:
target.set(1, 0, 0);
up.set(0, -1, 0);
break;
case Cubemap.NEGATIVE_X:
target.set(-1, 0, 0);
up.set(0, -1, 0);
break;
case Cubemap.POSITIVE_Y:
target.set(0, 1, 0);
up.set(0, 0, 1);
break;
case Cubemap.NEGATIVE_Y:
target.set(0, -1, 0);
up.set(0, 0, -1);
break;
case Cubemap.POSITIVE_Z:
target.set(0, 0, 1);
up.set(0, -1, 0);
break;
case Cubemap.NEGATIVE_Z:
target.set(0, 0, -1);
up.set(0, -1, 0);
break;
}
// Creates a view matrix using target and up vectors according to each face of pointlight's
// cubemap. Furthermore, I translate it in minus light position in order to place
// the point light in the world's origin and render each cubemap's face at this
// point of view
view.lookAt(origin, target, up);
view.mul(new EZ3.Matrix4().translate(light.position.clone().negate()));
// Flips the Y-coordinate of each cubemap face
// scaling the projection matrix by (1, -1, 1).
// This is a perspective projection matrix which has:
// 90 degress of FOV.
// 1.0 of aspect ratio.
// Near clipping plane at 0.01.
// Far clipping plane at 2000.0.
projection = light.projection.clone();
projection.scale(new EZ3.Vector3(1, -1, 1));
// Attaches a cubemap face to current framebuffer in order to record depth values for the face with this line
// gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + j, id, 0);
light.depthFramebuffer.texture.attach(gl, j);
// Clears current framebuffer's color with these lines:
// gl.clearColor(1.0,1.0,1.0,1.0);
// gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
this.clear(color);
// Renders shadow caster meshes using the depth program
for (k = 0; k < shadowCasters.length; k++)
this._renderShadowCaster(shadowCasters[k], program, view, projection);
}
} else {
// Directional light & Spotlight case ...
}
}
Quatrièmement, c'est ainsi que je calcule la cartographie omnidirectionnelle des ombres en utilisant mon cubemap de profondeur dans mon Vertex Shader & Fragment Shader principal:
Vertex Shader:
precision highp float;
attribute vec3 position;
uniform mat4 uModel;
uniform mat4 uModelView;
uniform mat4 uProjection;
varying vec3 vPosition;
void main() {
vPosition = vec3(uModel * vec4(position, 1.0));
gl_Position = uProjection * uModelView * vec4(position, 1.0);
}
Fragment Shader:
float unpackDepth(in vec4 color) {
return dot(color, vec4(1.0 / (256.0 * 256.0 * 256.0), 1.0 / (256.0 * 256.0), 1.0 / 256.0, 1.0 ));
}
float pointShadow(const in PointLight light, const in samplerCube shadowSampler) {
vec3 direction = vPosition - light.position;
float vertexDepth = clamp(length(direction), 0.0, 1.0);
float shadowMapDepth = unpackDepth(textureCube(shadowSampler, direction));
return (vertexDepth > shadowMapDepth) ? light.shadowDarkness : 1.0;
}
Enfin, c'est le résultat que j'obtiens, ma scène a un plan, un cube et une sphère. En outre, la sphère lumineuse rouge est la source lumineuse ponctuelle:
Comme vous pouvez le voir, il me semble que le cubemap de framebuffer de profondeur de lumière ponctuelle ne fait pas une bonne interpolation entre leurs faces.
Jusqu'à présent, je ne sais pas comment résoudre ce problème.
la source
Réponses:
SOLUTION
Après quelques jours, j'ai réalisé que je calculais ma matrice de projection en utilisant un angle de champ de vision en degrés et qu'elle devrait être en radians . J'ai fait la conversion et maintenant tout fonctionne très bien. L'interpolation entre les faces du cubemap de mon framebuffer de profondeur est maintenant parfaite. Pour cette raison, il est important de gérer l'angle de chaque fonction trigonométrique en radians.
De plus, j'ai réalisé que vous pouvez calculer votre matrice de vue soit comme je l'ai dit dans la question et de cette façon:
Cette approche signifie que votre point de vue est placé au centre du projecteur et que vous effectuez un rendu dans chaque direction de votre cubemap, mais quelles sont ces directions? Eh bien, ces directions sont calculées en ajoutant chaque cible que j'ai dans le bloc de commutation (en fonction de la face de chaque cubemap) avec la position de votre projecteur .
De plus, il n'est pas nécessaire de retourner la coordonnée Y de la matrice de projection , dans ce cas, il est correct d'envoyer la matrice de projection en perspective du projecteur à votre shader GLSL sans la mettre à l'échelle par (1, -1, 1) car je travaille avec les textures qui n'ont pas de coordonnée Y inversée , je pense que vous ne devriez inverser la coordonnée Y de la matrice de projection de votre point lumineux que si vous travaillez avec la coordonnée Y d'une texture inversée , ceci afin d'avoir un effet de mappage d'ombre omnidirectionnel correct.
Enfin, je vais laisser ici la version finale de mon algorithme Omnidirectional Shadow Mapping côté CPU / GPU. Côté CPU, j'expliquerai chaque étape que vous devez faire pour calculer une carte d'ombre correcte pour le visage de chaque cubemap. D'un autre côté côté GPU, j'expliquerai le vertex / fragment shader de mon programme de profondeur et la fonction de mappage omnidirectionnel des ombres dans mon fragment shader principal, ceci afin d'aider quelqu'un qui pourrait apprendre cette technique, ou résoudre les doutes futurs sur cet algorithme. :
CPU
Sur la fonction renderMeshDepth, j'ai:
GPU
Programme de profondeur Vertex Shader:
Shader de fragment de programme de profondeur:
Fonction Omnidirectionnelle Shadow Mapping dans mon shader de fragment principal:
Ici vous avez un rendu final de l'algorithme
Amusez-vous à coder de beaux graphiques, bonne chance :)
CZ
la source