Trước hết, tôi muốn nói rằng tôi đã đọc rất nhiều bài viết về ánh xạ bóng bằng cách sử dụng bản đồ độ sâu và hình khối và tôi hiểu cách chúng hoạt động và tôi cũng có kinh nghiệm làm việc với chúng khi sử dụng OpenGL, nhưng, tôi gặp vấn đề khi thực hiện Kỹ thuật lập bản đồ bóng đa hướng sử dụng nguồn sáng một điểm trong công cụ đồ họa 3D của tôi có tên là "EZ3". Công cụ của tôi sử dụng WebGL làm API đồ họa 3D và JavaScript làm ngôn ngữ lập trình, đây là luận án cử nhân Khoa học Máy tính của tôi.
Về cơ bản đây là cách tôi đã triển khai thuật toán ánh xạ bóng của mình, nhưng tôi sẽ chỉ tập trung vào trường hợp đèn điểm vì với chúng tôi có thể lưu trữ ánh xạ bóng đa hướng.
Đầu tiên, tôi chủ động loại bỏ mặt trước như thế này:
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;
}
Thứ hai, tôi tạo một chương trình độ sâu để ghi lại các giá trị độ sâu cho từng mặt sơ đồ khối, đây là mã chương trình độ sâu của tôi trong 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);
}
Mảnh vỡ mảnh:
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);
}
Thứ ba, đây là phần thân của hàm JavaScript của tôi "lưu trữ" ánh xạ bóng đa hướng
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 ...
}
}
Thứ tư, đây là cách tôi tính toán Bản đồ bóng tối đa hướng bằng cách sử dụng sơ đồ khối sâu của mình trong Vertex Shader & Fragment Shader chính của tôi:
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);
}
Mảnh vỡ mảnh:
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;
}
Cuối cùng, đây là kết quả mà tôi nhận được, cảnh của tôi có một mặt phẳng, một khối lập phương và một khối cầu. Bên cạnh đó, quả cầu sáng màu đỏ là nguồn sáng điểm:
Như bạn có thể thấy, tôi có vẻ như sơ đồ khối của framebuffer độ sâu ánh sáng điểm nó không thực hiện một phép nội suy tốt giữa các khuôn mặt của chúng.
Cho đến bây giờ, tôi không biết làm thế nào để giải quyết điều này.