개발 · 컴퓨터공학/LearnOpenGL / / 2024. 11. 17. 17:06

OpenGL Materials 머터리얼 [Learn OpenGL 쉬운 번역]

728x90
반응형
※ 본 포스팅은 learnopengl.com을 번역 및 가감한 포스팅이며, learnopengl에서는 번역 작업 참여를 적극 장려하고 있습니다!
아래 링크에서 원문을 확인할 수 있습니다.

 

Materials

빛을 받으면 재질에 따라서 다르게 보입니다. 철 재질이면 반짝거리고, 도자기 재질이면 거친 표면으로 반짝거리지 않고 나무도 나무만의 재질 느낌이 있습니다. 재질에 따라서 표면의 울퉁불퉁한 정도가 다르니까 빛을 산란 시키는 정도에 따라서 반사광의 반경이 달라지기 때문입니다.

OpenGL에서도 이러한 효과를 주기 위해서 material 속성을 정의하도록 합니다.

 

이전에는 객체의 색상과 빛의 색상을 설정하고, 주변광과 반사광 요소를 조합해서 객체의 모습을 표현했습니다. 표면을 더 정확하게 표현하려면, ambient, diffuse, specular에 각각 색상을 지정할 수 있습니다. 이렇게 해서 색을 세밀하게 조절합니다. 여기다가 shininess까지 추가하면 필요한 material을 위한 속성이 갖춰집니다.

 

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

 

프래그먼트 셰이더에서 표면의 재질 속성을 저장하기 위해 위와 같은 구조체를 만들어 줍니다. 물론 각 속성을 개별 유니폼 값으로 저장할 수도 있지만, 구조체로 저장하는데 더 깔끔하겠죠. 먼저 shader로 넘길 구조체의 레이아웃을 정의하고, 새로 만든 구조체 Material 타입으로 유니폼 변수를 선언합시다.

 

퐁 조명의 각 구성 요소에 대해 색상 벡터를 정의할 겁니다.

ambient material 벡터는 주변광 아래에서 표면이 반사하는 색을 정의하며, 거의 객체 표면의 전반적인 색을 정한다고 보면 됩니다. diffuse material 벡터는 확상되는 빛을 쐬는 쪽의 색상을 정의합니다.

diffuse도 ambient랑 비슷하게 혹은 좀 더? 거의 객체의 원래 색상이 됩니다.

specular material 벡터는 표면의 반사 하이라이트 색을 설정하며, 적당한 빛이면 표면 고유의 색상이 드러나는데, 빛이 쎄면 흰색에 가까워집니다.

마지막으로, shininess 광택은 반사 하이라이트의 산란/반경 즉 하이라이트의 크기에 영향을 줍니다.

 

이렇게 material 구조체를 구성하는 네 가지 요소를 사용해서 여러 실제 재질을 시뮬레이션할 수 있습니다.

 

http://devernay.free.fr/cours/opengl/materials.html

 

OpenGL/VRML Materials

 

devernay.free.fr

 

위 사이트의 표를 가지고 실제 재질 속성과 가깝게 표현할 수 있는 수치 리스트를 적용할 수 있습니다. 아래 이미지를 보면 실제 재질 수치 값들을 큐브에 적용하면 어떤색인 지 알 수 있습니다.

 

https://learnopengl.com/Lighting/Materials

보시다시피, 표면의 재질 속성을 정확하게 지정하면 실제로 그 물체가 연상되지 않나요? 큐브 대신 퀄리티 있는 모델을 사용하면 더 현실감 있게 보이겠습니다. 모델 퀄리티를 높이기 위한 모델 로딩은 이후에 다루겠습니다.

 

하지만 처음부터 직접 객체에 맞는 material 수치를 찾아내기는 힘들 겁니다. 잘못된 재질 설정 하나로 객체의 시각적 품질이 완전히 망가지는 경우도 흔합니다.

이제 셰이더에서 material 시스템을 구현해봅시다.

 

Setting materials

프래그먼트 셰이더에 유니폼 material 구조체를 만들었으니, material 속성 값들을 이용해서 조명 계산을 수정합니다. 유니폼 구조체로 각각의 mateiral의 속성에 접근해서 이용합니다.

 

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 값들을 이용해서 color를 계산합니다. 각각의 material 속성들이 해당하는 조명 요소와 곱해져서 적용됩니다.

 

 

사실 이렇게 유니폼 구조체로 만든게 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);

이렇게 말이죠.

객체가 color값을 띄려면 주변광과 확산광 값이 설정되어야하고, 반사광 요소는 중간 밝기의 색상으로 설정합니다. shininess 광택 값은 32로 유지합니다.

이렇게 애플리케이션에서 객체의 material을 조정할 수 있습니다. 프로그램을 실행하면 다음과 같은 결과가 나옵니다.

 

https://learnopengl.com/Lighting/Materials

근데 좀 빛이 너무 쎈 것 같습니다.

 

Light properties

물체가 너무 밝은 이유는 ambient, diffuse, specular 세 빛이 다 너무 세기 때문입니다. 빛의 세기는 각각 주변광, 난반사광, 반사광의 세기가 다르게 적용되는게 사실 일반적입니다. 이전에는 intensity 값을 사용해서 주변광과 반사광의 강도를 조절했었는데, 이번에는 각 조명 성분에 대한 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);

lightColor의 각 요소들을 통해 객체의 material이 빛의 각 구성 요소에 대한 값으로 변경되어 들어갑니다. `vec3(1.0)` 값들은 각 광원에 따라 개별적으로 조정하는게 좋을 것 같습니다. 지금은 객체의 ambient가 큐브 색상에 미치는 영향이 제일 큽니다. 그런데 사실 ambient의 영향력이 큰 건 빛을 표현하기에 좋은 방향은 아닙니다. 빛의 주변광 세기를 낮은 값으로 설정해서 주변광의 영향을 줄입시다:

 

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

 

같은 방식으로 diffuse 와 specular에도 적용할 수 있습니다. 각각의 조명 요소에 개별적으로 영향을 줄 수 있는 특성을 만들어 놓는 겁니다. material 구조체를 만들었던 것처럼 빛의 각 요소의 세기를 담는 변수를 만듭니다:

 

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

uniform Light light;

광원은 주변광, 난반사광, 반사광 각각 강도가 달라야합니다. ambient 주변광은 색이 너무 강하게 나오지 않도록 보통 낮은 강도로 설정합니다. diffuse 난반사는 실제 빛의 색상으로 설정하는데, 보통 밝은 흰색으로 합니다. specular 반사 성분은 보통 `vec3(1.0)`으로 설정해서 최대 강도로 빛나게 합니다. 구조체에는 또한 빛의 위치 벡터도 추가되어있어 이를 사용합니다.

 

vec3 ambient  = light.ambient * material.ambient;
vec3 diffuse  = light.diffuse * (diff * material.diffuse);
vec3 specular = light.specular * (spec * material.specular);

uniform material 에서 했던 것처럼 프래그먼트 셰이더 안에서 값을 적용하주어야하고.

 

lightingShader.setVec3("light.ambient",  0.2f, 0.2f, 0.2f);
lightingShader.setVec3("light.diffuse",  0.5f, 0.5f, 0.5f); // darken diffuse light a bit
lightingShader.setVec3("light.specular", 1.0f, 1.0f, 1.0f);

애플리케이션에서 빛의 강도를 설정해주어야 셰이더로 전달됩니다.

 

이제 빛이 객체의 재질에 영향을 주는 정도를 조절했고, 출력해보면 아까랑 비슷한 것 같지만, 이번에는 객체의 조명과 재질을 제어해서 자연스럽게 되었습니다.

https://learnopengl.com/Lighting/Materials

이제 객체의 시각적 요소를 변경하는 것이 쉬워졌으니 다른 시도를 해봅시다.

 

Different light colors

지금까지는 빛의 색상이 흰색에서 회색, 검정색으로 빛의 강도만 달랐는데, 이제 빛의 특성에 쉽게 접근할 수 있으니 시간에 따라 빛의 색상을 변경하는 효과를 만들 수 있습니다. 프래그먼트 셰이더에서 이미 모든 설정이 되어 있으니, 빛의 color 값만 바꾸어주면 될 것 같습니다.

 

https://learnopengl.com/Lighting/Materials

이미지를 보면 쏘는 빛의 색을 바꾸니 객체의 빛이 바뀝니다. 빛의 색상이 객체가 반사할 수 있는 색상에 직접적으로 영향을 주기 때문이죠.

 

여기서 `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);

여러 가지 조명과 재질 값을 바꿔 보면서 테스트해봅시다. 

 

 

728x90
반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유