우선, 깊이 맵과 큐브 맵을 사용하여 그림자 매핑에 대한 많은 게시물을 읽었으며 작동 방식을 이해하고 OpenGL을 사용하여 작업 경험을 가지고 있지만 구현에 문제가 있다고 말하고 싶습니다. "EZ3"라는 내 3D 그래픽 엔진에서 단일 포인트 광원을 사용하는 전 방향 그림자 매핑 기술. 내 엔진은 WebGL을 3D 그래픽 API로 사용하고 JavaScript는 프로그래밍 언어로 사용합니다. 이것은 컴퓨터 과학 학사 학위 논문입니다.
기본적으로 이것이 그림자 매핑 알고리즘을 구현 한 방법이지만 전 방향 그림자 매핑을 보관할 수 있기 때문에 포인트 라이트 케이스에만 초점을 맞출 것입니다.
먼저 다음과 같이 전면 컬링을 활성화했습니다.
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;
}
둘째, 각 큐브 맵면의 깊이 값을 기록하기 위해 깊이 프로그램을 만듭니다. 이것은 GLSL 1.0의 깊이 프로그램 코드입니다.
정점 셰이더 :
precision highp float;
attribute vec3 position;
uniform mat4 uModelView;
uniform mat4 uProjection;
void main() {
gl_Position = uProjection * uModelView * vec4(position, 1.0);
}
조각 쉐이더 :
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);
}
셋째, 이것은 전 방향 그림자 매핑을 "보관"하는 JavaScript 함수의 본문입니다.
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 ...
}
}
넷째, 메인 버텍스 쉐이더 및 프래그먼트 쉐이더에서 깊이 큐브 맵을 사용하여 전 방향 그림자 매핑을 계산하는 방법입니다.
정점 셰이더 :
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);
}
조각 쉐이더 :
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;
}
마지막으로 이것은 내가 얻는 결과이며, 내 장면에는 평면, 큐브 및 구가 있습니다. 게다가, 붉은 밝은 구체는 점 광원입니다 :
보시다시피, 포인트 라이트 깊이 프레임 버퍼의 큐브 맵처럼 보이지만 얼굴 사이의 보간이 잘 이루어지지 않습니다.
지금까지 나는 이것을 해결하는 방법을 모른다.