본문 바로가기
코딩/셰이더 프로그래밍 입문

[셰이더 프로그래밍] 06. 물체에 색을 입히는 디퓨즈/스페큘러 맵핑

by witn331ss 2023. 6. 5.

실 세계에서 물체들이 다른 색을 갖는 이유는

물체마다 흡수.반사하는 빛의 스텍트럼이 다르기 때문이다.

 

예를 들면 검정색 표면은 모든 스펙트럼을 흡수해서 검정색인 것이고

햐안색은 모든 스펙트럼을 반사해서 하얀색이다.

빨간색으로 빨강색의 스펙트럼을 반사하고 그외 스펙트럼을 흡수하기 때문에 빨강색이다.

 

-

 

그렇다면 표면이 빛을 흡수하는 성질은 쉐이더에서 어떻게 표현할 수 있을까?

표면 전체가 한 가지 색으로 칠해져있다면 간단히 전역변수를 사용하면 된다.

 

하지만 대부분의 경우 물체의 표면은 다소 복잡한 패턴을 가지고 있어서 각 픽셀하마 색상을 정해줘야 한다.

 

그러므로 표면에서 반사할 색을 이미지로 그린 뒤, 픽셀쉐이더에서 그 텍스쳐를 읽어와 조명 계산의 결과에 곱하면 된다.

 

-

 

 

이전 공부를 살펴보면 난반사광과 정반사광을 따로 구했었다.

그렇다면 난반사광과 정반사광을 합친 결과에 이 텍스쳐를 곱해야할까?

 

인간이 물체를 지각할 수 있는 이유는 대부분 난반사광 덕분이다.

따라서 위 텍스쳐를 난반사광의 결과에만 곱해도 충분하다.

 

이렇게 난반사광에 적용하는 텍스쳐를 diffuse map이라고 부른다.

 

그렇다면 정반사광은 어떻게 해야할까?

 

물론 디퓨즈 맵을 정반사광에 그래도 사용할 수도 있지만

 

다음과 같은 두 가지 이유 때문에 정반사광용으로 스폐큘러맵을 따로 만드는 경우가 많다.

 

#01 난반사광이 반사하는 빛과 정반사광이 반사하는 빛의 스펙트럼이 다른 경우가 있다.

#02 각 픽셀이 반사하는 정반사광의 정도를 조정하는 용도로 스폐큘러 맵을 사용할 수도 있다.

 

이외에도 물체의 색에 영향을 미치는 다른 요소가 존재한다.

그것은 바로 조명의 색상. 흰색 물체에 빨간 조명을 비춘다면 물체가 붉게 보일 것이다.

 

조명의 색은 전역변수로 지정할 수 있다.

 

 

#

 

 

난반사광 = 빛의 색상 * 난반사광의 양 * 디퓨즈맵의 값

정반사광 = 빛의 색상 * 정반사광의 양 * 스페큘러 맵의 값

 

 

##

기초설정

디퓨즈맵과 스폐큘러 맵을 만들기 위해 텍스쳐를 추가해주자.

 

스페큘러 맵은 어떻게 생겼을까? 디퓨즈 맵과 비교해서 살펴보자

 

스페큘러 맵 사이에서 돌판 사이의 틈새가 검은색인 것을 볼 수 있다.

 

따라서 이 틈새는 정반사광을 전혀 반사하지 않는다. (하지만 여전히 난반사광은 존재한다.)

 

스페큘러 맵은 색상값이 아니라 각 픽셀이 반사하는 정반사광의 양이라고 생각하면 된다.

 

 

버텍스 쉐이더

새롭게 추가된 소스만 설명할 것이다.

 

 

 

새로 추가해야할 전역번수가 있나?

새로 추가된 변수는 빛의 색상 그리고 2개의 텍스쳐 샘플러다.

 

텍스쳐 샘플러는 픽셀쉐이더에서 사용하는 것이니 여기서 선언하지 않아도 된다.

빛의 색상 또한 픽셀 쉐이더에서 곱해주면 된다.

 

 

버텍스 쉐이더 입출력 구조체를 살펴보자.

 

픽셀쉐이더에서 텍스쳐매핑을 하려면 UV좌표가 필요하므로 새로 추가해야하며

버텍스버퍼에서 UV좌표를 가져와 픽셀쉐이더에 전달해야한다.

 

그러므로 인풋 아웃풋 두군데 모두 float2 mUV : TEXCOORD0; 를 추가해주자.

 

그 다음 버텍스쉐이더 함수에 UV좌표를 전달해주는 코드를 추가해준다

Output.mUV = Input.mUV;

 

 

픽셀쉐이더

먼저 소스코드부터 살펴보자

sampler2D DiffuseSampler;
sampler2D SpacularSampler;

float3 gLightColor;

우선 렌더몽키에서 추가했던 세 변수를 전역적으로 선언한다.

 

float2 mUV : TEXCOORD0;

그 다음 PS_INPUT 구조체에 UV좌표를 추가한다.

 

  float4 albedo = tex2D(DiffuseSampler, Input.mUV);

픽셀쉐이더 함수의 제일 윗줄에서 디퓨즈맵을 샘플링해준다음

 

   float3 diffuse = gLightColor * albedo.rgb * saturate(Input.mDiffuse);

난반사광의 양과 빛의 색상을 곱해야 하니 이전 diffuse 변수를 구하던 코드를 다음과 같이 바꿔준다.

 

 

이제 specular 변수의 거듭제곱을 구하는 코드 바로 밑에서 스페큘러 맵을 샘플링 하자.

      float4 specularIntensity = tex2D(SpecularSampler, Input.mUV);

 

이제 여기에 빛의 색상을 곱하면 된다.

specular *= specularIntensity.rgb * gLightColor;  

 

 

그 다음 ambient에 albedo를 곱해준다.