본문 바로가기

Game/Graphics

Learn OpenGL - Lighting : Lighting maps

link : https://learnopengl.com/Lighting/Lighting-maps


Lighting maps


 이전 튜토리얼에서 우리는 빛과 다르게 반응하는 고유한 재료를 갖는 각 물체의 가능성에 대해 논의했습니다.


이는 조명이 있는 장면의 다른 오브젝트와 비교해 각 오브젝트에 고유한 모양을 부여하는데 유용하지만


오브젝트의 시각적 출력에 너무 많은 유연성을 제고하지는 못한다.



 이전 튜토리얼에서 우리는 전체 객체에 대한 material을 정의했지만, 실제 세계의 객체는 일반적으로 단일 material로


구성되지 않았지만 여러 material로 구성되었다. 자동차를 생각해보아라. 외관은 반짝이는 천으로 구성되어 있으며,


부분적으로 주변 환경을 반영하는 창문이 있다. 타이어는 반짝 반짝 빛나고 반사광이 없으며 반짝이는 바퀴가 있다.


자동차에는 또한 전체 대상물에 대해 동일하지 않은 확산 및 주위 색상이 있다. 자동차는 다양한 주변 color/diffuse color를


표시한다. 결국, 이러한 물체는 각기 다른 부분에 대해 서로 다른 물성을 지니고 있다.



 따라서 이전 튜토리얼의 소재 시스템은 단순한 모델을 제외한 모든 모델에 충분하지 않으므로 확산 및 반사 맵을


도입해 이전 시스템을 확장해야한다. 이것들은 우리가 diffuse 그리고 훨씬 더 정밀한 object의 specular component에


영향을 줄 수 있다.






Diffuse maps


 우리가 원하는 것은 각각의 개별 조각에 대해 객체의 분산 색상을 설정하는 방법이다. 객체의 단편의 위치를 기반으로 색상


값을 검색할 수 있는 시스템의 일종인가?



 이것은 아마도 모두 매우 익숙한 소리일 것이다. 솔직히 우리는 그런 시스템을 잠시동안 사용해왔다.


이것은 이전 튜토리얼에서 폭넓게 논의한 텍스처와 비슷하다. 기본적으로 텍스처이다:


우리는 동일한 기본 원칙에 대해 다른 이름을 사용하고 있다. 조각 주위의 고유한 색상 값을 indexing할 수 있는


객체를 감싸는 이미지를 사용하는 것이다. 라이트된 장면에서는 텍스처 이미지가 모든 오브젝트의 확산된 색상을


나타내므로 이를 일반적으로 확산 맵이라고 부른다.



 확산 맵을 보여주기 위해 강철 테두리가 있는 나무 컨테이너의 다음 이미지를 사용한다:




 쉐이더에서 확산 맵을 사용하는 것응ㄴ 텍스처 튜토리얼과 완전히 동일하다. 그러나 이번에는 Material 구조체 내에 sampler2D로


텍스처를 저장한다. 앞서 정의한 vec3 확산 색상 벡터를 확산 맵으로 대체한다.

sampler2D는 불투명한 타입이므로 이러한 타입을 인스턴스화 할 수 없다는 것을 의미하지만, 유니폼으로만 정의할 수 있다. 우리가 이 구조체를 uniform 이외의 것으로 인스턴스화한다면 GLSL은 이상한 오류를 던질 수 있다. 따라서, 같은 불투명한 유형을 가진 모든 구조체에 적용된다.

 주변 색상은 거의 모든 경우 확산 색상과 동일하므로 주변 색상 벡터를 제거하므로 별도로 저장할 필요가 없다:

struct Material {
    sampler2D diffuse;
    vec3      specular;
    float     shininess;
}; 
...
in vec2 TexCoords;

만약 당신이 조금 완고하지만 주변 색을 다른 값으로 설정하고 싶다면 주변 vec3를 유지할 수 있지만 주변 색은 여전히 전체 대상에 대해 동일하게 유지된다. 각 조각마다 다른 ambient 값을 얻으려면 ambient 값만 다른 텍스처를 사용해야한다.

 조각 쉐이더에서 텍스처 좌표가 다시 필요하므로 추가 입력 변수를 선언했다. 그런 다음 텍스처에서 샘플링해 조각의 확산된 색상 값을 검색한다:

vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));  

 또한, 주변 재료의 색상을 확산 재료의 색상과 동일하게 설정하는 것을 잊지 말아라:

vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));

  그리고 그것이 확산된 지도를 사용하는데 필요한 전부이다. 보시다시피 새로운 것은 아니지만 시각적 품질이 크게 향상된다.


이를 작동시키려면 꼭지점 데이터를 텍스처 좌표로 업데이트하고, 이것을 꼭지점 속성으로 조각 쉐이더에 전송하고, 텍스처를 로드한


다음 텍스처를 적절한 텍스처 단위에 바인딩해야한다.



 업데이트된 정점 데이터는 여기에서 찾을 수 있다. 이제 정점 데이터에는 큐브의 정점 각각에 대한 정점 위치, 법선 벡터 및 텍스처


좌표가 포함된다. 정점 쉐이더를 업데이트해 정점 애트리뷰트로 텍스처 좌표를 받아들여 조각 쉐이더로 전달하자:

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;
...
out vec2 TexCoords;

void main()
{
    ...
    TexCoords = aTexCoords;
}  

 두 정점의 정점 애트리뷰트 포인터를 업데이트해 새로운 정점 데이터와 일치시키고 컨테이너 이미지를 텍스처로 로드해라.


컨테이너를 그리기 전에 선호하는 텍스처 유닛을 material.diffuse uniform sampler에 할당하고, 컨테이너 텍스처를 이 텍스처


유닛에 바인딩하려고 한다:

lightingShader.setInt("material.diffuse", 0);
...
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, diffuseMap);

 이제 확산 맵을 사용해 세부 사항을 다시 한 번 강조하고, 이번에는 조명이 추가되어 컨테이너가 실제로 빛나기 시작한다.


이제 컨테이너가 다음과 같이 보일 것이다:









Specular maps


 우리는 물체가 대개 나무로 구성된 컨테이너이기 때문에 반사 하이라이트가 약간 보였음을 알았을 것이다.


우리는 나무가 그러한 반사 하이라이트를 주지 않는다는 것을 알고 있다. 객체의 경면 재질을 vec3 (0.0)으로


설정하면 이 문제를 해결할 수 있지만 컨테이너의 강철 테두리가 반사 하이라이트 표시를 중지하고,


스틸에 일부 반사 하이라이트가 표시된다는 것을 알 수 있다. 다시 말하지만, 우리는 오브젝트의 어느 부분이


specular 하이라이트를 다양한 강도로 표시해야하는지 제어하려고 한다. 이것은 diffuse 맵 토론에 정말 익숙한


문제이다.



 우리는 또한 반사 하이라이트에 대해서만 텍스처 맵을 사용할 수 있다. 이것은 우리가 객체의 각 부분의 specular


intensity를 정의하는 흑백 텍스처를 생성해야한다는 것을 의미한다. specular map의 예는 다음 이미지이다:




 반사 하이라이트의 강도는 이미지의 각 픽셀의 밝기로 검색된다. specular 맵의 각 픽셀은 색상 벡터로 표시될 수 있다.


여기서 검은 색은 색상 벡터 vec3 (0.0)을 나타내고, 회색은 색상 벡터 vec3 (0.5)를 나타낸다. 조각 쉐이더에서


우리는 해당 색상 값을 샘플링하고 이 값에 빛의 반사 강도를 곱한다. 따라서 픽셀이 '흰색'일수록


곱셈의 결과가 높아지므로 물체의 반사 성분이 더 밝아진다.



 컨테이너는 대부분 목재로 이루어져 있고, 목재는 재질로 볼 때 반사 하이라이트가 없어야하므로


확산 텍스처의 전체 나무 섹션이 검은 색으로 변환되었다. 검정 섹션에는 반사 강조가 없다.


컨테이너의 강철 경계는 강철 자체가 균열이 아닌 동안 반사 하이라이트에 상대적으로 민감한


다양한 경면 강도를 갖는다.

기술적으로 목재는 훨씬 더 낮은 빛의 값 (더 많은 빛의 산란)과 덜 충격적이지만, 학습 목적으로 목재가 반사광에 반응하지 않는다고 가정할 수 있지만, spectacle 하이라이트가 있다.

 Photoshop이나 Gimp와 같은 도구를 사용하면 일부 부품을 잘라내 흑백과 흰색으로 변환하고,


밝기/대비를 높여서 확산 질감을 반사 이미지로 변환하는 것이 상대적으로 쉽다.






Sampling specular maps


 specular map은 다른 텍스처와 동일하므로 코드가 확산 맵 코드와 유사하다. 이미지를 올바르게


로드하고 텍스처 객체를 생성해야한다. 같은 조각 쉐이더에서 또 다른 텍스처 샘플러를 사용하기


때문에 specular map에 다른 텍스처 유닛을 사용해야하므로 렌더링 전에 적절한 텍스처 유닛에 바인드하자:

lightingShader.setInt("material.specular", 1);
...
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, specularMap);  

 그런 다음 조각 쉐이더의 재질 속성을 업데이트해 sampler2D를 vec3 대신 해당 반사 요소로 허용한다:

struct Material {
    sampler2D diffuse;
    sampler2D specular;
    float     shininess;
};  

 그리고 마지막으로 specular map을 샘플링해 해당 조각의 해당 specular intensity를 검색한다.

vec3 ambient  = light.ambient  * vec3(texture(material.diffuse, TexCoords));
vec3 diffuse  = light.diffuse  * diff * vec3(texture(material.diffuse, TexCoords));  
vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
FragColor = vec4(ambient + diffuse + specular, 1.0);   

 specular 맵을 사용해 객체의 어떤 부분이 실제로 반짝이는 속성을 가지고 있는지 엄청 상세하게


지정할 수 있으며 해당 강도를 설정할 수도 있다. specular 맵은 diffuse 맵 위에 추가 컨트롤 레이어를


제공한다.

너무 주류가 되고 싶지 않다면 specular 맵에서 실제 색상을 사용해 각 조각의 반사 강도뿐만 아니라 반사 하이라이트의 색상을 설정할 수도 있다. 그러나 현실적으로, 반사면 강조의 색상은 광원 자체에 의해 대부분 결정된다. (실제적으로는 흑백이다 : 강도만 신경 쓰는 이유다)

 이제 응용 프로그램을 실행하면 컨테이너의 재질이 스틸 프레임이 있는 실제 나무 컨테이너의


재질과 매우 유사하다는 것을 분명하게 알 수 있다:



 diffuse 맵과 specular 맵을 사용해 상대적으로 단순한 오브젝트에 엄청난 양의 디테일을


추가 할 수 있다. normal/bump 맵 및 reflection 맵과 같은 다른 텍스처 맵을 사용해 오브젝트에


더 많은 디테일을 추가할 수도 있지만 다음 튜토리얼을 위해 예약할 것이다.




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


오늘도 무사히 튜토리얼 하나를 마쳤다. lighting 파트는 눈에 바로바로 보이니 할맛이 더욱 난다.


나중에 게임을 만들 때에는 아마도 많이 막힐듯싶다. 하지만 반복하면서 공부하는게 좋은 방법이니


마냥 두려워하지는 말자!