시작에 앞서, 이 자료는 다음 깃허브 레포의 소스 코드들을 기반으로 실제 과제의 멘덴토리를 구현하기까지의 과정을 정리한 자료임을 밝힌다. 그렇기에 기본적으로 이 자료도 위의 실습을 그대로 따라가며 정리한 수준이거나 개인적으로 이해가 되지 않은 부분들을 보충하는 수준에 가깝다.
실습하듯이 miniRT 멘덴토리를 구현할 수 있게끔 조리있게 짜여진 자료이기에 miniRT에 어려움을 겪는다면 위의 레포의 실습을 그대로 진행하는 것을 추천한다.
MiniLibX와 “mlx.h”는 같은 말로, 스크린에 무언가를 렌더링 할 수 있게 해주는 작은 그래픽 라이브러리다.
miniRT에서는 MiniLibX 라이브러리를 사용해서 모니터 상에 이미지를 띄워 가상 공간 상에 그려진 물체를 렌더링하게 된다.
이번 과제에서 사용할 mlx의 기능은 단순하다. 창을 띄우고, 창에 이미지를 쏘고, 특정 버튼이 눌렸을 때 꺼지기만 하면 된다.
#include <mlx.h>
int main(void)
{
void *mlx_ptr;
mlx_ptr = mlx_init();
return (0);
}
mlx.h 파일에 선언되어 있는 mlx_init함수를 활용해서 포인터를 초기화할 수 있는데, mlx_ptr은 mlx_init()함수가 리턴하는 mlx 구조체의 주소를 가리키게 된다.
#include <mlx.h>
int main()
{
void *mlx_ptr;
void *win_ptr; // 생성할 윈도우를 가리키는 포인터
mlx_ptr = mlx_init();
win_ptr = mlx_new_window(mlx_ptr, 500, 500, "Hellow World!");
mlx_loop(mlx_ptr); // loop를 돌면서 event를 기다리고, 생성한 윈도우를 Rendering한다.
return (0);
}
화면에 빈 모니터를 띄울 수 있다.
만약 초기화 하지 않은 mlx_ptr이 mlx_new_window함수로 전달된다면 segfault를 발생한다.
이 함수는 창을 생성하는 것 까지만 작동하고 실제로 모니터상에 창을 띄우진 않는다. mlx_loop 함수를 주석처리해보면 실행파일을 실행했을 때, 아무것도 보이지 않는 것을 알 수 있다.
mlx_loop에 mlx 포인터를 집어넣지 않으면 창이 떠오르지 않는다. mlx_loop는 loop를 돌면서 이벤트를 기다리는 동시에 화면을 모니터 상에 띄우는 작업을 한다.
#include <unistd.h>
#include <mlx.h>
int main(
{
void *mlx_ptr;
void *win_ptr;
t_data image;
mlx_ptr = mlx_init();
win_ptr = mlx_new_window(mlx_ptr, 500, 500, "Hellow World!");
image.img = mlx_new_image(mlx_ptr, 500, 500); // 이미지 객체 생성
image.addr = mlx_get_data_addr(image.img, &image.bits_per_pixel, &image.line_length, &image.endian); // 이미지 주소 할당
for (int i = 0 ; i < 500; i++)
{
for (int j = 0 ; j < 500; j++)
{
mlx_pixel_put(mlx_ptr, win_ptr, i, j, 0x00FFFFFF);
}
}
mlx_loop(mlx_ptr);
return (0);
}
mlx_pixel_put는 i, j에 해당하는 한 픽셀을 색칠하는 함수다.
#include <unistd.h>
#include <mlx.h>
//이미지의 정보를 나타내는 변수를 저장한 구조체
typedef struct s_data
{
void *img;
char *addr;
int bits_per_pixel;
int line_length;
int endian;
} t_data;
//이미지의 원하는 좌표에 해당하는 주소에 color값을 넣는 함수
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;
//
}
int main()
{
void *mlx_ptr;
void *win_ptr;
t_data image;
mlx_ptr = mlx_init();
win_ptr = mlx_new_window(mlx_ptr, 500, 500, "Hellow World!");
image.img = mlx_new_image(mlx_ptr, 500, 500); // 이미지 객체 생성
image.addr = mlx_get_data_addr(image.img, &image.bits_per_pixel, &image.line_length, &image.endian); // 이미지 주소 할당
for (int i = 0 ; i < 500 ; i++)
{
for (int j = 0 ; j < 500 ; j++)
{
my_mlx_pixel_put(&image, i, j, 0x00FFFFFF);
}
}
mlx_put_image_to_window(mlx_ptr, win_ptr, image.img, 0, 0);
mlx_loop(mlx_ptr);
return (0);
}
image.addr에 mlx_new_image를 통해 할당받는 img 구조체의 포인터 img에 대한 정보를 리턴하는데, 이때 정보는 해당 이미지를 한 줄로 쭉 늘인 형태와 같은 char 배열이 된다. 해당 위치에 TRGB 값에 해당하는 색깔을 집어넣으면 이미지의 해당 좌표가 색칠되는 형식이다.
색상은 int형식으로 표현된다. 우리는 ARGB값을 포함하는 int를 얻으려면 몇가지 규칙이 필요하다.
우리는 TRGB 형식을 사용하기 위해 비트연산을 활용한다. 색상을 표현하기 위현 형식은 0xTTRRGGBB와 같이 초기화해서 사용한다. 각 코드는 다음을 의미한다.
RGB 는 위와같이 초기화 할 수 있고, 몇 가지 예시는 아래와 같다.
16진수이므로 두개의 자릿수는 16 * 16, 256의 값을 나타낼 수가 있다.
과제에서 제공해주는 mlx는 minilibx_opengl, minilibx_mms 두 가지 버전이 존재한다.
minilibx_opengl는 정적 라이브러리, minilibx_mms는 동적 라이브러리 + get_screen_size
함수를 사용 가능하다는 차이점이 있다. 동적, 정적 두 방식 모두 특징이 있어서 목적에 적합한 라이브러리를 이용하면 된다. 이 과제에서는 opengl 라이브러리를 사용하기로 하였다.
#include <stdio.h>
#include <mlx.h>
#include "print.h"
#include "scene.h"
#include "structures.h"
#include "trace.h"
#include "utils.h"
typedef struct s_data
{
void *img;
char *addr;
int bits_per_pixel;
int line_length;
int endian;
} t_data;
typedef struct s_vars {
void *mlx;
void *win;
t_data image;
} t_vars;
// esc key press event
int key_hook(int keycode, t_vars *vars)
{
if(keycode == 53)
{
mlx_destroy_window(vars->mlx, vars->win);
exit(0);
}
return (0);
}
int main(int argc, char *argv[])
{
int canvas_width;
int canvas_height;
t_color3 pixel_color;
t_scene *scene;
t_vars vars;
t_data image;
image = vars.image;
canvas_width = 960;
canvas_height = 640;
vars.mlx = mlx_init();
vars.win = mlx_new_window(vars.mlx, canvas_width, canvas_height, "Hello miniRT!");
image.img = mlx_new_image(vars.mlx, canvas_width, canvas_height); // 이미지 객체 생성
image.addr = mlx_get_data_addr(image.img, &image.bits_per_pixel, &image.line_length, &image.endian); // 이미지 주소 할당
mlx_put_image_to_window(vars.mlx, vars.win, image.img, 0, 0);
mlx_key_hook(vars.win, key_hook, &vars); // esc key press event
mlx_loop(vars.mlx);
return (0);
}
# **************************************************************************** #
# #
# ::: :::::::: #
# Makefile :+: :+: :+: #
# +:+ +:+ +:+ #
# By: sham <sham@student.42.fr> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2022/02/10 15:45:19 by sham #+# #+# #
# Updated: 2022/03/13 14:54:50 by sham ### ########.fr #
# #
# **************************************************************************** #
NAME = miniRT
CC = gcc
# CFLAGS = -Werror -Wall -Wextra
LIBRARY = ./mlx/opengl
COMFILE_FLAGS = -I $(LIBRARY) -I ./header
LINKING_FLAGS = -lmlx -L $(LIBRARY) -framework OpenGL -framework AppKit
MAIN_SRCS = $(addprefix src/, main.c)
MAIN_OBJS = $(MAIN_SRCS:.c=.o)
all : $(NAME)
$(NAME) : $(MAIN_OBJS)
@make -C ./mlx/opengl
$(CC) $(CFLAGS) $(LINKING_FLAGS) $(MAIN_OBJS) -o $(NAME)
%.o: %.c
$(CC) $(CFLAGS) $(COMFILE_FLAGS) -c $< -o $@
clean :
rm -rf $(MAIN_OBJS)
fclean : clean
rm -rf $(NAME)
re : fclean all
.PHONY : all clean fclean re
실제 과제처럼 컴파일 하기 위해서는 miniRT를 컴파일 하기 전에 mlx/opengl 폴더에 들어있는 라이브러리 디렉토리를 컴파일 해야 한다. 이를 위해 @make -C ./mlx/opengl
명령어를 사용해서 make 시 최우선적으로 라이브러리 먼저 컴파일 하도록 제어해주었다.
miniRT
"mlx.h"
[so_long] mlx(MiniLibX) 함수 정리
(1) mlx 활용과 color 표현 하는 방법!