※ 본 포스팅은 learnopengl.com을 번역 및 가감한 포스팅이며, learnopengl에서는 번역 작업 참여를 적극 장려하고 있습니다!
아래 링크에서 원문을 확인할 수 있습니다.
Specular Lighting
이제 정반사(specular) highlight를 추가하면 phong 조명 모델을 완성할 수 있습니다.
diffuse light 처럼 specular 정반사 조명도 빛이 방향 벡터와 객체의 법선 벡터가 필요합니다. 다른 점은 이번에는 관찰자가 프래그먼트를 보는 방향이 추가적으로 필요합니다. 다시 말하면 view 방향이죠. 정반사 조명은 빛의 반사적인 특성에서 나온 겁니다. 객체의 표면이 거울이라 가정하면 정반사 조명은 표면에서 빛이 반사되는 부분이 제일 강합니다.
빛은 법선 벡터를 기준으로 반사되죠. 빛의 방향과 법선 벡터를 갖고 반사 벡터를 계산할 수 있습니다. 그 다음, 반사 벡터와 뷰 방향 간의 각도 θ를 구합니다. 이 각도가 가까우면 정반사 조명이 세겠죠. 정반사의 결과, 표면을 통해서 반사된 빛이 하이라이트를 띄게 됩니다.
정반사 조명을 위해서는 뷰 벡터가 필요합니다. 뷰 벡터는 구하는 법은 관찰자의 월드 공간 위치벡터와 프래그먼트 위치 벡터의 차로 계산합니다. 그 다음 정반사 빛의 강도를 계산하고, 조명의 색과 곱해서 기존 주변광과 확산광 성분에 추가하는 식으로 하면 됩니다.
우리는 조명을 월드 공간에서 계산하지만, 보통은 뷰 공간에서 많이 계산합니다. 왜 그러냐면 뷰 공간에서는 관찰자가 항상 (0,0,0)이라서 관찰자의 위치를 이미 알게 되죠? 하지만 월드 공간에서 계산해보면서 공부를 해보는게 직관적이고 좋습니다. 뷰 공간에서 조명 계산을 하고 싶으면 벡처들을 모두 view matrix로 변환해야하죠(법선 행렬에도 view matrix를 추가해야죠).
관찰자의 월드 공간 좌표는 카메라의 위치 벡터를 가져오면 됩니다. 프래그먼트 셰이더에 uniform을 추가해서 카메라 위치 벡터를 셰이더에 전달합니다.
uniform vec3 viewPos;
lightingShader.setVec3("viewPos", camera.Position);
이제 정반사 강도를 구할 수 있습니다.
먼저 정반사 하이라이트에 0.5라는 중간 정도의 밝기 강도를 줍시다.
float specularStrength = 0.5;
값이 1.0이면 너무 밝으면 코랄색 큐브에는 적당하지 않습니다. 추후에는 이러한 조명 강도에 대해서 객체에 어떤 영향을 미치는지 구체적으로 다뤄보면 좋겠네요.
이제 뷰 방향 벡터와 법선 축을 가지고 반사 벡터를 계산합니다.
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
여기서 중요한 점은 reflect하는 함수를 사용할 때 lightDir가 (-)가 된 겁니다. 왜 반대로 방향을 바꿨냐면 지금 lightDir의 방향은 광원에서 프래그먼트로 향하는게 아니라 반대로 프래그먼트에서 광원을 향하는 방향이거든요. (이전 코드를 뒤져보면 광원벡터에서 프래그먼트 벡터를 빼서 계산했어요)
그래서 lightDir를 (-)로 방향을 반대로해서 적용하고, 두 번째 인자로 법선 벡터를 기준으로 반사하도록 norm을 줍니다.
이제 정반사 성분을 구하는 공식을 적용합니다.
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
vec3 specular = specularStrength * spec * lightColor;
자, 코드를 봅시다.
뷰 방향과 반사 방향의 내적을 계산합니다(max로 음수는 안되도록). 그리고 pow로 32제곱을 합니다. 왜 이렇게 높은 제곱을 하냐면 눈부신 하이라이트 광택을 표현하기 위해서 그럽니다. 이 광택값이 높을수록 하이라이트의 면적이 좁아집니다. 광택 값에 따라서 하이라이트가 어떻게 변하는지 보시죠.
이제 정반사 성분을 주변광과 확산광 성분에 추가해서 결과를 객체의 색상과 곱하면 완성입니다.
vec3 result = (ambient + diffuse + specular) * objectColor;
FragColor = vec4(result, 1.0);
이걸로 phong light model의 모든 조명을 추가해서 완료했습니다.
초기에는 phong light model을 vertex shader에서 구현했었다고 해요. 그렇게하면 프래그먼트보다 정점의 수가 적으니까 조명 계산을 효율적으로 할 수 있다고 합니다. 하지만 vertex shader에서 계산한 색상은 그 정점에서만 색을 나타내고 주변 프래그먼트의 색은 자동으로 보간된 색상으로 결정됩니다. 그러면 정점이 엄청 많은게 아닌 이상 현실적인 조명을 표현하지는 못했죠.
사실 방금 말한 vertex shader에서 phong light 를 구현한건 phong shading이 아니라 gouraud(고러드) shading이라고 해요. 사진으로보면 조명이 좀 어색한 감이 있죠. 반면에 phong은 부드럽고요.
이제 shader가 얼마나 강력한 도구인지 알았나요? shader는 몇 가지 정보만 이렇게 넘겨주면 조명이 프래그먼트 색상에 미치는 효과를 계산해서 표현할 수 있답니다. 다음에는 조명 모델을 더 활용해보도록 합시다.
'개발 · 컴퓨터공학 > LearnOpenGL' 카테고리의 다른 글
OpenGL Diffuse Lighting 디퓨즈 확산광 조명 [Learn OpenGL 쉬운 번역] (10) | 2024.09.10 |
---|---|
OpenGL Basic Lighting 조명 [Learn OpenGL 쉬운 번역] (16) | 2024.09.09 |
OpenGL Lighting Scene 조명 씬 구현 [Learn OpenGL 쉬운 번역] (12) | 2024.09.08 |
OpenGL Color 색 [Learn OpenGL 쉬운 번역] (6) | 2024.09.07 |
OpenGL Camera Zoom 카메라 줌 [Learn OpenGL 쉬운 번역] (21) | 2024.09.06 |