본문 바로가기

Game/Graphics

Learn OpenGL - Lighting : Materials

link : https://learnopengl.com/Lighting/Materials


Materials


 실제 세계에서는 각 객체가 빛과 다르게 반응한다. 예를 들어 강철 물체는 꽃병보다 더 빛나는 경우가 많으며


나무 컨테이너는 강철 컨테이너처럼 빛에 반응하지 않는다. 각 오브젝트는 반사 하이라이트와 다르게 반응한다.


일부 오브젝트는 너무 많은 산란없이 빛을 반사해 작은 하이라이트가 생기고, 다른 오브젝트가 많이 산재해


하이라이트의 반경이 커진다. OpenGL에서 여러 유형의 객체를 시뮬레이트하려면 각 객체에 특정한 재료


특성을 정의해야한다.



 이전 튜토리얼에서는 ambient 및 반사 강도 구성 요소와 결합된 객체의 시각적 출력을 정의하기 위해 객체와


밝은 색상을 지정했다. 객체를 설명할 때 우리는 3개의 조명 구성 요소 각각에 대한 물질 색상을 정의할 수 있다.


: ambient, diffuse, specular lighting


 각 구성 요소에 색상을 지정해 객체의 색상 출력을 세부적으로 제어할 수 있다. 이제 이러한 3가지 색상에


shininess 구성 요소를 추가하면 필요한 모든 재질 속성이 준비됐다:

#version 330 core
struct Material {
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    float shininess;
}; 
  
uniform Material material;

   조각 쉐이더에서는 객체의 material 속성을 저장할 구조체를 만든다. 우리는 또한 그것들을 개별 유니폼 값으로


저장할 수 있지만 구조체로 저장하면 더욱 체계적으로 유지된다. 먼저 구조체의 레이아웃을 정의한 다음 새로 생성된


구조체를 해당 유형으로 사용해 단일 변수를 간단히 선언한다.



 보시다시피 Phong 조명의 각 구성 요소에 대한 색상 벡터를 정의한다. 주변 재료 벡터는 이 객체가 주변 조명 하에서


반사하는 색상을 정의한다. 이것은 일반적으로 객체의 색상과 동일하다. diffuse material vector는  diffuse light 아래에서


오브젝트의 색상을 정의한다. diffuse color는 원하는 물체의 색으로 설정된다. specular material vector는 반사 조명이


오브젝트에 대해 갖는 색상 영향을 설정한다. 마지막으로, 광택은 diffuse highlight의 scattering/radius에 영향을 준다.



 물체의 재질을 정의하는 이 4가지 구성 요소를 사용해 많은 실제 물체를 시뮬레이션 할 수 있다.


devernay.free.fr 에 있는 표는 바깥 세상에서 발견된 실제 재료를 시뮬레이션하는 여러 가지 물성을 보여준다.


다음 이미지는 실제 세계의 여러 자료가 큐브에 미치는 영향을 보여준다:



  보시다시피, 물체의 물성을 정확하게 지정하면 물체에 대한 인식이 바뀌는 것처럼 보인다. 그 효과는 분명히 두드러지지만


가장 현실적인 결과를 얻으려면 큐브보다 더 복잡한 모양이 필요하다. 이 튜토리얼 섹션에서는 더 복잡한 모양에 대해


설명한다.



 물체에 적합한 재료를 얻는 것은 대부분 실험과 많은 경험이 필요한 어려운 일이다. 그렇기에 잘못 배치된 물체에 의해


물체의 시각적 품질을 완전히 파괴하는 것은 드문 일이 아니다.



 쉐이더에서 이러한 재료 시스템을 구현해보자.






Setting materials


 우리는 조각 쉐이더에 균일한 재료 구조체를 만들었다. 그런 다음 새 재료 속성을 준수하도록 조명 계산을 변경하려고 한다.


모든 재료 변수는 구조체에 저장되므로 재료 유니폼에서 액세스 할 수 있다.

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);
}

 보시다시피 필요한 모든 구조체 속성에 액세스할 수 있으며 이번에는 재료의 색상을 사용해 결과 출력 색상을 계산한다.


물체의 재질 속성 각각에 각각의 조명 구성 요소가 곱해진다.



 적절한 유니폼을 설정해 응용 프로그램에서 객체의 재질을 설정할 수 있다. 그러나 GLSL의 구조체는 유니폼을 설정할 때


특별한 점이 없다. 구조체는 유니폼 변수의 캡슐화로만 작동하므로 구조체를 채우고 싶다면 개별 유니폼을 설정해야하지만


이번에는 구조체 이름 앞에 접두어를 붙인다:

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);

 ambient 및 diffuse 컴포넌트를 오브젝트가 갖고 싶어하는 컬러로 설정하고, 오브젝트의 specular 컴포넌트를


보통 밝은 색상으로 설정한다. 우리는 이 특정 물체에 대해 반사 요소가 너무 강하기를 원하지 않는다.


또한, 우리는 shininess를 32로 유지한다. 이제 우리는 어플리케이션에서 오브젝트의 재질에 쉽게 영향을


미칠 수 있다.



 프로그램을 실행하면 다음과 같은 결과를 얻을 수 있다:




Light properties


 그 물체는 너무 밝다. 물체가 너무 밝아지는 이유는 ambient, diffuse 및 specular 색상이 모든 광원에서 완전한 힘으로 반사되기 때문이다.


또한, 광원은 ambient, diffuse 및 specular 구성 요소마다 서로 다른 강도를 갖는다. 이전 튜토리얼에서 우리는 강약 값을 사용해 ambient 및


spective 강도를 변경해 이를 해결했다. 비슷한 것을 하고 싶지만, 이번에는 조명 구성 요소 각각에 대해 강도 벡터를 지정해라.


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); 

  따라서 객체의 각 재질 속성은 광원의 각 구성 요소에 대해 최대 강도로 반환된다. 이러한 vec3 (1.0) 값은 각 광원마다 개별적으로 영향을


줄 수 있으며 일반적으로 우리가 원하는 것이다. 현재 오브젝트의 ambient 컴포넌트는 큐브의 색상에 완전히 영향을 미치지만 ambient 컴포넌트는


최종 색상에 실제로 큰 영향을 주지 않아야하므로 light의 주변 강도를 더 낮은 값으로 설정해 주변 색상을 제한할 수 있다:

vec3 ambient = vec3(0.1) * material.ambient;  

 같은 방법으로 광원의 확산 및 반사 강도에 영향을 줄 수 있다. 이것은 이전 튜토리얼에서 했던 것과 매우 유사하다.


각 조명 구성 요소에 개별적으로 영향을 주는 조명 속성을 이미 만들었다고 말할 수 있다. light 속성을 위한 재료


구조체와 비슷한 것을 만들고 싶다:

struct Light {
    vec3 position;
  
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};

uniform Light light;  

 광원은 ambient, diffuse, specular light의 강도가 다르다. ambient 컬러가 너무 지배적이어서는 안되기 때문에


ambient light는 일반적으로 낮은 강도로 설정된다. diffuse 요소는 일반적으로 빛에 원하는 정확한 색상으로


설정된다. (종종 밝은 흰색 색상) specular 구성 요소는 일반적으로 최대 강도로 빛나는 vec3 (1.0)로 유지된다.


라이트의 위치 벡터를 구조체에 추가했다.



 material 유니폼처럼 조각 쉐이더를 업데이트해야한다:

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); // darken the light a bit to fit the scene
lightingShader.setVec3("light.specular", 1.0f, 1.0f, 1.0f); 

 빛이 모든 물체의 재질에 어떤 영향을 주는지 조정했으므로 이전 튜토리얼의 결과와 비슷한 시각적 출력을 얻는다.


이번에는 조명과 대상의 재료를 완전히 제어했다:




 객체의 시각적인 측면을 변경하는 것은 현재 비교적 쉽다. 양념을 쳐보자!






Different light colors


 지금까지 밝은 색상을 사용해 흰색에서 회색까지 검은 색을 선택하고, 개체의 실제 색상에 영향을 주지 않고 개별 구성


요소의 강도를 변경했다. 빛의 속성에 쉽게 접근할 수 있으므로 시간이 지남에 따라 색상을 변경할 수 있어 정말 흥미로운


효과를 얻을 수 있다. 조각 쉐이더에는 모든 것이 이미 설정되어 있으므로 라이트의 색상을 변경하는 것은 쉽고 즉시


펑키한 효과를 만든다:




 보시다시피 다른 밝은 색상은 오브젝트의 색상 출력에 큰 영향을 준다. 밝은 색상은 오브젝트가 반사할 수 있는 색상에 직접적인


영향을 주기 때문에 시각적 출력에 중요한 영향을 미친다.



 우리는 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 diffuseColor = lightColor   * glm::vec3(0.5f); // decrease the influence
glm::vec3 ambientColor = diffuseColor * glm::vec3(0.2f); // low influence
  
lightingShader.setVec3("light.ambient", ambientColor);
lightingShader.setVec3("light.diffuse", diffuseColor);

  몇 가지 조명 및 재료 값을 시험해보고 실험 결과가 시각적 출력에 어떤 영향을 미치는지 보아라.





** ** ** ** ** ** ** ** ** **


원래 오늘은 축구를 하는 날이라서 축구 보면서 쉬려고 했는데 전반전에 5:0으로 스코어가 너무 차이나서 흥이 깨져 치맥하다가


커피 한잔 마시고 들어왔다. 시간이 남은김에 이번 파트는 짧길래 아까 한 것도 되새길겸 공부했다.


구조체를 만들어서 하니 훨씬 보기 좋고, 나중에 코드를 짤때에도 사용하기 편할 것 같다. 굿굿!