코드를 보니깐 바보같이 shader 작성한 부분이 있어 급히 수정하고
이걸 좋다고 블로그에 올렸어서 어디가 잘못 되었고 어떤 부분을 수정했는지 블로그에 올린다.
PCF를 적용하는 부분에서 shadow cubemap에서 주변 벡터를 샘플링하는 과정에서 생긴 문제인데
기존 코드
vec3 rotatedVectors[8];
vec3 getRotationAxis(vec3 direction) {
vec3 worldUp = vec3(0.0, 1.0, 0.0);
if (abs(dot(direction, worldUp)) > 0.99) {
return normalize(vec3(1.0, 0.0, 0.0));
}
return normalize(cross(direction, worldUp));
}
vec3 rotateVector(vec3 v, vec3 axis, float angle) {
float cosTheta = cos(angle);
float sinTheta = sin(angle);
return v * cosTheta + cross(axis, v) * sinTheta + axis * dot(axis, v) * (1.0 - cosTheta);
}
void generateRotatedVectors(vec3 direction) {
vec3 rotationAxis = getRotationAxis(direction);
float angleStep = radians(0.02);
for (int i = 0; i < 8; i++) {
float angle = angleStep * float(i);
rotatedVectors[i] = rotateVector(direction, rotationAxis, angle);
}
}
float PCFShadowCube(samplerCube shadowMap, vec3 fragToLight, float currentDepth) {
float shadow = 0.0;
generateRotatedVectors(fragToLight);
for (int i = 0; i < 8; i++) {
float closestDepth = texture(shadowMap, rotatedVectors[i]).r;
shadow += (currentDepth - 0.005 > closestDepth) ? 0.0 : 1.0;
}
return shadow / 8.0;
}
가장 문제점은 generateRotatedVectors인데
지금 sample vector생성이 기존 벡터방향에서 한쪽 축 방향으로 angleStep만큼 멀어지며 샘플링을 해준다.
당초 계획은 기존 벡터 중심으로 원을 그리며 가까운 거리의 벡터의 그림자 여부를 평균화해서 계산하는 건데 이러면 안된다.
개선된 코드
vec3 rotatedVectors[8];
vec3 getRotationAxis(vec3 direction) {
vec3 worldUp = vec3(0.0, 1.0, 0.0);
if (abs(dot(direction, worldUp)) > 0.99) {
return normalize(vec3(1.0, 0.0, 0.0));
}
return normalize(cross(direction, worldUp));
}
vec3 rotateVector(vec3 v, vec3 axis, float angle) {
float cosTheta = cos(angle);
float sinTheta = sin(angle);
return v * cosTheta + cross(axis, v) * sinTheta + axis * dot(axis, v) * (1.0 - cosTheta);
}
void generateRotatedVectors(vec3 direction) {
vec3 rotationAxis = getRotationAxis(direction);
float rotationAngle = radians(360.0 / 8.0);
float angleStep = radians(0.1);
for (int i = 0; i < 8; i++) {
vec3 newRotationAxis = rotateVector(rotationAxis, direction, rotationAngle * float(i));
rotatedVectors[i] = rotateVector(direction, newRotationAxis, angleStep);
}
}
float PCFShadowCube(samplerCube shadowMap, vec3 fragToLight, float currentDepth) {
float shadow = 0.0;
generateRotatedVectors(fragToLight);
float closestDepth = texture(shadowMap, fragToLight).r;
shadow += (currentDepth - 0.005 > closestDepth) ? 0.0 : 1.0;
for (int i = 0; i < 8; i++) {
closestDepth = texture(shadowMap, rotatedVectors[i]).r;
shadow += (currentDepth - 0.005 > closestDepth) ? 0.0 : 1.0;
}
return shadow / 9.0;
}
샘플링을 정말 원하는데로 원을 그리며 샘플링 해주려면 각을 변경하는 것이 아닌 축을 회전시켜야한다.
고정된 각도만큼 멀어지고 축을 회전시키며 샘플링 해주고
본래의 각까지 포함하여 9개의 그림자여부를 평균내주었다.
이게 진짜 pcf 적용된 pointlight의 shadow다
그동안 해골물 마시고 있었다.
이제 마지막으로 최적화가 남아있는데
면접이 있어 준비하느라 일이 잠깐 스톱이 되었다.
면접보고 온 후 우리 엔진의 프레임드랍 문제에 대해 기술하겠다.
'Computer Graphics > Vulkan' 카테고리의 다른 글
Vulkan Game Engine - 22. shadowmap에 storage buffer, push constants 적용 (0) | 2025.03.17 |
---|---|
Vulkan Game Engine - 21. 엔진 문제와 해결 방안 고민 (최적화) (0) | 2025.03.12 |
Vulkan Game Engine - 19. skybox개선, 콜라이더 그리기 (0) | 2025.02.28 |
Vulkan Game Engine - 18. 그림자 퀄리티 개선, PCF (0) | 2025.02.17 |
Vulkan Game Engine - 17. Skybox 적용 (0) | 2025.02.17 |