눈팅하는 게임개발자 블로그
OpenGL Lighting 3-4 Lighting maps 본문
원본 사이트
learnopengl.com/Lighting/Lighting-maps
번역 사이트
Lighting maps
이전에는 빛을 다르게 반사하는 고유의 재질(material)을 가지고 있는 오브젝트에 대해 다루었다.
이는 빛이 존재하는 scene에서 이는 각 오브젝트들이 고유하게 보일 수 있도록 해주지만 여전히
오브젝트를 시각적으로 출력함에 있어서 유연성을 가지지는 못한다.
일반적으로 실세계의 오브젝트들은 하나의 재질로 이루어져 있지 않고 여러가지 재질을 가지고 있다.
예를 들면 자동차의 외부는 빛나는 재질로, 창문은 유리 재질로 빛을 부분적으로 반사시키고.
타이어는 빛나지 않고 specular 하이라이트를 갖지 않는다.
오브젝트 전체에 대해 각자 다른 ambient, diffuse 색상을 가지고 있는 것이다.
따라서 이전까지의 material 시스템은 다소 복잡한 모델에 적용하기에는 부적합하다.
diffuse, specular maps를 이용하여 시스템을 확장할 수 있다.
이들은 오브젝트의 diffuse(간접적으로 ambient도 포함.), specular 컴포넌트를 사용하여
오브젝트를 더 정확하게 묘사할 수 있도록 해준다.
Diffuse maps
오브젝트를 더 정확하게 묘사하기 위한 하나의 방법으로
오브젝트에 대한 각각의 fragment들의 diffuse 색상을 설정한다.
우리는 지금까지 오브젝트의 fragment 위치를 기반으로 하여 컬러 값을 얻을 수 있는 일종의 시스템을 사용해왔다.
이는 이전에 폭 넓게 다루었던 텍스처와 비슷하다.
fragment마다 고유한 색상 값으로 인덱싱할 수 있는 오브젝트를 감싸고 있는 이미지를 사용한는 원리와 같은
단지 동일한 근본적인 원리를 다른 이름으로 부르는 것 뿐이다.
빛이 존재하는 scene에서 이는 텍스처 이미지가 오브젝트의 모든 diffuse 색상들을 나타내기 때문에
일반적으로 diffuse map이라고 불린다.
diffuse map을 보여주기 위해 모서리가 철로 되어있고 속은 나무로 되어있는 다음의 컨테이너의 이미지를 사용한다.
Shader에서 diffuse map을 사용하는 것은 정확히 텍스처를 사용하는 것과 같은 방법이다.
하지만 이번에는 텍스처를 material 구조체 내부에 sampler2D로 저장한다.
이전에 정의했던 diffuse 벡터 변수를 diffuse map으로 수정한다.
또한 ambient 색상은 대부분의 경우 diffuse 색상과 동일하므로 별도로 저장될 필요가 없기 때문에
ambient 색상 벡터를 배제한다.
struct Material {
sampler2D diffuse;
vec3 specular;
float shininess;
};
...
in vec2 TexCoords;
fragment shader에 다시 텍스처 좌표가 필요하므로 추가적인 입력 변수를 선언한다.
다음 fragment의 diffuse 색상 값을 얻기 위해 텍스처를 간단히 샘플링한다.
vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
또한 ambient material 색상을 diffuse material 색상과 동일하게 설정해야 한다.
vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
diffuse map을 사용하기 위한 준비는 여기까지다.
새로운 것은 없지만 시각적인 품질에서 드라마틱한 향상을 얻을 수 있다.
위의 코드가 동작하기 위해 vertex 데이터에 텍스처 좌표를 업데이트하고 vertex attribute에서 fragment shader로
전달해야 한다. 텍스처를 로드하고 적절한 텍스처 유닛에 바인딩해야 한다.
#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;
}
이후 두 개의 VAO 모두 새로운 verte x데이터에 맞추어 수정하고, 컨테이너 이미지를 텍스처로 불러온 후
텍스처 유닛을 uniform sampler에 할당하고 텍스처 유닛에 바인딩한다.
lightingShader.setInt("material.diffuse", 0);
...
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, diffuseMap);
결과는 다음과 같다.
Specular maps
오브젝트의 대부분이 나무로 되어있고 나무는 어떤 specular 하이라이트도 가지지 않기 때문에
위의 결과는 다소 이상해 보일 수 있다.
specular material을 0.0으로 수정하면 해결할 수 있지만 겉면의 철 재질에도 specular 하이라이트가 사라지게 된다.
이제 다른 세기의 specular 하이라이트를 보여줘야할 오브젝트의 부품을 관리해야 한다.
specular 하이라이트를 위한 텍스처 맵을 사용한다.
이는 오브젝트의 각 부분에 specular 세기를 정의하는 흑백 텍스처를 생성해야 한다는 것을 의미한다.
예제의 specular map은 다음과 같다.
Specular 하이라이트의 세기는 이 이미지의 각 픽셀의 밝기로부터 얻을 수 있다.
검정색은 vec3(0.0)의 색상 벡터를 나타내고 회색은 vec3(0.5)의 색상 벡터를 나타낸다.
fragment shader에서 해당 색상 값을 샘플링하고 이 값과 light의 specular 세기와 곱한다.
픽셀이 흰색과 가까울수록 최종 결과가 증가하여 오브젝트의 specular 컴포넌트가 더 밝아진다.
Sampling specular maps
Specular map은 다른 텍스처들과 비슷하므로 코드도 diffuse map 코드와 비슷하다.
적절히 이미지를 로드하고 텍스처 객체를 생성한다.
fragment shader에서 다른 텍스처 샘플러를 사용하기 때문에 specular map을 위한 다른 텍스처 유닛을 사용해야 한다.
그리고 렌더링하기 전에 적절한 텍스처 유닛을 바인딩해야 한다.
lightingShader.setInt("material.specular", 1);
...
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, specularMap);
fragment shader의 material의 specular 컴포넌트를 sampler2D로 변경해준다.
struct Material {
sampler2D diffuse;
sampler2D specular;
float shininess;
};
그리고 마지막으로 fragment의 해당 specular 세기를 가져오기 위해 specular map을 샘플링한다.
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);
결과는 다음과 같다.
철제 부분에만 하이라이트가 생성되는 것을 확인할 수 있다.
'공부한거 > OpenGL' 카테고리의 다른 글
OpenGL Advanced 5-6 Cubemaps (0) | 2020.11.26 |
---|---|
OpenGL Lighting 3-5 Light casters (0) | 2020.11.04 |
OpenGL Lighting 3-3 Materials (0) | 2020.10.22 |
OpenGL Lighting 3-2 Basic Lighting (0) | 2020.10.11 |
OpenGL Lighting 3-1 Color (0) | 2020.10.11 |