Point Light & Phong Specular 정리
1. reflect() 내부 원리
GLSL reflect(I, N)의 공식:
R = I - 2 * dot(N, I) * N
기하학적 의미: I를 N 방향 성분과 접선 성분으로 분해한 뒤, N 방향 성분만 뒤집음.
이는 표면(접선면)에 대한 기하학적 대칭(mirror)이지, 단순 부호 반전이 아님.
N↑
| /← R (반사)
| /
─────┼────── (surface)
\
↘ I (입사)
교과서 공식과의 관계:
| 공식 |
I의 방향 |
용도 |
R = I - 2*dot(N,I)*N |
광원→표면 (GLSL reflect) |
셰이더 구현 |
R = 2*N*(N·I) - I |
표면→광원 (교과서) |
수학적 유도 |
I를 -I로 바꾸면 두 공식은 동일한 결과를 가짐.
2. reflect(I, N) == reflect(I, -N)
Normal을 뒤집어도 반사 벡터는 동일:
reflect(I, -N)
= I - 2*dot(-N, I)*(-N)
= I - 2*dot(N, I)*N ← 동일
결론: faceforward로 normal 방향을 바꾸어도 reflection 결과는 변하지 않음.
faceforward는 NdotL 부호에만 영향을 줌.
3. lightDir 컨벤션
Point Light 셰이더에서 사용하는 컨벤션:
vec3 lightDir = normalize(position - light.position); // 광원→fragment 방향
| 변수 |
방향 |
비고 |
lightDir |
광원→fragment |
입사 방향 (GLSL reflect의 I) |
-lightDir |
fragment→광원 |
NdotL 계산 시 사용 |
viewDir |
fragment→카메라 |
normalize(camera.position - position) |
4. 광원이 표면 뒤에 있을 때 (Two-sided Lighting)
Point Light가 Mesh 내부에 있으면 normal이 광원 반대를 가리켜 NdotL < 0 발생.
방법 A: abs(NdotL) — 권장하지 않음
float NdotL = abs(dot(normal, -lightDir));
- diffuse는 해결되지만 specular에서
reflect(-lightDir, normal)이 잘못된 방향을 반환함.
방법 B: faceforward on normal — 부분적 해결
vec3 N_eff = faceforward(normal, lightDir, normal);
float NdotL = dot(N_eff, -lightDir); // 항상 >= 0
vec3 reflectDir = reflect(lightDir, N_eff); // reflect(lightDir, normal)과 동일
- NdotL은 올바르지만 reflection은 동일 (faceforward가 reflect에 영향 없음).
방법 C: incidentDir 보정 — 권장
// dot(normal, lightDir) > 0 이면 reflect로 반대쪽으로 넘김, 아니면 그대로
vec3 incidentDir = lightDir - 2.0 * max(dot(normal, lightDir), 0.0) * normal;
- 삼항 연산자 없이 동일한 효과
incidentDir은 항상 "표면 앞쪽에서 들어오는" 입사 방향이 됨
- 이후
NdotL = dot(normal, -incidentDir), reflectDir = reflect(incidentDir, normal) 사용
5. Point Light Attenuation
float lightDist = length(light.position - position);
float distRatio = lightDist / max(light.range, 0.0001);
float falloff = clamp(1.0 - distRatio * distRatio, 0.0, 1.0);
float attenuation = falloff / (lightDist * lightDist + 1.0);
1 / r² 단독 사용 시 r→0 에서 발산 → + 1.0 으로 안정화
falloff: range 기반 smooth cutoff (range 밖에서 0)
6. 주의 사항
- GLSL 내장 함수
distance()와 변수명 충돌 주의 → lightDist 사용
usampler2D (tGPhong)는 반드시 GL_NEAREST 필터링 적용 필요
- Point Light pass에는 ambient 항 포함하지 않음 (directional pass에서 처리)
Point Light & Phong Specular 정리
1. reflect() 내부 원리
GLSL
reflect(I, N)의 공식:기하학적 의미: I를 N 방향 성분과 접선 성분으로 분해한 뒤, N 방향 성분만 뒤집음.
이는 표면(접선면)에 대한 기하학적 대칭(mirror)이지, 단순 부호 반전이 아님.
교과서 공식과의 관계:
R = I - 2*dot(N,I)*Nreflect)R = 2*N*(N·I) - II를 -I로 바꾸면 두 공식은 동일한 결과를 가짐.
2. reflect(I, N) == reflect(I, -N)
Normal을 뒤집어도 반사 벡터는 동일:
결론:
faceforward로 normal 방향을 바꾸어도 reflection 결과는 변하지 않음.faceforward는 NdotL 부호에만 영향을 줌.
3. lightDir 컨벤션
Point Light 셰이더에서 사용하는 컨벤션:
lightDir-lightDirviewDirnormalize(camera.position - position)4. 광원이 표면 뒤에 있을 때 (Two-sided Lighting)
Point Light가 Mesh 내부에 있으면 normal이 광원 반대를 가리켜
NdotL < 0발생.방법 A: abs(NdotL) — 권장하지 않음
reflect(-lightDir, normal)이 잘못된 방향을 반환함.방법 B: faceforward on normal — 부분적 해결
방법 C: incidentDir 보정 — 권장
incidentDir은 항상 "표면 앞쪽에서 들어오는" 입사 방향이 됨NdotL = dot(normal, -incidentDir),reflectDir = reflect(incidentDir, normal)사용5. Point Light Attenuation
1 / r²단독 사용 시 r→0 에서 발산 →+ 1.0으로 안정화falloff: range 기반 smooth cutoff (range 밖에서 0)6. 주의 사항
distance()와 변수명 충돌 주의 →lightDist사용usampler2D(tGPhong)는 반드시GL_NEAREST필터링 적용 필요