Przede wszystkim chcę powiedzieć, że przeczytałem wiele postów na temat mapowania cieni za pomocą map głębokości i map sześciennych i rozumiem, jak one działają, a także mam doświadczenie w pracy z nimi przy użyciu OpenGL, ale mam problem z implementacją Technika dookólnego mapowania cieni przy użyciu jednopunktowego źródła światła w moim silniku graficznym 3D o nazwie „EZ3”. Mój silnik używa WebGL jako API grafiki 3D i JavaScript jako języka programowania, to jest praca licencjacka z informatyki.
Zasadniczo tak zaimplementowałem mój algorytm mapowania cieni, ale skupię się tylko na obudowie świateł punktowych, ponieważ dzięki nim mogę archiwizować dookólne mapowanie cienia.
Po pierwsze, aktywuję wygładzanie przedniej części twarzy w następujący sposób:
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;
}
Po drugie, tworzę program głębokości, aby rejestrować wartości głębokości dla każdej powierzchni mapy cubemap, oto mój kod programu głębokości w GLSL 1.0:
Shader Vertex:
precision highp float;
attribute vec3 position;
uniform mat4 uModelView;
uniform mat4 uProjection;
void main() {
gl_Position = uProjection * uModelView * vec4(position, 1.0);
}
Shader fragmentów:
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);
}
Po trzecie, to jest ciało mojej funkcji JavaScript, które „archiwizuje” wszechkierunkowe mapowanie cienia
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 ...
}
}
Po czwarte, w ten sposób obliczam wielokierunkowe mapowanie cieni za pomocą mojej mapy głębokości w moim głównym Vertex Shaderze i Fragment Shaderze:
Shader Vertex:
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);
}
Shader fragmentów:
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;
}
Wreszcie otrzymuję wynik, moja scena ma płaszczyznę, sześcian i kulę. Poza tym czerwona jasna kula jest punktowym źródłem światła:
Jak widać, wydaje mi się, że mapa kubełkowa ramki światła o głębokości światła punktowego nie interpoluje dobrze ich twarzy.
Do tej pory nie mam pojęcia, jak to rozwiązać.
źródło
Odpowiedzi:
ROZWIĄZANIE
Po kilku dniach zdałem sobie sprawę, że obliczam swoją matrycę projekcyjną używając kąta pola widzenia w stopniach i powinna być w radianach . Dokonałem konwersji i teraz wszystko działa świetnie. Interpolacja między twarzami mapy kubełkowej mojego głębokiego bufora ramki jest teraz idealna. Z tego powodu ważne jest, aby obsługiwać kąt każdej funkcji trygonometrycznej w radianach.
Ponadto zdałem sobie sprawę, że możesz obliczyć swoją matrycę widoków, tak jak powiedziałem w pytaniu i w ten sposób:
To podejście oznacza, że Twój punkt widzenia znajduje się w centrum światła punktowego, a ty po prostu renderujesz w każdym kierunku mapy cubes, ale jakie są te kierunki? cóż, te kierunki są obliczane, dodając każdy cel, który mam w bloku przełączników (zgodnie z twarzą każdej mapy kubaturowej) z pozycją twojego światła punktowego .
Co więcej, nie jest konieczne odwracanie współrzędnej Y matrycy projekcji , w tym przypadku jest ok, można wysłać macierz perspektywy rzutowania punktowego do twojego modułu cieniującego GLSL bez skalowania go o (1, -1, 1), ponieważ pracuję z tekstur, które nie mają odwróconej współrzędnej Y , myślę, że powinieneś odwrócić współrzędną Y matrycy rzutowania światła punktowego tylko, jeśli pracujesz z odwróconą współrzędną Y tekstury , aby uzyskać prawidłowy efekt odwzorowania cienia we wszystkich kierunkach.
Na koniec zostawię tutaj ostateczną wersję mojego dookólnego algorytmu mapowania cieni po stronie CPU / GPU. Po stronie procesora wyjaśnię każdy krok, który musisz zrobić, aby obliczyć poprawną mapę cienia dla twarzy każdej mapy. Z drugiej strony, po stronie GPU, wyjaśnię moduł cieniujący wierzchołki / fragmenty mojego programu głębokości i funkcję mapowania dookólnego w moim głównym module cieniującym fragmentów, aby pomóc komuś, kto mógłby nauczyć się tej techniki lub rozwiązać przyszłe wątpliwości dotyczące tego algorytmu :
procesor
W funkcji renderMeshDepth:
GPU
Program do głębokości Shader wierzchołków:
Moduł głębi fragmentu programu:
Funkcja dookólnego mapowania cieni w moim głównym module cieniującym fragmenty:
Tutaj masz ostateczne renderowanie algorytmu
Baw się dobrze kodując piękną grafikę, powodzenia :)
CZ
źródło