최초의 fps게임인 Wolfenstein 3D와 같은 프로그램을 레이케스팅이라는 랜더링 기술을 활용하여 그래픽 디자인을 구현해보는것이 목표이다.
플레이어를 기준으로 시야각 범위에서 무수히 많은 빛을 쏘아서 벽을 탐지하는 기법이다. 이때 벡터를 활용하여 단위길이만큼 광선의 길이를 늘려가며 주어진 맵에서 벽을 확인을 해주게된다. 단순히 광선이 포함하는 모든 점들에 대해서만 벽의 존재를 확인해주면 벽을 탐지하지 못하는 경우가 발생할 수 도있고, 연산자체도 상당히 많아져서 DDA라는 기법을 사용한다.
레이케스팅을 하여 벽과의 실제 거리, 즉 Euclid거리를 구할 수 있게된다. 이때 Euclid 거리를 사용하여 화면에 벽을 그려준다면 둥글게 그려지는 현상을 확인할 수 있게된다. 이를 방지하기위해 보정된 거리인 PerpDist를 사용하여 화면에 그려준다.
RGB는 색을 나타내는 방식으로 unsignedint범위 내로 표현이 되어진다. RGB의 경우 3바이트로 16진수의 방식으로 보통 표현한다.(물론 10진수값으로 넣어도 무방)
예시 ) RGB = 0xFF00FF
마찬가지로 ARGB는 RGB에서 색의 투명도를 추가로 표현해준것이다. 때문에 1바이트가 큰 4바이트이다.
예시 ) ARGB = 0x00FF00FF
#ifndef CUB3D_H
# define CUB3D_H
# include <fcntl.h>
# include <math.h>
# include <mlx.h>
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include "libft/libft.h"
# define WIDTH 1280
# define HEIGHT 920
# define FOV 0.66
# define MV_SPEED 0.1
# define X_EVENT_KEY_PRESS 2
# define X_EVENT_KEY_EXIT 17
# define KEY_ESC 53
# define KEY_W 13
# define KEY_A 0
# define KEY_S 1
# define KEY_D 2
# define KEY_LEFT 123
# define KEY_RIGHT 124
enum e_element
{
NORTH,
SOUTH,
EAST,
WEST,
FLOOR,
CEILING
};
enum e_side
{
HORIZONTAL,
VERTICAL
};
struct s_vector
{
double x;
double y;
};
struct s_img
{
void *img_ptr;
unsigned int *buffer;
int width;
int height;
int bits_per_pixel;
int size_line;
int endian;
};
struct s_map_info
{
char *north_path;
char *south_path;
char *west_path;
char *east_path;
unsigned int color_floor;
unsigned int color_ceiling;
char **map;
int width;
int height;
int check[6];
};
struct s_ray
{
t_vector side_dist;
t_vector delta_dist;
t_vector dir;
t_side side;
int map_x;
int map_y;
int dx;
int dy;
int hit;
int wall_height;
int draw_start;
int draw_end;
double wall_x;
};
struct s_player
{
t_vector pos;
t_vector dir;
t_vector plane;
char direction;
};
struct s_cub
{
int fd;
int error;
void *mlx_ptr;
void *win_ptr;
t_player player;
t_map_info map_info;
t_img *background;
t_img *texture[4];
};
#endif
#include "../cub3d.h"
int main(int argc, char **argv)
{
t_cub cub;
ft_memset(&cub, 0, sizeof(t_cub));
if (argc != 2)
cub_error(&cub, "Invalid number of arguments");
init_map(&cub, argv[1]);
start_game(&cub);
return (0);
}
void raycast(t_cub *cub)
{
t_img img;
t_ray ray;
int idx;
img.img_ptr = mlx_new_image(cub->mlx_ptr, WIDTH, HEIGHT);
img.buffer = (unsigned int *)mlx_get_data_addr(img.img_ptr, \
&(img.bits_per_pixel), &(img.size_line), &(img.endian));
set_transparent_img(img.buffer);
idx = -1;
while (++idx < WIDTH)
{
init_ray(cub, &ray, idx);//초기화
dda(cub, &ray);// DDA
set_wall_element(cub, &ray);//화면에 출력해주기 위해 필요한 인자를 구함
draw_vertical_pixel(cub, &ray, &img, idx);//수직으로 img.buffer의 ARGB 데이터를 값에 맞게 변경
}
mlx_put_image_to_window(cub->mlx_ptr, cub->win_ptr, \
cub->background->img_ptr, 0, 0);//천장 바닥 이미지 출력
mlx_put_image_to_window(cub->mlx_ptr, cub->win_ptr, img.img_ptr, 0, 0);// 벽이미지 출력
}
직접 그래픽 디자인을 통해 2차원 맵을 3차원으로 확장하여 화면에 출력해주는 것을 구현하였는데, 이와 같은 과제를 해보지 않았다면 알기 어려웠던 여러 알고리즘과 기법들에 대해 공부할 수 있었던 시간이었다.