42Seoul

FdF - Mandatory

millar 2023. 8. 14. 18:59

FdF

The representation in 3D of a landscape is a critical aspect of modern mapping.

For example, in these times of spatial exploration, to have a 3D representation of Mars is a prerequisite condition to its conquest.

As another example, comparing various 3D representations of an area of high tectonic activity will allow you to better understand these phenomena and their evolution, and as a result, be better prepared. It’s your turn today to modelize some 3D magnificent landscapes, imaginary or not.

FDF is short for ’fil de fer’ in French which means ’wireframe model’.

개요

 드디어 2 써클의 마지막, 그래픽 과제입니다. 동료가 그래픽 과제로 FdF를 선택하여 푸는 모습을 보았는데, 재밌어 보였습니다. 대단한 것을 만드는 것 같고 무엇보다 표현되는 그림이 성취감을 자극하더군요. 하지만 이렇게 어려운 줄 알았겠습니까?

 

 저는 그래픽 과제에 환상이 있었습니다. 원래 그림을 표현한다는 것은 그리는 행위이지만, 컴퓨터로 그림을 표현한다는 것은... 수학 문제를 푸는 것이었습니다. 컴퓨터로 그림을 그린다고 해서 달라지는 건 없더군요.

 

 FdF를 선택한 이유는 할만하다고 생각했고, 가장 그래픽 과제다웠기 때문입니다. so_long은 게임을 만드는 것에 가깝고 올바른 맵인지 확인하는 데에 괴로워하는 주변인들을 보아왔기 때문에 선택하지 않았고, fract-ol은 너무 어려워 보였습니다.

 

목표

It’s time for you to create a basic computer graphics project! You are going to use the school graphical library: the MiniLibX!

This library was developed internally and includes basic necessary tools to open a window, create images and deal with keyboard and mouse events.

This will be the opportunity for you to get familiar with the MiniLibX, to discover the basics of graphics programming, especially how to place points in space, join them and, most important, how to see the scene from a specific viewpoint.

MiniLibX를 사용하여 키보드 마우스에 반응하는 3차원 그래픽 프로그래밍

 

 과제의 요구 조건을 살펴봅시다.

1. MiniLibX를 반드시 사용해야 합니다.

2. 그림을 3차원으로 표현하는 방법은 등각 투영법(평행 투영법)입니다.

3. 프로그램명은 fdf이고, 맵의 좌표 정보가 기록된 파일은 ".fdf" 파일입니다. 따라서 다음과 같이 실행될 것입니다.

  • >./fdf 42.fdf

4. 프로그램은 윈도우에 이미지를 나타내야 합니다.

5. 윈도우는 정상적으로 작동해야 합니다. (다른 창으로 전환, 최소화 등)

6. 키보드의 ESC 키를 누르면 윈도우 창과 프로그램이 올바른 방법으로 정상 종료되어야 합니다.

7. 윈도우 상단 프레임 좌측의 'X' 표시(quit)를 클릭했을 때 역시, 올바른 방법으로 정상 종료되어야 합니다.

8. MiniLibX의 이미지를 사용하는 것이 의무입니다. *이미지 파일이 아니라, image 함수를 의미합니다.

 


구현 전 사전 지식

1. MiniLibX

2. 등축 투영법


1. MiniLibX

https://bigpel66.oopy.io/library/c/etc/3

 

MiniLibx

MiniLibX에 대해서 알아보자.

velog.io

 MiniLibX는 그래픽 프로그래밍을 위한 간단한 그래픽 라이브러리입니다. 주로 컴퓨터 그래픽스 및 GUI (그래픽 사용자 인터페이스) 애플리케이션 개발에 사용됩니다. C 프로그래밍 언어로 작성되었으며, 학생들에게 그래픽 프로그래밍 기초를 가르치기 위해 개발되었습니다. 라고 42에서 하네요

 

 그래픽 라이브러리인 만큼, 윈도우 창을 생성하고 이미지를 띄우거나 픽셀에 점을 찍어 그림을 표현할 수 있습니다. 키보드와 마우스 클릭 등 키 반응을 감지하여 이벤트를 줄 수 있습니다.

 

 내용도 크고 제가 학습한 내용을 적자니 참고한 페이지와 다를 게 없으므로, 도움이 되었던 링크를 남겨놓겠습니다. 응용은 구현에서 설명해 드리도록 하겠습니다.

 

2. 등축 투영법

 우선 Isometric Projection을 찾아보면 등축 투영법이라고 나옵니다. 그런데 과제를 풀려고 구글을 돌아다니다 보면 등각 투영법인지 등축 투영법 인지 헷갈릴 정도로 두 표현이 같이 등장하는데, 영문으로 검색할 때 등축 투영법이라고 나오고 위키백과에 등각 투영법에 대해서는 따로 자료 페이지가 없으므로 등축 투영법이라 표현합니다.

 

 GPT는 이렇게 말하긴 합니다만... 한국어로 번역되면서 해석의 차이가 있었을까요? 등각이든 등축이든 Isomertic Projection으로 표현합시다.

  • 등각 투영법 (Isometric Projection): 등각 투영법은 3차원 물체를 각도가 동일한 세 개의 축을 사용하여 2차원 화면에 투영하는 방법입니다. 이때, x, y, z 축 간의 각도는 120도로 동일하게 유지됩니다. 따라서 등각 투영법을 사용하면 물체의 세 가지 축을 모두 보여주는 형태로 투영됩니다. 등각 투영법은 물체의 비율을 일정하게 유지하며, 물체 간의 상대적 크기를 더욱 정확하게 나타낼 수 있습니다.
  • 등축 투영법 (Orthographic Projection): 등축 투영법은 3차원 물체를 각각의 축을 따라 수직으로 투영하여 2차원 화면에 나타내는 방법입니다. 이때, 각각의 축은 서로 수직이게 투영됩니다. 등축 투영법은 물체의 실제 크기와 형태를 유지하면서 각도와 거리에 따른 왜곡 없이 투영할 수 있습니다. 그러나 이 방법은 물체의 깊이와 차원을 잃게 됩니다.

 

 회전 변환 행렬을 통해 그림을 등각 투영법의 각도로 회전시켜 2차원 윈도우에 표현해야 합니다. 회전하기 위해서는 회전 변환 행렬에 대해서 알아야 합니다. 잘 정리된 글이 있으므로 직접 읽어보고 회전각을 구해서 좌표를 회전해 보세요. 이후 miniRT와 Cub3D에서 많은 도움이 된다고 합니다!

https://gaussian37.github.io/math-la-rotation_matrix/

 

회전 변환 행렬 (2D, 3D)

gaussian37's blog

gaussian37.github.io


구현

mars.fdf

 소스 코드 파일도 많고 구현 자체를 너무 방대하게 잡고 진행한 터라, 일부 코드만 보이고 설명하는 것은 비효율적이라 생각해서 핵심적으로 다루어야 하는 좌표와 회전을 어떻게 진행했는지 설명하겠습니다. 과제 업데이트가 좀 있는 모양이라 pdf와 제공 파일등으로 평가를 정확히 구현 방향을 정하기가 어렵습니다. 코드 설명 후 이 부분에 관해서 이야기하겠습니다.

 

code

typedef struct s_map
{
	double	x;
	double	y;
	double	z;
	int		color;
}	t_map;

t_map	**initial_map(t_file *info)
{
	t_map	**map;
	char	**coordinate_array;
	int		row;
	int		col;

	info->fd = open(info->filename, O_RDONLY, 0644);
	map = (t_map **)malloc(sizeof(t_map *) * info->limit_row);
	row = -1;
	while (row++ < info->limit_row - 1)
	{
		col = -1;
		map[row] = (t_map *)malloc(sizeof(t_map) * info->limit_col);
		coordinate_array = one_coordinate_line(info);
		while (col++ < info->limit_col - 1)
			map[row][col] = coordinate(row, col, coordinate_array, info);
		free_split(coordinate_array);
	}
	close(info->fd);
	return (map);
}

 맵 파일 행렬의 개수를 세고 2차원 배열을 만들 겁니다. 맵 파일 그 자체로 좌표를 만드는 것이죠. 행만큼의 포인터와 열만큼의 구조체를 생성합니다. 그리고 파일을 읽어 x, y, z 좌표와 color 좌표를 받습니다. 과제에서는 color에 대한 언급이 없으나, test_maps에 존재하는 파일에는 색 데이터가 존재하는 맵 파일이 있습니다. 그래서 "특정 맵은 색이 뚜렷하게 나타나는데, 언급은 없으니까? 구현은 하지 않아도 되지 않나?"라고 생각할 수도 있습니다. 물론 색이 없으면 그림을 표현할 수 없으므로, 그리고 화면에 픽셀을 찍는 함수도 색이 필요하므로default 색값을 정하고 단색 그림을 표현해도 저는 할 말이 없습니다만, 단색으로 하시는 분들은 보지못했습니다... 저는 색 표현을 하지 않아도 되겠다고 생각을 하지는 않았지만, 색 데이터를 파싱하는 걸 귀찮아하시는 분들이 있어서 말씀만 드립니다. 그냥 하시는 게 평가에서 편하지 않을까...

 

void	isometric_projection(t_map **map, t_file *info)
{
	int		row;
	int		col;
	double	theta;

	row = 0;
	while (row < info->limit_row)
	{
		col = 0;
		while (col < info->limit_col)
		{
			theta = M_PI / ANGLE_Z;
			rotate_std_z(&map[row][col], theta);
			theta = M_PI / ANGLE_Y;
			rotate_std_y(&map[row][col], theta);
			++col;
		}
		++row;
	}
}

 모든 좌표에 대해서 z축을 기준으로 그림을 반시계 방향으로 돌립니다. 그림의 중심을 기준으로 좌우 대칭이 되었으면 그림을 뒤로 눕혀야 하겠죠. 그다음 Y축을 기준으로 그림을 반 시계로 돌리면 뒤로 눕힐 수 있습니다.


동료 평가

1. MiniLibX을 어떻게 활용하였는지 함수들에 대해서 숙지하고 설명할 수 있어야 합니다.

2. Isometric Projection & 회전 변환 행렬에 대해서 설명할 수 있어야 합니다.

 

Defense

 과제 페이지에서 제공하는 원본 fdf프로그램은 이러한 특징이 있습니다.

 1. 맵(.fdf)파일은 반드시 사각형이 아니어도 됩니다. 첫 행의 길이(열)가 기준입니다. 하지만 생성되는 맵(Isomertic Projection 하기 전)은 왜 항상 사각형으로 만들어질까요?

  • 첫 행 이후의 행들은 첫 행의 길이를 따라갑니다. 만약 첫 번째 행이 길이가 10(열 요소가 10개)이었다면, 다음 행들은 길이가 10 이상일 경우 10으로 고정되어 맵에 반영됩니다.
  • 첫 행의 길이보다 짧은 행이 존재한다면 그릴 수 없는 맵으로 판단합니다.

2. 맵의 요소는 atoi로 처리됩니다.

  • atoi 함수는 숫자로 판단할 수 있는 자리까지만 숫자로 만들고, int 범위를 넘어간 수는 오버플로우 됩니다. 수가 아닌 문자로 시작하는 문자열은 0을 반환합니다.
  • 맵 파일의 나타나는 수(자릿수 X)들은 z 좌표를 의미합니다. 이 z좌표를 문자로 바꾸어도 0(수)과 같은 그림이 나타납니다.
  • x, y좌표는 자릿수가 의미하므로 z좌표와 색값을 ft_atoi로 처리하면 되겠습니다.

3. fdf는 그림의 크기에 따라 윈도우 크기가 바뀝니다.

 저는 이 부분이 정말 어려웠는데요, 도화지와 그림의 크기가 정해지지 않았다는 것이 크기를 조절하는데 너무 어려웠습니다. 좌표 수가 적은 그림은 윈도우 창이 작으며, 그림은 작은 윈도우에 잘 보이게 확장됩니다. 좌표수가 많은 그림은 윈도우 창이 크며, 그림은 역시 큰 윈도우에 잘 보이게 확장됩니다.

 

 큰 그림일수록 좌표를 조금만 떨어뜨려 선을 이어야 하고, 작은 그림일 수록 배수값을 크게하여 점과 점 사이의 거리를 멀리 떨어뜨려야 잘 보이는 그림이 완성될 수 있습니다. 저는 그냥 윈도우창을 고정했습니다... 하ㅎㅏ


마치며

 push_swap 과제를 할 때까지만 하더라도, 들은 얘기도 있고... 2 써클에서 처음 도전하는 과제여서 가장 어려운 줄 알았습니다. FdF는 push_swap과 pipex의 어려운 점을 모두 합쳐놓은 것 같습니다. 새로운 라이브러리의 많은 함수를 공부해야 하는 pipex의 어려움과 수학적인 구현이 필요한 push_swap의 어려움이 합쳐진 특징이 있습니다. 한 달 가까이 걸렸습니다. 푸는 시간은 하루 종일이지만 그 결과가 그림으로 딱 나올 때 쾌감은... 그만큼 해결할 때 정말 개운했고 재미있었던 과제였습니다.