꽃미남 프로그래머 김포프가 창립한 탑 프로그래머 양성 교육 기관 POCU 아카데미 오픈!
절찬리에 수강생 모집 중!
프로그래밍 언어 입문서가 아닌 프로그래밍 기초 개념 입문서
문과생, 비전공자를 위한 프로그래밍 입문책입니다.
jobGuid 꽃미남 프로그래머 "Pope Kim"님의 이론이나 수학에 치우치지 않고 실무에 곧바로 쓸 수 있는 실용적인 셰이더 프로그래밍 입문서 #겁나친절 jobGuid "1판의내용"에 "새로바뀐북미게임업계분위기"와 "비자관련정보", "1판을 기반으로 북미취업에 성공하신 분들의 생생한 경험담"을 담았습니다.
Posted by 알 수 없는 사용자

Fast sine function approximation 

근래에 세이더를 짜는동안 vertex shader 에서 sine function 많이 쓰는 경우가 생겼는데, 요놈이 아무래도 모바일 쪽에서는 당연히 빨리 돌아가지 않을 같다는 생각에 여러 가지로 궁리를 하고  찾아 보던 Devmaster 에서 좋은 정보를 찾아서 공유하고자 합니다~

사실 많이 알려져 있을 수 도 있는 방법인데 아직 GDF 에는 안 올라 와 있는것 같아서요.

 

 

~ 그럼 두말 필요 없이 cg 소스 공개~

 

float sine(float x)

{

const float pi = 3.14159265358979323846264338327950288f;

const float pi_2 = 6.28318530717958647692528676655900577f;

const float b = 1.2732395447351626861510701069801f ;

const float c = -0.40528473456935108577551785283891f;

//const float p = 0.225f;

// x 값을 -pi ~ +pi 로 리 맵핑해주기

float k = fmod( abs(x), pi_2 ) - pi;


   //sine 값 계산 

   float y = (b  + c * abs(k)) * k;


    // 더 정확한 계산이 필요 하다면 이부분 이용

// float y = p * ( y *abs(y) -y ) + y;


// x 값이 0 보다 클땐 결과물을 반전 해줘야 함.

   y  *= (step(x,0)*2 - 1);


return y;

}

 

일단 저도 Devmaster 올라온 글을 100% 숙지 하지 않아서 어찌하여 이렇게 짜야 하는지는 이해하지 못했습니다. 오리지널 포스팅은 여기 있습니다.

 

http://devmaster.net/forums/topic/4648-fast-and-accurate-sinecosine/

 

~ 이제 보시면 Devmaster Nick 라는 천재의 approximation 정말 훌륭하다고 있는데요. 가지의 곱하기와 더하기로 아주 훌륭한 sine function 구현 한걸 알수 있죠. 여기서 한가지 주의 해야 하실 거는 Nick sine approximation input 값을 -pi 에서 +pi 하고 있습니다. 그럼 이슈는 어떻게 하면 외에 숫자도 여기에 들어 갈수 있게 멥핑 해주느냐 인데요.

 

위에 보시면 Nick 오리지널 포스팅 에는 없는 부분이 있습니다.

 

바로  요거하고

float k = fmod(abs(x), pi_2) - pi;

 

이것 입니다.

y  *= (step(x,0)*2 - 1);

 

float k = fmod(abs(x), pi_2) - pi;

이것은 x 절대값을 2pi 모둘라 해주고 거기에서 pi 값을 빼주는 작업인데요. 이렇게 하면 일단 x 값이 -pi 에서 pi 값으로 들어옵니다. 여기서 -pi 를 빼주는 이유로 x 0  보다 클때는 sine 결과물 반대가 되야 하기 때문에 나중에 결과물에 -1 곱해줘야 하더군요. (이건 간단하게 sine 그래프만 그려보시면 이유를 아실수 있을꺼라 생각 됩니다.)   이렇게 맵핑 하는 게 최적화를 생각 했을때 잘한 일인지는 모르겠지만 일단 저는 이렇게 해결을 봤습니다. ( 왠지 x 리 맵핑 작업이 sine 값을 계산하는 부분보다 부하가 더 걸릴것 같다는 생각이... -,.- 이 부분은 더 좋은 방법이 있을듯 한데 ...)

 

// for precision boost

// y = p * (y *abs(y) -y) + y;

 

부분은 쓰게 되면 정확도가 많이 올라가는데요. 필요에 따라 수도 있고 수도 있습니다. 경우에는 굳이 정확도가 그리 중요하지가 않아서 쓰지 않았습니다.

 

테스트 해본 결과 제가 필요로 하는 정확도를 만족 시켰고요. 아무래도 cg 에서 제공하는 sine 보다는 훨씬 빠르지 않을까 싶네요. Vertex shader 에서 필요로 했기에 lookup 같은 것을 하고 싶지도 않았는데 아주 좋았습니다. 유니티를 안드로이드 모발에서 테스트 해봤는데 돌아 가더군요. 아무래도 모발이 이런 수학적인 함수가 부담이 많이 갈수 밖에 없기 때문에 모발에서 쓰기에 더욱더 적절하지 않을까 싶네요. ( mobile 에서는 필요에 따라 float 대신에 half 를 써도 무난할 듯 합니다.. )

 

참고로 세이더 뿐만아니라 그냥 다른 분야에도 쓸수 있는 데요, Unity c# 에서 기본으로 제공 하는것 보다 얼마나 더 빠른지는 비교해 보지는 못했습니다. ( 귀찮아서.. -,.- )

 

시간 있으면 원본을 정독해서 완전히 이해해 봤으면 하네요. 언제 시간이 좀 나겠죠.~


혹시 이런 방법보다 더 효율적 인게 있으면 같이 공유 합시다~

 

.. 그럼 오늘은 이만.

 

꾸벅.

이세훈

 

 

반응형
,