실 세계에서, 각 오브젝트는 빛에 다르게 반응한다. 예를들어 철 물건들은 종종 점토로된 꽃병보다 더 더 밝게 빛난다. 그리고 나무 용기는 철로된 용기같이 빛에 똑같이 반응하지 않는다. 각 오브젝트는 또한 specular highlight들에 다르게 반응한다. 몇몇 사물들은 작은 highlight를 만들어내면서 너무 많이 흩어지는 것 없이 빛응ㄹ 반사시키고, 다른 것들은 더 큰 반경을 highlight에 만들면서 흩뿌린다. 만약 우리가 OpenGL에서 몇 가지 사물들의 유형을 재연하려 한다면, 우리는 각 사물들에 특정한 material 특징을 정의해야만 한다.
이전 튜토리얼에서 우리는 한 오브젝트의 시각적 결과를 정의하기 위해 object와 light color를 명시 했었고, ambient specular intensity component와 결합되었다. 오브젝트들을 묘사할 때, 우리는 세 가지 lighting component 들 각각에 대해 material color를 정의할 수 있다. 그 세 가지 lighting component 는 ambient, diffuse and specular lighting이다. 각 요소에 대해 컬러를 명시하여, 우리는 오브젝트의 color output에 대해 꽤 좋은 통제를 갖게 된다. 지금 그러한 세 가지 컬러에 shininess component를 추가하자, 그러면 우리는 우리가 필요한 모든 material properties를 가진다:
#version 330 core struct Material { vec3 ambient; vec3 diffuse; vec3 specular; float shininess; }; uniform Material material;
fragment shader에서 우리는 오브젝트의 material properties를 저장할 구조체를 만든다. 우리는 또한 그것들을 각각의 uniform value들로 저장할 수 있지만, 그것을 구조체로 저장하는 것이 좀 더 정리가 잘된다. 우리는 처음에 구조체의 layout을 정의하고나서 간단히 새로이 만들어진 구조체 타입으로 uniform 변수를 선언한다.
너도 보듯이, 우리는 Phong lighting의 컴포넌트들 각각에 대해 color vector를 정의한다. ambient material vector는 이 오브젝트가 ambient lighting에서 무슨 컬러를 반사시킬지를 정의한다. 이것은 보통 오브젝트의 색과 같다. diffuse material vector는 diffuse lighting의 오브젝트의 색을 정의한다. diffuse color는 (ambient lighting 처럼) 요구되는 오브젝트의 색으로 설정된다. specular material vector는 specular light가 오브젝트에 대해 가지는 컬러를 설정한다. (또는 가능하게 심지어 오브젝트에 특정한 specular highlight color를 반사시킨다) 마지막으로 shininess는 specular highlight의 scattering/반경에 영향을 준다.
오브젝트의 material을 정의하는 이러한 4가지 요소들과 함께, 우리는 많은 실 세계의 재료들을(materials) 재현할 수 있다. devernay.free.fr에서 볼 수 있는 한 테이블은 외부 세계에서 발견된 실제 재료들을 재현하는 몇 가지 재료들의 특성을 보여준다. 다음의 이미지는 이러한 실 세계의 물질들이 우리의 cube에 대해 가지는 효과를 보여준다.
너도 볼 수 있듯이, 한 오브젝트의 material 특성을 정확히 명시하여, 우리가 오브젝트에 대해 가지는 인지를 바꾸는 것처럼 보인다. 효과는 명백미 눈치챌만 하지만, 좀 더 현실적인 결과를 위해, 우리는 결국 cube보다는 좀 더 복잡한 모형들이 필요하다. 다음 튜토리얼 섹션에서, 우리는 좀 더 복잡한 모형들에 대해 이야기 할 것이다.
한 오브젝트에 대해 올바른 materials을 갖는 것은 대개 실험과 많은 경험을 요구하는 어려운 특징이다. 그래서 잘못된 material로 한 오브젝트의 시각적 quality를 완전히 파괴하는 것이 그렇게 흔하지 않은 것은 아니다.
쉐이더들에서 그러한 material system을 구현해보자.
Setting materials
우리는 fragment shader에서 uniform material 구조체를 만들었다. 그래서 다음에 우리는 새로운 material properties에 맞는 조명 계산을 하기를 원한다. 모든 material 변수들이 struct에 저장되어있기 때문에, 우리는 material uniform으로 부터 그것들에 접근할 수 있다:
void main() { // ambient vec3 ambient = lightColor * material.ambient; // diffuse vec3 norm = normalize(Normal); vec3 lightDir = normalize(lightPos - FragPos); float diff = max(dot(norm, lightDir), 0.0); vec3 diffuse = lightColor * (diff * material.diffuse); // specular vec3 viewDir = normalize(viewPos - FragPos); vec3 reflectDir = reflect(-lightDir, norm); float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); vec3 specular = lightColor * (spec * material.specular); vec3 result = ambient + diffuse + specular; FragColor = vec4(result, 1.0); }
너도 볼 수 있듯이, 우리는 우리가 필요한 곳 어디든, 모든 material struct의 properties에 접근한다. 그리고 이번에 material의 color의 도움으로 최종 output color를 계산한다. 그 오브젝트의 material attributes 각각은 그것들의 개별적인 lighting components들과 곱해진다.
우리는 프로그램에서 적절한 uniforms들을 설정하여 오브젝트의 material을 설정할수 있다. 그러나 GLSL에서 구조체는 uniforms을 설정할 때와 관련해서 특별하지 않다. 구조체는 오직 uniform 변수의 캡슐화로서 작동한다. 그래서 만약 우리가 그 구조체를 채우기를 원한다면, 우리는 여전히 개별적인 uniforms들을 설정해야 한다. 그러나 구조체의 이름을 접두사로 써야 한다:
lighting_shader.setVec3("material.ambient", 1.f, 0.5f, 0.31f); lighting_shader.setVec3("material.diffuse", 1.f, 0.5f, 0.31f); lighting_shader.setVec3("material.specular", 0.5f, 0.5f, 0.5f); lighting_shader.setFloat("material.shininess", 32.0f);
우리는 오브젝트가 갖게 할 컬러로 ambient와 diffuse component를 설정하고, 오브젝트의 specular component를 중간 밝기의 컬러로 설정한다. 우리는 specular component가 이 특정한 오브젝트에 너무 강해지길 원하지 않는다. 우리는 또한 shininess를 32로 유지한다. 우리는 이제 쉽게 프로그램의 오브젝트의 material에 쉽게 영향을 줄 수 있다.
프로그램을 실행하는 것은 너에게 이것과 같은 것을 준다:
좋아 보이지 않냐?
Light properties
그 오브젝트는 너무 밝다. 그 오브젝트가 너무 밝은 이뉴는 ambient, diffuse 그리고 specular 컬러들이 어떤 광원으로부터 full force를 가지고 반사되었기 때문이다. 광원들은 또한 그것들의 ambient, diffuse 그리고 specular 컴포넌트들에 대해 개별적으로 다른 강도를 가진다. 이전 튜토리얼에서 우리는 이것을 strength value로 ambient와 specular 강도를 다르게 하여 해결했다. 우리는 유사한 것을 하길 원하지만, 이번에는 lighting 컴포넌트 각각에 대해 intensity 벡터를 명시하여 할 것이다. 만약 lightColor를 vec3(1.0)으로 시각화 한다면, 코드는 이것처럼 보일 것이다:
vec3 ambient = vec3(1.0) * material.ambient; vec3 diffuse = vec3(1.0) * (diff * material.diffuse); vec3 specular = vec3(1.0) * (spec * material.specular);
그래서 각 오브젝트의 material property는 light의 컴포넌트 각각에 대해 full 강도로 반환된다. 이러한 vec3(1.0)값은 각 광원에 대해 또한 개별적으로 영향을 받을 수 있다. 이것이 보통 우리가 원하는 것이다. 지금 당장 그 오브젝트의 ambient 컴포넌트는 완전히 cube의 color를 영향을 주고있다. 그러나, 그 ambient component는 최정 컬러에 그러한 큰 영향을 주어선 안된다. 그래서 우리는 빛의 ambient intensity를 더 낮은 값으로 설정하여 ambient color를 제한할 수 있다:
vec3 ambient = vec3(0.1) * material.ambient;
우리는 같은 방식으로 diffuse와 specular 강도에 영향을 줄 수 있다. 이것은 이전 이전 튜토리얼에서 우리가 했던 것과 유사하다. 너는 우리가 이미 각 lighting component에 개별적으로 영향을 주는 light properties를 만들었다고 말할 수 있다. 우리는 light properties에 대해 material 구조체와 유사한 것을 만들고 싶을 것이다:
struct Light { vec3 position; vec3 ambient; vec3 diffuse; vec3 specular; }; uniform Light light;
한 광원은 그것의 ambient, diffuse 그리고 specular light에 대해 다른 강도를 가진다. ambient light는 보통 낮은 강도로 설정된다. 왜냐하면 우리는 ambient color가 너무 우세해지는 것을 원하지 않기 때문이다. 한 광원의 diffuse component는 보통 우리가 한 빛이 가졌으면 하는 정확한 color로 설정되어 진다. 종종 밝은 하얀 색으로. specular component는 보통 vec3(1.0)으로 완전한 강도로 빛나면서 유지되어진다. 우리가 또한 struct에 light의 position vector를 추가했다는 것에 주목해라.
material uniform처럼 우리는 fragment shader를 업데이트하기를 원한다:
vec3 ambient = light.ambient * material.ambient; vec3 diffuse = light.diffuse * (diff * material.diffuse); vec3 specular = light.specular * (spec * material.specular);
그러고나서 우리는 프로그램에서 light 강도들을 설정하고 싶다:
lighting_shader.setVec3("light.ambient", 0.2f, 0.2f, 0.2f); lighting_shader.setVec3("light.diffuse", 0.5f, 0.5f, 0.5f); lighting_shader.setVec3("light.specular", 1.f, 1.f, 1.f);
빛이 어떻게 모든 오브젝트의 materials들에 영향을 줄지를 모듈화했으니, 우리는 이전 튜토리얼의 output과 같아 보이는 시각적 결과를 얻는다. 그러나 이번에 우리는 조명과 오브젝트의 material에 대해 완전한 통제를 얻었다:
오브젝트의 시각적 측면을 바꾸는 것은 상대적으로 쉽다 이제. 가지고 놀아보자!
Different light colors
지금까지 우리는 하얀색부터 회색 검정색까지 범위의 컬러들을 선택하여 light color들의 개별 컴포넌트의 강도만을 다양하게 하기 위해 light color들을 사용했다. 이것은 오브젝트의 실제 색에 영향을 주지 않았다. (오직 그것의 강도에만 영향을 준다.) 우리는 이제 빛의 properties에 쉬운 접근을 가지고 있기 때문에, 우리는 정말 흥미로운 효과를 얻기위해 시간에 따라 그것들의 컬러를 바꿀 수 있다. 모든 것이 fragment shader에서 설정되기 때문에, 빛의 색을 바꾸는 것은 쉽고 즉시 멋진 효과를 만들어낸다:
너도 볼 수 있듯이, 다른 light color는 크게 오브젝트의 color output에 영향을 미친다. 빛의 색은 직접적으로 그 오브젝트가 무슨 color들을 반사시킬지를 영향을 미치기 때문에 (너는 Colors 튜토리얼에서 기억할지도 모른다.) 그것은 시각적 output에 중요한 영향을 가진다.
우리는 쉽게 light의 ambient와 diffuse color를 sin과 glfwGetTime을 통해 바꾸어 빛의 색을 시간에 따라 쉽게 바 꿀 수 있다:
glm::vec3 lightColor; lightColor.x = sin(glfwGetTime() * 2.0f); lightColor.y = sin(glfwGetTime() * 0.7f); lightColor.z = sin(glfwGetTime() * 1.3f); glm::vec3 diffColor = lightColor * glm::vec3(0.5f); // decreasethe influence glm::vec3 ambientColor = diffColor * glm::vec3(0.2f); // low influence lighting_shader.setVec3("light.ambient", ambientColor); lighting_shader.setVec3("light.diffuse", diffColor);
몇 가지 조명과 material value를가지고 실험해보아라. 그리고 그것들이 어떻게 시각적 output에 영향을 미치는지 보아라. 너는 프로그램의 소스코드를 여기에서 볼 수 있다.
Exercise
- 너는 우리가 이번 튜토리얼 시작에서 보았떤 것처럼 오브젝트들의 개별 materials을 정의하여 실 세계 오브젝트들을 재현할 수 있는가? table의 ambient 값들은 diffuse value와 같지 않다는 것에 주목해라; 그것들은 빛의 강도를 고려하지 않았다. 정확히 그것들의 값을 설정하기 위해서, 너는 같은 output을 얻기위해 모든 빛의 강도를 vec3(1.0)으로 설정해야만 한다 : cyan plastic container의 solution.
lighting_shader.setVec3("material.ambient", 0.f, 0.1f, 0.06f); lighting_shader.setVec3("material.diffuse", 0.f, 0.50980392f, 0.50980392f); lighting_shader.setVec3("material.specular", 0.50196078f, 0.50196078f, 0.50196078f); lighting_shader.setFloat("material.shininess", 25.0f);
위의 링크에 따라 material 값을 설정해주면, 그에 맞는 material을 만들 수 있다.
댓글 없음:
댓글 쓰기