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

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# 에서 기본으로 제공 하는것 보다 얼마나 더 빠른지는 비교해 보지는 못했습니다. ( 귀찮아서.. -,.- )

 

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


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

 

.. 그럼 오늘은 이만.

 

꾸벅.

이세훈

 

 

댓글을 달아 주세요

  1. 이군 2012.07.12 00:18  댓글주소  수정/삭제  댓글쓰기

    쉐이더에서 사용되는 sin, cos 명령어는 하드웨어의 FPU를 써서 한 클럭에 연산될거다라고 (막연히) 생각했었는데 모바일쪽은 그렇지도 않은가보네요. 최적화의 중요성을 상기시켜주는 좋은 글 감사합니다.

    • 이세훈 2012.07.12 08:01  댓글주소  수정/삭제

      저도 명확한 프로파일링 을 안 해봐서 정말 이 방법이 더 빠르다고는 말할수 없겠지만 유니티 문서에는 ios 에서는 sine,pow 같은 수학 함수를 될수 있으면 많이 쓰지 말라고 권하고는 있습니다. 이 참에 회사가면 간단한 테스트라도 해봐야 겠네요.

    • Favicon of https://gamedevforever.com 대마왕J 2012.07.12 08:51 신고  댓글주소  수정/삭제

      오옷 테스트 해봐주세요!!!
      정말 좋으면 이걸로 다 바꿔봐야징!

  2. 이세훈 2012.07.12 14:51  댓글주소  수정/삭제  댓글쓰기

    우 우 음...

    간단한 테스트를 해본 결과로는 ...
    approximation 을 쓰는게 일단 생각 했던 것보다 그냥 cg의 sine 을 쓰는것보다 현저하게 빠른건 아니었습니다. 적어도 desktop 에 서는 gpu 에 따라 차이 는 있었지만 더 느린 경우도 있었습니다. 역시 플렛 폼의 영향을 많이 받는군요. sine 이 gpu 에서 single clock 으로 계산해 주는 gpu 가 데스크톱 에서는 요즘 많기 때문인것 같기도 하고요... (실망 ㅋㅋㅋ)

    하지만 mobile 에서 S2 에 돌려본 결과 최소한 조금 빠르거나 같은 속도가 나오더 군요. 저의 짧은 생각으로는 점점 하드웨어가 발전하면서 이런 함수들도 빨라지지 않을까 생각하고요. (모발쪽도) 이런 approximation 은 좀 옛날 구 모발에서 돌릴때 사용하면 더 좋은 효과가 나지 않을까 싶네요.

    그래서 제가 지금 까지 내린 결과는 모바일에서 s2 급 정도 나 아래가 타겟이면 써 볼 가치가 있다고 사료 됩니다. 하지만 이렇게 custom 함수를 쓰게 되면 여러가지 기계에 대해 최적화된 컴파일을 하기가 힘들어 보여서 딜레마가 좀 생기네요...

    CPU 쪽으로도 사용 가능하니까 시간나면 그쪽도 프로 파일해서 어떤지 봐야 겠네요.

  3. Hybrid 2012.07.14 04:08  댓글주소  수정/삭제  댓글쓰기

    정말 속도를 위해서라면 mod 같은 함수를 이용하기 보다는 테이블을 이용하는게 빠르지 않을까 싶습니다.

    테이블을 사용해도 1차 혹은 2차 보간을 이용하기 때문에 이것도 속도가 얼마나 빠를지, 혹은 더 느릴지는 모르겠지만, 실제로 널리 사용했던/사용하는 방법이니까요. (언제나 그렇듯 최적화는 무조건 테스트, 테스트, 테스트니.... 최근 하드웨어에서는 일단 테스트를 해봐야 알 수 있는...)

    • Favicon of https://gamedevforever.com 대마왕J 2012.07.14 18:49 신고  댓글주소  수정/삭제

      테이블이라 하면 미리 값을 상수로 넣어 놓고 그 사이를 보간하는 방식?

    • 이세훈 2012.07.15 13:30  댓글주소  수정/삭제

      보간이라면.. Interpolation 인가... 하지만. 그렇게 하면 x 값을 테이블 안에 들어가게 하는 방법이 뭐가 있을까요.. Modulation 을 안해도 할수있는 좋은 방법이.있나요

    • 이세훈 2012.07.15 13:34  댓글주소  수정/삭제

      테이블을 이용하는 좋은 예제가 있으면 좀 소계해 주세용~~~

    • Hybrid 2012.07.20 14:52  댓글주소  수정/삭제

      아.. 오랜만에 들어와서 답변이 늦었습니다. 'ㅁ';

      생각해보니까 mod는 좀 용도가 다르군요. mod를 제외한 게산을 태이블로 바꾼다는게 맞겠네요.

      여튼, mod 연산자 자체는 안쓰는 방법으로는 요 문서를 참고해보시면 될 것 같습니다.
      Faster Math Functions
      http://www.research.scea.com/research/pdfs/RGREENfastermath_GDC02.pdf
      (페이지 7을 참고하세요)

      Sony 문서인데, 그 유명한 Spherical Harmonics 문서를 작성한 Robin Green의 글입니다.

    • Hybrid 2012.07.20 14:53  댓글주소  수정/삭제

      근데 뒷북이니지만 이제와서 생각해보면 쉐이더 안에서는 이렇게 하나 저렇게 하나 원래 함수보다 느려지거나 거의 나아지지 않는 것이 맞는것으로 보입니다.

      요즘도 그런지는 모르겠는데, 모바일과 GPU에서는 이런 수학 함수를 CPU 함수처럼 정밀하게 하지 않죠. IEEE의 제한 오차 범위 자체가 다른걸로 알고 있습니다.

      좀 예전 얘기라 요즘은 어떤지 모르겠는데, 이래저래 생각해보면, 그냥 쓰던거 쓰는게 가장 좋은 선택일지도요. @.@

  4. 이아람 2012.07.17 14:52  댓글주소  수정/삭제  댓글쓰기

    와.
    이론으로 접근하려니까 하나도 못알아듣겠.. 네.. 요.. ㅠ
    멘사들의 모임같음 ㅠㅠㅠㅠ