Skip to content

[Summary] Point Light & Phong Specular #39

Description

@Winteradio

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에서 처리)

Metadata

Metadata

Assignees

Labels

documentationImprovements or additions to documentation

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions