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_image
와mlx_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_pixel
과line_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);
}
출력 예시 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);
}
8. 학습에 대한 총평
- miniRT 과제에서 픽셀을 제대로 찍어보기까지 1주일이 넘게 걸린 것 같다. 라이브러리의 각 함수들의 프로토 타입들을 이해하는데에도 오래 걸렸고, 또한 어떻게 사용해야 하는지도 막막했었다. 결국 42docs의 설명대로 해보니 하나하나 진행해나가게 된 것 같았다. 성취감도 있고, 이제 벡터를 이용해 레이트레이싱을 해야할 생각에 기대가 되기도 하지만 한편으론 두려움이 앞선다. 계속 이렇게 학습해 나가보자.
9. 다음 학습 계획
- miniRT 벡터의 활용법