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# 에서 기본으로 제공 하는것 보다 얼마나 더 빠른지는 비교해 보지는 못했습니다. ( 귀찮아서.. -,.- )
시간 있으면 원본을 정독해서 완전히 이해해 봤으면 하네요. 언제 시간이 좀 나겠죠.~
혹시 이런 방법보다 더 효율적 인게 있으면 같이 공유 합시다~
자.. 그럼 오늘은 이만.
꾸벅.
이세훈
'프로그래밍' 카테고리의 다른 글
C# WPF TreeView에서 상위 TreeViewItem 얻어오기 (8) | 2012.08.10 |
---|---|
cocos2d 윈도우 개발 환경 만들기 ( cocos2d for window ) (9) | 2012.07.14 |
PC에서 3D 입체 영상 게임 개발하기 #4 (1) | 2012.07.09 |
C++에서도 coroutine & yield (3) | 2012.07.02 |
그래픽스 프로그래머 (17) | 2012.06.28 |