지뢰찾기 프로그램

지난 주에는 SoC 아카데미에 잡혀있어야 했는데, 인터넷도 안되고 깔려있는 프로그램도 몇 가지 없어서 너무너무 심심하더군요. 흑;; 결국 지뢰찾기에 매진하게 되었고, 달인의 경지에 오를려는 찰나! ‘이 정도는 나도 만들 수 있겠다!’ 란 생각이 들었습니다.

바로 Visual Studio 를 실행시키고 -_-; 한 두 시간 정도 뚝딱뚝딱 거린 끝에 결국 콘솔용 지뢰찾기를 완성하기에 이르렀습니다. GUI 버젼으로도 만들어볼까 하는 생각이 있어서 게임 코어와 인터렉션 부분을 분리시켜 놓기는 했는데 구차니즘 말기인 요새 상황으로 볼때 GUI 버젼이 정말 나올까 하는 걱정이 조금 –;;

코드는 아래 url 로 가시면 볼 수 있고, Visual Studio 와 gcc (linux/Windows) 에서 정상적으로 컴파일되는 것을 확인했습니다. :) standard c function 을 제외하곤 별 다르게 사용한 게 없어서 아마 다른 플랫폼에서도 잘 실행될거라 생각합니다.

http://trac.unfix.net/browser/snippet/console_mine

p.s) 콘솔에 찍기에는 9×9 사이즈 이상은 너무나도 커져버려서 사이즈를 고정해놨는데 gui 로 옮기게 되면 저 부분에 관련해서 좀 업데이트를 해야겠네요.

sql injection 을 피하기위한 간단한 방법…

sql injection 을 피하기 위해 single quote 를 escape 시켜주는 코드를 작성해야할 필요가 있었는데, 매번 하나하나에 대해 mysql_real_escape_string 을 호출해주는 건 너무나도 번거로웠다. 뭔가 간단하게 처리할 방법이 없을까 하고 찾아봤더니 왠걸! array_map 이라는 마법의 함수를 발견할 수 있었다.

common function 들을 정의해놓은 파일 맨 아래 저 코드를 삽입해버리니 sql injection 따위 이제 두려워할 필요가 없어졌다. 움하하핫!!

p.s) 그냥 array_map 에 stripslashes 나 mysql_real_escape_string 을 사용하게 되면 array 가 넘어온 경우 문제가 생길 수 있어서 약간 수정을 했습니다.

CG: Perspective Projection

HCI 과제 덕에 심심찮게 프로그래밍을 하게 되네요. 첫 과제 였던 3D rotation 관련을 구현하는 것도 상당히 흥미로웠지만, 두번째 과제인 Perspective Projection 를 구현하는 것은 정말 멋진 경험이었다고 생각합니다.

지난 며칠간 꽤나 재밌게 프로그래밍을 했던 관계로 블로그에도 살짝 정리해보는게 어떨까 하는 생각이 들었는데, 막상 쓸려니 내용이 잘 전해질지 의문이네요.

What is the Perspective Projection?

Perspective Projection 이란 아래의 왼쪽 이미지를 오른쪽 이미지 처럼 변화시키는 것을 얘기합니다. 꼭 저렇게 비뚜러진 이미지를 바로잡는것은 아니고, 이미지가 투영되는 면을 변화시키는 것이라고 생각하시면 됩니다.

이해를 돕기 위해 wikipedia 에서 이미지를 하나 가져왔습니다. 아래 이미지의 연보라색 면이 상이 맺히는 곳이라고 할 때, perspective transform 은 그 보라색 면을 이동시킨 것 같은 효과를 주기 위해 사용합니다.

How to get a projection matrix.

기본 적으로 Perspective Transform 을 위한 식은 다음과 같습니다.

homogenious coordinate 를 사용하고 있으니 x’ 와 y’ 에 관한 식은 아래와 같이 바꿔쓸 수 있습니다.

이를 정리하면 다음과 같은 꼴로 만들 수 있고,

우리가 값을 알고 싶은 변수들은 a, b, c, d, e, f, g, h 이렇게 8 개이므로, (x, y) 와 그에 대응되는 (x’, y’) 쌍을 4개만 알고 있으면 projection matrix 를 구할 수 있습니다. 이를 구하기 위한 매트릭스는 아래와 같습니다.

남은 건 8×8 matrix 의 inverse matrix 를 구한 뒤 뒤 쪽의 매트릭스에 곱해주는 것 뿐이군요.

Implementation of Perspective projection

이제까지 Perspective Transform 을 위한 매트릭스에 대해 알아봤습니다. 이제는 실제 구현을 해보는 것만 남았네요. 위에서 알아봤듯이 Perspective matrix 를 구하려면 matrix multiplication 과 inverse 를 위한 인터페이스가 필요합니다.

matrix multiplication 의 경우 서로 곱할 수 있는 형식인지를 체크한 뒤 단순한 계산을 하면 되고, inverse 는 gauss elimination 을 이용 reduced row echelon form 으로 만들어주는 것을 통해 쉽게(?) 구해낼 수 있습니다.

위의 두 가지까지 구현했다면, 이제 warping 만을 구현하면 되겠습니다. 이 warping 은 크게 두가지 방법을 통해 구현할 수 있습니다.

forward mapping

forward mapping 은 말 그대로 src 의 x, y 좌표에 대하 dst 의 x’, y’ 를 계산 한 뒤 값을 채워주는 방식입니다. 간단히 pseudo code 로 표현하면 다음과 같이 표현할 수 있겠네요.

근데 막상 구현을 해놓고 보면 pixel 이 정수단위이기 때문에 아래와 같이 hole 이 발생하는 것을 확인할 수 있습니다.

backward mapping

위에서 얘기한 hole 을 방지하기 위한 방법 중 하나로 backward warping 이란 것이 있습니다. forward warping 에서 src 의 좌표를 기준으로 dst 의 좌표를 계산했다면, backward warping 에서는 dst 의 좌표를 기준으로 src 의 좌표를 계산하게 됩니다.

간단하게 pseudo code 로 표현하면 아래와 같이 되겠습니다.

간단히 코드만 봐도 예상할 수 있겠지만 backward_warping 을 해주게 되면 hole 은 확실하게 없앨 수 있습니다. 결과 이미지는 아래와 같은데, 아주 깔끔한 결과가 나오지는 않았습니다.

forward (or backward) warping with interpolation

forward warping 을 하게 되면 hole 이 생기게 되고, 단순한 backward warping 을 하게 되면 이미지의 화질 저하가 발생하게 되는데, interpolation 을 사용하게 되면 이를 조금 더 개선할 수 있습니다.

전 linear-interpolation 을 사용해보았는데, 설명하기는 복잡하니 관심있으신 분은 저 아래 첨부할 소스를 참고해보시면 좋겠습니다. 결과는 아래와 같이 나옵니다.

우선 interpolation 을 이용한 forward warping 입니다. 복잡하게 하기는 귀찮고 해서 대강 구현했더니, hole 이 줄기는 했지만 여전히 존재하고 있습니다.

다음은 backward warping 에 linear interpolation 을 적용한 결과입니다. hole 도 없고, 보기에 상당히 괜찮아진 것을 확인할 수 있습니다.

소스코드:
https://github.com/Tee0125/snippet/tree/master/perspective_projection

참고자료:

http://en.wikipedia.org/wiki/Perspective_%28graphical%29

http://en.wikipedia.org/wiki/Gaussian_elimination

p.s) 부동 소숫점 연산에서 x – x/x*x = 0 이라는 것이 보장되질 않더군요. 코드 한 줄 줄일려다가 디버깅을 30분동안 해야했습니다. -_ㅜ

OpenGL: texture vs glDrawPixels

openGL 을 사용해서 2D 이미지 데이타를 화면에 뿌려주는 방법은 대강 다음과 같이 세가지로 분류할 수 있는 것 같다.

  1. glBegin(GL_POINTS); glColor3i(…);glVertex3d(x0,y0,0); …반복; glEnd();
  2. texture 로 올려주고, 해당 texture 가 입혀진 quad 을 그려줌
  3. glDrawPixels 를 이용

첫번째 방법이야 그냥 저렇게도 가능하다는거지 실제 저렇게 사용할 일은 없다고 생각되고, 실제 빠르게 화면에 2D 이미지를 그려주기 위해서는 2번째 방법이나 3번째 방법을 사용해야할텐데, 저 중에 어떤 걸 사용하는게 더 좋은 방법인지 확신이 들질 않는다.

우선 화면이 확대되었을 때 texture 를 사용할 경우 GL_LINEAR 등의 기본으로 제공되는 interpolation method 들이 있기 때문에 (약간 Blur 된 결과일지는 모르지만) 더 좋은 품질의 이미지를 얻을 수 있겠고, 화면이 다시 그려질 일이 있을 때 texture 데이타가 다시 전송될 필요가 없다는 장점이 있는 듯 싶지만, (texture 로 등록할 때 이미지 데이타는 비디오 메모리로 옮겨진다.) width 나 height 가 2^x 형태로 표현되어야 한다는 제약이 있다. 이게 만약 이미지가 크지 않다면 큰 문제가 되지 않겠지만 만약 HD Sequence 라면? 1920×1080 을 표현하기 위해 2048×2048 = 4MB 를 사용해야 하므로 반 정도의 공간이 낭비될 수밖에 없다.

glDrawPixels 는 다시 화면을 그려줘야할 때마다 이미지 데이타를 메인메모리->비디오메모리 로 복사해줘야 하는 문제가 있지만 만약 동영상 플레이어등을 만들 때 처럼 빠르게 화면이 전환되는 경우라면 이는 큰 문제가 되지 않을 듯 싶기는 하다. 물론 화면이 멈춰있는 상태라면 얘기가 다를 지 모르겠다. 또 이미지를 실제 크기보다 더 크게 표현할 경우 glPixelZoom 을 이용해 간단히 구현할 수 있지만 실제로는 픽셀 크기만 커지는 효과이지 interpolation 은 일어나지 않으므로 화질은 texture 를 사용할 때에 비해 떨어진다고 할 수 있을 듯…

뭐 하튼 뭘 사용하는게 더 좋은건지 인터넷을 열심히 찾아봤지만 뭐가 더 좋은지에 대한 정확한 답은 찾을 수가 없다. -_-!

p.s) yuv2rgb 변환 같은 것은 cg 를 이용해서 처리할 수 있는 것 같은데… 이 경우 texture 를 사용해야지만 가능 한 듯…

openGL 에 이미 4×4 matrix multiplication 은 구현되어 있으므로 color_matrix 를 사용해서 yuv2rgb 변환을 빨리할 수 있지 않을까 하는 생각도 해봤지만 실제 결과는 참담…