20210304(목) - jungcow/42Cursus GitHub Wiki

1. 학습날짜

  • 2021-03-04(목)

2. 학습시간

  • 11:00 ~ 15:00 / 16:00 ~ 15:00 / 21:00 ~ 01:00 (집)

3. 학습범위 및 주제

  • miniRT 학습했던 것들 정리

4. 동료 학습 방법

X

5. 학습 목표

  • miniRT 학습했던 것들 정리

6. 과제 제출 repository 주소

7. 상세 학습 내용

window 창 여는 방법 && 픽셀에 색칠하는 방법

int main(void)
{
	int img_width = 1080;
	int img_height = 720;
	void *mlx = mlx_init();
	void *mlx_win = mlx_new_window(mlx, img_width, img_height, "hello");
	int color;

	color = (int)0xAAAAAAAA;
	for (int j = 0; j < img_height; j++)
	{
		for (int i = 0; i < img_width; i++)
			mlx_pixel_put(mlx, mlx_win, i, j, 0x00FF0000);
	}
	mlx_loop(mlx);
	return (0);
}

우선, 우리는 mlx_pixel_put 함수가 매우 매우 매우 느리다는 것을 고려해야 한다. 프레임이 완전히 렌더링되기를 기다리지 않고 픽셀을 창으로 즉시 밀어넣기 때문입니다. 위 말을 봤을 때, 더 최적화된 함수가 존재한 다는 것을 알 수 있다. 바로 mlx_new_imagemlx_put_image_to_window, 그리고 mlx_get_data_addr 이 함수들이다. 이들은 버퍼로 메모리상에 pixel을 찍어놓고 이후에 완성된 이미지를 창에 내보내준다.

픽셀에 색칠하는 다른 방법

typedef struct s_mlx
{
	void *ptr;
	void *win;
	void *img;
	int width;
	int height;
	int color;
} t_mlx;

typedef struct s_data
{
	char *addr;
	int bits_per_pixel;
	int line_length;
	int endian;
} t_data;

void my_mlx_pixel_put(t_data *data, int x, int y, int color)
{
	char *dst;

	dst = data->addr + (y * data->line_length + x * (data->bits_per_pixel / 8));
	*(unsigned int *)dst = color;
}

t_data ft_mlx_init(t_mlx *mlx)
{
	t_data data;

	mlx->width = 256;
	mlx->height = 256;
	mlx->color = 0xAAFFFFFF;
	mlx->ptr = mlx_init();
	mlx->win = mlx_new_window(mlx->ptr, mlx->width, mlx->height, "Hello World");
	mlx->img = mlx_new_image(mlx->ptr, mlx->width, mlx->height);
	data.addr = mlx_get_data_addr(mlx->img, &(data.bits_per_pixel), &(data.line_length), &(data.endian));
	return (data);
}

int main(void)
{
	t_mlx mlx;
	t_data data;

	data = ft_mlx_init(&mlx);
	for (int j = 0; j < 256; j++)
	{
		for (int i = 0; i < 256; i++)
		{
			my_mlx_pixel_put(&data, i, j, mlx.color);
			mlx.color -= 0x00000100 / 2;
		}
		mlx.color -= 0x00010000 / 2;
		printf("mlx.color[%d]: %x\n", j, mlx.color);
	}
	mlx_put_image_to_window(mlx.ptr, mlx.win, mlx.img, 0, 0);
	mlx_loop(mlx.ptr);
	return (0);
}
  • 처음에 이해가 안간 부분은 mlx_get_data_addr() 함수가 정확히 어떤 식으로 이용되는지이다. 인자로 들어가는 bits_per_pixelline_lenght(man에는 size_length라고 되어있음), 그리고 endian 이 세가지도 왜 int * 형으로 받는지 궁금했다. 하지만 42docs 에서 간략하게 어떻게 인자를 넣어야 하는지 나와있고, 여기서 int * 형을 집어넣는 이유가 int 형의 주솟값을 집어넣어 이 함수 내부에서 이 int 값을 저장하는 식이라는 것을 알게 되었다.
  • 또한 mlx_get_data_addr 함수가 반환하는 값 char * addr는 픽셀을 저장할 버퍼의 시작 주소이다. 즉, 이 주소는 (0,0)의 픽셀의 위치를 나타내는 메모리 상의 주소라고 할 수 있다.
  • 그럼 이제 bits_per_pixel 값과 line_lenght, 그리고 endian에 대해서 그 이후의 사용성이 궁금해졌다. 이것도 42docs-my_mlx_put_pixel 여기에 나와있다.

void            my_mlx_pixel_put(t_data *data, int x, int y, int color)
{
    char    *dst;

    dst = data->addr + (y * data->line_length + x * (data->bits_per_pixel / 8));
    *(unsigned int*)dst = color;
}

위와 같이 메모리 주소에 접근 가능한 char * 형 dst를 선언해주고, 픽셀을 찍을 주소를 대입해준다. 이 때 주소를 얻는 원리는 처음 주소 addr에 y좌표 * 각 line끼리 메모리 주소상에서 떨어져있는 거리(여기서 1024) + x좌표 * (bits_per_pixel / 8) (여기는 한 픽셀당 8비트를 사용하는 8bits 색 체계를 사용하므로, 8로 나누어 옆으로 한칸 이동될 수 있도록 한것이다.)으로 처음 addr주소에 더해주어 그 메모리 주소에 어떤 색을 넣을지 정하게 된다. 그런식으로 픽셀 하나하나당 해당하는 메모리 값에 컬러에 해당하는 int 형 숫자를 할당하여 그 메모리 값을 int형으로 채워넣게 되면 픽셀에 색이 들어간 것과 같다. 이렇게 이미지를 완성하게 되면 마지막으로 mlx_put_image_to_window 함수를 호출하여 window 창에 이미지를 넣게 된다. 이것이 버퍼와 같은 기능을 활용하여 렌더링 될때마다 렌더링을 기다려, 애니메이션에서 장면에서 장면으로 넘어가는 것처럼 한 장면을 출력할 수 있게 된다.

출력 예시 1.

int main(void)
{
	t_mlx mlx;
	t_data data;

	data = ft_mlx_init(&mlx);
	for (int j = 0; j < 1024; j++)
	{
		for (int i = 0; i < 1024; i++)
		{
			my_mlx_pixel_put(&data, i, j, mlx.color);
			if ((i >= 256 && i < 511) || (i >= 768 && i < 1024))
				mlx.color += 0x00000001;
			else
				mlx.color -= 0x00000001;
		}
	}
	mlx_put_image_to_window(mlx.ptr, mlx.win, mlx.img, 0, 0);
	mlx_loop(mlx.ptr);
	return (0);
}

출력1

출력 예시 2

int main(void)
{
	t_mlx mlx;
	t_data data;

	data = ft_mlx_init(&mlx);
	for (int j = 0; j < 1024; j++)
	{
		for (int i = 0; i < 1024; i++)
		{
			my_mlx_pixel_put(&data, i, j, mlx.color);
            mlx.color -= 0x00000001 / 2;
		}
        mlx.color -= 0x00010000 / 2;
	}
	mlx_put_image_to_window(mlx.ptr, mlx.win, mlx.img, 0, 0);
	mlx_loop(mlx.ptr);
	return (0);
}

출력2

8. 학습에 대한 총평

  • miniRT 과제에서 픽셀을 제대로 찍어보기까지 1주일이 넘게 걸린 것 같다. 라이브러리의 각 함수들의 프로토 타입들을 이해하는데에도 오래 걸렸고, 또한 어떻게 사용해야 하는지도 막막했었다. 결국 42docs의 설명대로 해보니 하나하나 진행해나가게 된 것 같았다. 성취감도 있고, 이제 벡터를 이용해 레이트레이싱을 해야할 생각에 기대가 되기도 하지만 한편으론 두려움이 앞선다. 계속 이렇게 학습해 나가보자.

9. 다음 학습 계획

  • miniRT 벡터의 활용법