눈팅하는 게임개발자 블로그
OpenGL Lighting 3-3 Materials 본문
원문 사이트
learnopengl.com/Lighting/Materials
번역 사이트
Materials
실제 세계의 사물들은 각자 빛에 반응하는 정도가 다르다.
예를 들어 철로 된 사물은 상대적으로 빛을 많이 반사하고.
나무로 만들어진 사물은 철로 된 사물보다 상대적으로 빛을 적게 반사한다.
또한 어떤 사물은 빛이 작게 퍼져 작은 하이라이트를, 또다른 사물은 빛이 많이 퍼져 큰 하이라이트를 만들기도 한다.
이런 여러가지 유형의 오브젝트들을 시뮬레이션하기 위해 각 오브젝트에 material 속성을 정의해야 한다.
이전까지는 오브젝트의 시각적인 출력을 정의하기 위해 오브젝트의 색상과 빛 색상을 지정하였고.
이제 오브젝트를 묘사할 때 3가지의 lighting 컴포넌트(ambient, diffuse, specular)에 material을 정의할 수 있다.
3가지의 lighting 컴포넌트에 shininess 컴포넌트를 추가하여 필요한 material 속성을 갖출 수 있다.
#version 330 core
struct Material {
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;
};
uniform Material material;
Fragment Shader에서 오브젝트의 material 속성들을 저장하기 위해 struct를 생성한다.
모든 값을 따로 uniform으로 받을 수도 있지만 구조체를 통해 저장하는 것이 더 체계적이다.
해당 Material 구조체는 Phong lighting의 각 컴포넌트들에 대한 색상 벡터를 정의한다.
ambient material 벡터는 오브젝트가 ambient lighting에 대해 어떤 색상을 반사할 것인지를 정의하고
diffuse material 벡터는 diffuse lighting에 대한 색상을 정의한다.
specular material 벡터는 오브젝트가 specular light에 영향을 받아 생성되는 색상을 설정한다.
마지막으로 shininess는 specular 하이라이트의 퍼짐 정도, 하이라이트의 반지름에 영향을 준다.
오브젝트의 material을 정의하는 이 4개의 컴포넌트들로 실세계의 많은 재질(material)들을 시뮬레이션할 수 있다.
다음 이미지로 실세계의 material들이 적용되었을 때 어떤 효과를 내는지 확인할 수 있다.
오브젝트의 material 속성들을 정확하게 지정함으로써 오브젝트에 대한 인식을 바꿀 수 있다.
이제 shader에서 material 시스템을 구현해보자.
Setting Materials
우선 fragment shader에 material 구조체를 생성했으므로 새로운 material 속성을 사용하여 lighting 계산을 바꾼다.
모든 material 변수들이 구조체 안에 저장되어 있기 때문에 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 구조체의 모든 속성에 접근할 수 있다.
이제 material의 색상의 도움을 받아 최종 출력을 계산한다.
오브젝트의 각각의 material 속성들은 각각의 lighting 컴포넌트에 곱해진다.
적절한 uniform을 설정하여 응용 프로그램에서 오브젝트의 material을 설정할 수 있다.
GLSL에서 구조체가 uniform 변수로 사용될 때 특별한 작업을 해주는 것이 아니므로
구조체의 내용을 채우기 위해서 각각의 uniform 변수를 설정해주어야 한다.
구조체 이름으로 접두사를 붙인 uniform 변수를 사용한다.
lightingShader.setVec3("material.ambient", 1.0f, 0.5f, 0.31f);
lightingShader.setVec3("material.diffuse", 1.0f, 0.5f, 0.31f);
lightingShader.setVec3("material.specular", 0.5f, 0.5f, 0.5f);
lightingShader.setFloat("material.shininess", 32.0f);
위의 변수들을 활용하여 오브젝트의 material에 쉽게 영향을 줄 수 있게 되었다.
실행 화면은 다음과 같다.
Light properties
위의 오브젝트는 너무 밝다.
ambient, diffuse, specular 색상들이 모든 광원으로부터 완전하게 반사되기 때문이다.
또한 광원은 ambient, diffuse, specular 컴포넌트마다 각각 세기가 다르다.
이전에는 ambient, specular의 세기를 strength 값을 통해 다르게 함으로써 이를 해결할 수 있었는데.
이번에는 각 lighting 컴포넌트의 세기 벡터를 지정한다.
해당 lightColor를 vec3 코드로 시각화하면 다음과 같다.
vec3 ambient = vec3(1.0) * material.ambient;
vec3 diffuse = vec3(1.0) * (diff * material.diffuse);
vec3 specular = vec3(1.0) * (spec * material.specular);
그래서 각 오브젝트의 material 속성들은 각 light 컴포넌트들의 가장 강한 값을 반환하게 된다.
하지만 ambient 컴포넌트는 최종 색상에 이와같이 많은 영향을 줄 필요가 없으므로
ambient 세기의 값을 낮추어 ambient 색상을 제한할 수 있다.
vec3 ambient = vec3(0.1) * material.ambient;
같은 방법으로 광원의 diffuse, specular 세기에 영향을 줄 수 있다.
이런 빛의 속성을 체계적으로 관리하기 위해 light 구조체를 만든다.
struct Light {
vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
uniform Light light;
광원은 각 lighting 컴포넌트들의 빛에 대해 다른 세기를 가지고 있다.
ambient 빛은 일반적으로 작은 세기로 설정되며
diffuse 빛은 일반적으로 밝은 흰색으로 설정된다.
specular 빛은 일반적으로 가장 강한 세기인 1.0 값으로 유지된다.
또한 빛의 위치 벡터를 struct에 추가한다.
이제 material uniform과 마찬가지로 fragment shader를 수정해준다.
vec3 ambient = light.ambient * material.ambient;
vec3 diffuse = light.diffuse * (diff * material.diffuse);
vec3 specular = light.specular * (spec * material.specular);
다음 응용 프로그램에서 빛의 세기를 설정해준다.
lightingShader.setVec3("light.ambient", 0.2f, 0.2f, 0.2f);
lightingShader.setVec3("light.diffuse", 0.5f, 0.5f, 0.5f); // Scene에 맞는 어두운 빛
lightingShader.setVec3("light.specular", 1.0f, 1.0f, 1.0f);
이제 lighting과 오브젝트의 material의 모든 것을 관리할 수 있다.
결과는 다음과 같다.
오브젝트의 관점에서 보여지는 것을 수정하는 것은 비교적 어렵지 않다.
더 어려운 것을 해보자.
Different light colors
지금까지는 오브젝트의 실제 색상에 영향을 주는 것이 아니라 빛의 색상을 흰색에서 회색, 검정색 사이의 색상을 선택하여
각각의 컴포넌트들의 세기를 다르게 할 뿐이었다.
이제 구조체를 통해 빛의 속성에 쉽게 접근할 수 있기 때문에 색상을 시간이 지남에 따라 수정할 수 있다.
sin, glfwGetTime 함수를 통해 빛의 ambient, diffuse 색상을 수정함으로써 시간이 지남에 따라 빛의 색상을 쉽게 수정할 수 있다.
glm::vec3 lightColor;
lightColor.x = sin(glfwGetTime() * 2.0f);
lightColor.y = sin(glfwGetTime() * 0.7f);
lightColor.z = sin(glfwGetTime() * 1.3f);
glm::vec3 diffuseColor = lightColor * glm::vec3(0.5f); // 영향을 감소시킵니다.
glm::vec3 ambientColor = diffuseColor * glm::vec3(0.2f); // 낮은 영향
lightingShader.setVec3("light.ambient", ambientColor);
lightingShader.setVec3("light.diffuse", diffuseColor);
결과는 다음과 같다.
'공부한거 > OpenGL' 카테고리의 다른 글
OpenGL Lighting 3-5 Light casters (0) | 2020.11.04 |
---|---|
OpenGL Lighting 3-4 Lighting maps (0) | 2020.10.22 |
OpenGL Lighting 3-2 Basic Lighting (0) | 2020.10.11 |
OpenGL Lighting 3-1 Color (0) | 2020.10.11 |
OpenGL Getting Started 2-9 Camera (0) | 2020.09.29 |