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

[셰이더 프로그래밍] 05. 기초적인 조명 쉐이더 (정반사)

by witn331ss 2023. 6. 2.

정반사 (speculat light) 는 난반사광과 달리 한 방향으로만 반사되는 빛으로 

입사각이 출사각과 같은 것이 특징이다!

 

따라서 정반사광의 효과를 보려면 빛이 반사되는 방향에서 물체를 바라보아야 한다.

 

난반사광과 마찬가지로 정반사광을 수학적으로 재현해내는 수학공식이 여러개 있다.

게임업계에서 널리 사용되는 기법은 바로 퐁 모델.

 

퐁 모델은 반사광과 카메라벡터(카메라에서 현재 위치까지 선을 그른 벡터)가 이루는 코사인 값을 구하고

그 결과를 여러번 거듭제곱 하면 정반사광을 구할 수 있다.

 

 

반사광 R 카메라벡터 V가 이루는 각도의 코사인 값을 구하는 것은

 

이전 난반사광에서 했던 계산과 별반 차이가 나지 않는다.

 

법선벡터와 입사광 벡터 대신에

반사광벡터와 카메라 벡터를 쓴다.

A . B = ( a * d ) + ( b * e ) + ( c + f )

 

다만 왜 거듭제곱을 하는 것일까? 이것은 코사인 그래프를 보면 얼추 답이 나온다.

 

그래프를 보면 거듭제곱수가 늘어남에 따라서 코사인 값이 빠르게 줄어든다.

이제 실생활의 빛을 관찰해보자. 정반사광의 폭이 얼마나 될까?

 

난반사에 비해서 많이 타이트하지 않나?

이런 타이트한 정반사광을 재현하기 위해서 코사인 값에 거듭제곱을 하는 것 이다.

 

그렇다면 거듭제곱을 몇번이나 해주는 것이 옳을까?

이건 표면재질에 따라 조금 다른데

 

거친 표면일수록 정반사광이 덜 타이트하기 때문에 거듭제곱수를 줄여줘야한다.

보통 20번 정도 거듭제곱을 하면 괜찮은 결과를 얻을 수 있다.

 

기초설정

난반사와 정반사광 두개가 합쳐져야 제대로 된 광이라고 할 수 있다.

이전에 만든 것에 카메라 벡터가 추가적으로 필요하니

 

새로우 float4 변수를 추가해주자.

그다음 Viewposition으라는 변수 시맨틱을 대입한다.

 

반사광 벡터는 입사광 백터를 법선에 반사시킨 것이기 때문에 지금 가지고 있는 정보에서 해결이 가능하다.

카메라 백터 같은 경우는 입사광 벡터를 구했듯이 카메라 위치에서 현재 위치까지 선을 그어주면 된다.

 

그러기 위해선 카메라 위치를 전역변수로 만들어야 한다.

 

버텍스 쉐이더의 입력 데이터 및 전역변수

전역변수 선언 

버텍스 쉐이더의 출력 데이터

난반사광에서 그랬던 것처럼 버텍스쉐이더에서 정반사광을 계산한뒤 픽셀쉐이더에 전달할 수 있을까?

아쉽게도 그것은 불가능하다.

 

정반사광을 구하려면 코사인 값에 거듭제곱을 해야만하는데

 

거듭제곱을 한 뒤 보간을 한 결과와 / 보간을 한 뒤 거듭제곱을 한 결과의 차이는 매우 크다.

따라서 정반사광 계산은 픽셀 쉐이더에서 해야함으로 

 

이 계산에 필요한 두 방향벡터인 R과 V를 구한 뒤에 픽셀 쉐이더에 전달하도록 하겠다.

버텍스 쉐이더 함수

이제 정반사광을 계산하는데 필요한 두 방향벡터를 구할 것이다.

 

카메라 벡터 같은 경우는 카메라 위치로부터 선을 그으면 되기 때문에

입사광 방향벡터를 구하는 것과 같이 구해준다.

 

다음으로 정반사광의 방향벡터를 구할 차례인데 

이때 빛의 출사각과 입사각이 같다고 말했었다.

 

그래서 반사벡터를 구하는 수학공식이 필요한데 사실 HLSL에서 제공하는 함수로 처리가 가능하다.

reflect는 첫번째 인자로 입사광의 방향 벡터를 받아오고

두번째 인자로 반사면의 법선을 얻어온다

 

 

픽셀 쉐이더 

이제 정반사광을 계산하는데 필요한 두 방향벡터를 구할 것이다.

우선 버텍스 쉐이더의 출력 데이터에서 가져올 두 벡터를 구조체에 추가한다.

 

다시 픽셀쉐이더에서 Reflection 과 ViewDir을 정규화 시켜줄건데 그 이유는

보간기를 거치는 동안 값이 흐트러질 수 있기 때문이다.

 

 

그 다음 두 벡터의 내적을 구한 뒤 거듭제곱을 해준다.