Skip to main content

프랙탈을 보는 그래픽 프로그램 만들기

42Seoul ft_fract-ol

Published 4 min readMarkdown

fract-ol은 반복되는 모양이 나타나는 프랙탈 패턴을 적절한 색으로 표현하여 창에 디스플레이되도록 하는 그래픽 과제이다. 픽셀과 이미지 그리고 이벤트에 따라 이미지를 업데이트하는 그래픽 라이브러리를 다루는 방법, 그리고 fractal이라는 수학적으로 정의된 패턴을 이해하는 것, 이벤트(키, 마우스 휠 등)에 따라 평행이동, 줌, 색 팔레트, 패턴 선택을 처리하는 방법을 배울 수 있었다. 일단 그래픽 과제인만큼, 결과물을 먼저 보자.

1.그래픽 라이브러리(mlx)

위와 같은 이미지를 화면에 출력하기 위해서는 화면출력의 기본단위인 각 픽셀의 위치와 그 픽셀에 어떤 색을 출력할지, 그리고 그렇게 지정된 픽셀들의 세트인 이미지를 관리하고 이를 실제 os의 window에 표시해주는 라이브러리가 필요하다. 이번 과제 요구조건에 따라 42 커리큘럼에서 사용하는 mlx 라이브러리를 사용했다.

그동안 어렴풋이 짐작했던 컴퓨터 그래픽, 특히 화면에 화려한 그래픽을 출력하는 매우 기초적인 과정이 어떻게 작동하는 지를 한땀한땀 단계별로 배울 수 있는 라이브러리라 생각한다. mlx 라이브러리에서는 mlx를 초기화하고, 생성할 윈도우의 크기를 지정하고, 여기에 출력할 이미지를 생성하고 그 이미지의 x,y 번째 픽셀에 지정한 색을 rgb형식으로 지정하고 이 이미지를 생성한 윈도우에 출력하는 과정을 통해 위와 같은 컴퓨터 이미지가 화면에 출력된다.

또한 키보드 키를 누르거나 놓는 이벤트, 마우스 버튼을 누르거나 놓는 이벤트, 마지막으로 창의 종료버튼을 누르는 이벤트가 발생했을 때 호출할 event hook을 작성하여 해당 이벤트 발생시 위의 영상처럼 색의 종류를 변화시키거나, 줌인 줌아웃, 이동 등을 처리할 수 있었다.

이전까지도 c library의 malloc 등의 외부함수를 사용했지만 이렇게 여러가지 외부함수를 사용할 수 있는 첫 과제라 이 라이브러리로 목표로 하는 동작을 처리하기 위해 어떻게 사용해야하는지 익히는데 상당한 시간이 소요되었다. 특히 mlx는 42 교육과정에서만 쓰이는 것으로 추정되는 라이브러리인데 manual도 부실하고 (linux 버전을 설치하면 볼 수 있다), 검색해서 찾을 수 있는 레퍼런스도 부족하여 적응하는데 쉽지 않았다. 하지만 그만큼 교육적으로 로우레벨에서 어떻게 이미지와 픽셀을 처리하고 이를 화면에 출력하는지를 배울 수 있도록 설계된 것을 느낄 수 있었다.

2.프랙탈 패턴

처음 프랙탈을 화면에 출력한다 했을 때는 막막했다. 검색해서 나오는 fractal들의 이미지를 어떻게 구현하나 싶었는데 과제에서 요구하는 mandelbrot이나 julia set의 경우 수학자들이 찾아놓은 결과물을 구현하는 것이라 생각보다 어렵지 않았다.

간단하게 말하자면 복소평면 위의 점 z(x + yi)를 각 세트에서 주어진 방법으로 여러번 mapping하여 나오는 z_i 값이 몇번의 반복연산(합성함수라 생각하면 편할 것이다)을 거쳐 발산하는 경계를 넘어가는지를 계산하고 이 반복횟수가 낮으면 쉽게 발산하는 부분으로 해당 복소평면을 밝은 색으로, 반복횟수가 높으면 많은 연산을 하는 부분으로 해당 복소평면을 어두운 색으로 칠하게 되면 그 경계가 계속 확대했을 때 동일한 모양이 반복하여 나타나는 성질이 있다.

이 반복하는 연산에서 z -> f(z)의 연산을 어떻게 정의하는지에 따라 그 발산과 수렴의 경계의 모양이 다르게 나타나고 각각의 식을 발견한 사람의 이름을 따 Mandelbrot, julia set이라 한다. 복소평면과 맵핑, 그리고 무한 수열의 수렴 발산 등의 개념은 나에게 친숙한 것이라 수월하게 이해할 수 있었다.

3.이벤트 처리(평행이동, 줌인, 줌아웃)

w,a,s,d 키에 따라 화면을 상하좌우를 표시하도록 이동시키고, 마우스 휠 입력에 따라 줌인 줌 아웃하는 것이 주요 기능이었다. 이를 수월하게 처리하기 위해 화면에 표시할 복소평면의 범위를 x_min, x_inc(픽셀당 증가하는 x값), y_min, y_inc로 저장해놓고 적절한 수치(현재 화면에 표시되는 폭이나 높이의 0.05배, 즉 5%씩 증감하여 표시하도록 했다.

또한 줌인 줌아웃의 경우 처음에는 원점을 기준으로 폭이나 높이의 10%씩 줄이거나 늘리는 방법을 사용했는데 보너스에서 커서의 위치를 중심으로 줌인, 줌아웃을 요구했다. 이는 단순하게 커서가 위치한 픽셀이 나타내는 복소평면의 x,y좌표가 줌인, 줌 아웃 이후에도 동일한 x,y좌표를 동일한 픽셀에 표시하도록 하면 되었다. 평행이동->스케일링->평행이동으로 처리해도 되었지만 그냥 invariant개념을 이용하여 식을 간결하게 정리하여 계산하도록 하니 마우스 스크롤을 확 당겨도 조금 더 부드럽게 움직이는 것을 볼 수 있었다.

수학적인 배경이 없다면 어려울 수 있는 부분이지만, 오래되었더라도 물리학과에서 공부했던 좌표계 변환이었던 만큼 쉽게 처리할 수 있는 기능이었다. 위의 수치를 결정하는데 고민이 있었지만 오랜기간 그래픽 소프트웨어를 개발한 adobe의 프로그램들의 해당기능의 값들을 대략적으로 직접 찾아보고 그와 비슷하게 설정하니 자연스럽고 익숙한 상하좌우 이동, 줌인 줌아웃을 구현할 수 있었다.

4.색 팔레트와 컬러 스페이스

이전까지 RGB로 색을 표현한다는 것을 아마 red, green, blue의 중첩으로 계산하겠지라고 알고 있었다. 프랙탈 위키에 나온 알록달록한 색으로 표현하는 방법을 검색하여 그래픽에서 사용자가 어떻게 색을 인식하는지에 따라 다른 파라미터로 결정되는 컬러스페이스라는 개념을 배울 수 있었다.

Red, green, blue 색의 빛의 세기에 따라 색을 결정하는 simple RGB, 망막의 광수용체의 파장범위에 따라 조정된 xyz colorspace, 그리고 hue, chroma, luminance, 한국어로 하면 색조, 밝기, 휘도(채도?)로 번역되는 미술에서 사용되는 색의 성질의 값으로 색을 표현하는 HCL colorscpace가 있다는 것을 배울 수 있었다. 정말 그래픽이라는 분야가 미술과 광학, 그리고 시신경의 수용체까지 많은 분야의 지식이 모여 발전해왔음을 알게 되었다.

많은 그래픽 라이브러리에 이를 변환하는 함수 들이 있지만 mlx에는 없어 해당 변환 매트릭스를 사용하여 직접 프랙탈의 iteration->HCL->RGB로 색을 이미지로 변환해서 출력해보았다. 하지만 여기에 사용되는 행렬 연산이 많아 이벤트에 따른 이미지 처리속도가 더 떨어짐을 발견하였고 해당 matrix와 매핑을 근사하여 줄이고 줄였더니 연산량이 줄어들고 속도가 다시 회복되었다.

5.동료 평가

현재(cpp04)까지 받은 수많은 평가 중에서 유일하게 공통과정인 이너서클을 완료한 멤버에게 평가를 받은 과제가 이 프랙탈이었다.

숫자 인덱스를 지정해서 쓰는 것이 익숙해져 있었는데 enum이나 매크로를 활용하는 것이 가독성 향상에 좋다는 점, mlx와 같은 외부라이브러리의 경우 어떻게 작동하는지를 문서 등을 통해 더욱 정확하게 알고 사용하는 것이 좋다는 점(mlx ptr 반환, img 수정), 줌인 줌아웃에서 내가 생각했던 이론적이 한계가 아닌 사용자가 다시 줌인할 수 있는 한계를 고려하는 점, 협동작업을 위해 파일명을 더 직관적으로 작성하여 관리하는 점 등 수많은 건설적인 피드백을 받았다.

파일명, enum 처리등은 바로 다음과제인 minishell에서, 그리고 수많은 mlx 에러 처리, 사용자 중심으로 생각하여 구현하는 방법은 miniRT에서 적용하며 코드 가독성, 작업 효율, 프로그램의 편리성 등을 발전시킬 수 있었다.

또한 동료평가, 코드리뷰의 중요성을 다시 한번 깨닫고 내가 평가를 받을 때나 하러 갈 때나 조금 더 집중하여 하나라도 더 배울 수 있게 노력하고 있다.

No comments yet. Be the first to comment.