https://en.wikipedia.org/wiki/Phong_reflection_model
- Ambient -> 색 그 자체
- Diffuse -> 물체의 표면이 거칠어서 빛이 난반사된다고 가정
- Specular -> 반짝반짝한 질감, 매끈한 표면
- alpha
- ks
Ambient
그냥 색 그 자체 그대로.
Diffuse (view independent)
표면상의 한 지점이 얼마나 많은 빛을 받는가?
-> normal vector 와의 각도가 작을수록 빛을 더 많이 받는다. (해가 중천에 떳을때 가장 빛이 센걸 생각해보면 됨)
-> 즉, normal vector와 빛의 들어오는 벡터와의 각도에 따라 빛의 양을 조절하면 됨.
-> 각도가 0일때 가장 값이 크고, 각도가 커질때 값이 작아지는 함수 사용하면 되겠다!
-> cosθ! -> 그런데 코사인은 컴퓨터에서 계산할때 느려짐 -> 식 유도 과정에서 코사인이 사라짐
-> 90도가 넘으면 값이 0이 되어야 함
-> max(cosθ, 0.0f)
-> 둘 다 유닛 벡터이면 다음이 성립함 -> cosθ = n dot l
-> max(n dot l, 0.0f)
// Diffuse
// const vec3 dirToLight = ...
const vec3 dirToLight = glm::normalize(light.pos - hit.point);
// 빛이 90도가 넘어가면 빛이 음수가 되지 않도록 clamp
const float diff = glm::max(glm::dot(hit.normal, dirToLight), 0.0f);
Specular (view dependent)
정반사 되는 벡터를 구한다.
보는 사람의 각도도 고려해야 한다.
물체에서 보는 사람의 시선쪽으로의 벡터와, 반사된 빛의 벡터의 각도에 코사인을 씌우면
- 세타가 0일때 => 가장 강한 빛
- 세타가 90일때 => 가장 약한 빛
- max함수를 씌워서 음수일때는 0이게 하면 됨
그런데, Diffuse 때 했던것과 마찬가지로, 두 벡터가 모두 normalize된 벡터라면, 둘을 dot product하면 코사인 값을 구할 수 있음.
const vec3 reflectDir = glm::normalize(2.0f * glm::dot(hit.normal, dirToLight) * hit.normal - dirToLight);
const float specular = glm::max(glm::dot(-ray.dir, reflectDir), 0.0f);
그런데 우리가 원했던 Specular는 빛이 강렬하게 반짝거리는 느낌(마치 눈알에 하이라이트 들어가듯이)인데 우리가 만든 구체는 흐리멍텅한 느낌임. -> 더 강렬하게 빛이 모이게 만들 요소가 필요하다.
https://www.scratchapixel.com/lessons/3d-basic-rendering/phong-shader-BRDF/phong-illumination-models-brdf.html
n만큼 제곱을 해 주면 된다.
보통 phong 모델의 specular에 몇 제곱을 할 것인가(그림에서 n)는 alpha라는 수로 표현함
const vec3 reflectDir = glm::normalize(2.0f * glm::dot(hit.normal, dirToLight) * hit.normal - dirToLight);
const float specular = glm::pow(glm::max(glm::dot(-ray.dir, reflectDir), 0.0f), sphere->alpha);
이런식으로 빛이 더 잘 모여 보임
Ambient + Diffuse + Specular