void set_cam(t_scene *scene, t_cam *cam)
{
double vp[2];
t_vec3 w;
t_vec3 u;
t_vec3 v;
vp[1] = 2 * tan(cam->fov / 2 * M_PI / 180);
vp[0] = vp[1] * (double)scene->xres / scene->yres;
w = normalize(vscale(cam->nv, -1));
set_uv_axis(w, &u, &v);
cam->hor = vscale(u, vp[0]);
cam->ver = vscale(v, vp[1]);
cam->llc = vsub(cam->o, vscale(cam->hor, 0.5));
cam->llc = vsub(cam->llc, vscale(cam->ver, 0.5));
cam->llc = vsub(cam->llc, w);
}
miniRT에서는 camera의 위치
와 카메라가 보는 방향을 나타낼 normal vector
, 화각 fov
를 받는다.
fov를 이용해 먼저 viewport를 설정해주게 되는데, viewport의 height는 입력받은 fov값 / 2
의 tangent
값을 2 곱해준
길이가 된다.
(tangent 그래프가 π/2 를 기준으로 음수 값이 되니까 2로 나눈 값을 다시 2 곱해주는 식으로 사용하는 것 같다. 파싱할 때 0~180 사이 값만 받을 수 있다.)
C언어의 tan 함수는 라디안 값을 인자로 사용하기 때문에 π/180 을 곱해주었다.
viewport의 width는 height에 화면 비율만큼 곱해 구해준다.
w = normalize(vscale(cam->nv, -1));
set_uv_axis(w, &u, &v);
void set_uv_axis(t_vec3 w, t_vec3 *u, t_vec3 *v)
{
w = normalize(w);
if (w.y == 1 || w.y == -1)
*u = create_vec3(w.y, 0, 0);
else
*u = cross(create_vec3(0, 1, 0), w);
*v = cross(w, *u);
}
w벡터
를 파싱한 cam의 normal vector를 음수
로 바꿔준 값으로 정한다.0,1,0
벡터와 cross product
를 하여 수직인 벡터를 구해준다. 이 때 w axis가 이미 0,1,0
또는 0,-1,0
인 경우 수직 벡터를 제대로 구할 수 없기 때문에 그 땐 임의로 해당 벡터의 수직 벡터인 1,0,0
또는 -1,0,0
벡터로 세팅해줬다.w
와 u
를 cross product
한 벡터로 설정 해주면 된다.lower left corner
를 구해야 한다.-u/2
벡터와 -v/2
벡터를 더한 벡터와 cam에서 뷰포트로 가는 w
벡터를 더해준 벡터가 된다.ray = origin + (t * dir)
double hit_plane_time(t_ray ray, t_plane pl)
{
double denom;
double time;
denom = dot(pl.nv, ray.dir);
if (denom == 0)
return (INFINITY);
time = (dot(pl.nv, vsub(pl.p, ray.o))) / denom;
if (time <= EPSILON)
return (INFINITY);
return (time);
}
int hit_plane(t_ray *ray, t_figures elem)
{
double time;
t_plane pl;
pl = elem.fig.pl;
time = hit_plane_time(*ray, pl);
if (ray->hit.time > time)
{
ray->hit.time = time;
ray->hit.point = get_hit_point(*ray);
if (dot(ray->dir, pl.nv) > 0) // dot = |dir| * |nv| * cos(θ)
pl.nv = vscale(pl.nv, -1);
ray->hit.nv = pl.nv;
ray->hit.elem = elem;
return (1);
}
return (0);
}
int hit_sphere(t_ray *ray, t_figures elem)
{
t_vec3 oc;
t_sphere sp;
double time[2];
sp = elem.fig.sp;
oc = vsub(ray->o, sp.c);
solve_quadratic(length_squared(ray->dir), 2 * dot(ray->dir, oc),
length_squared(oc) - pow(sp.r, 2), time);
if (ray->hit.time > time[0])
{
ray->hit.time = time[0];
ray->hit.point = get_hit_point(*ray);
ray->hit.nv = normalize(vsub(ray->hit.point, sp.c));
ray->hit.elem = elem;
return (1);
}
return (0);
}
먼저 부딪힌 곳
(time의 값이 더 작은
좌표)의 색상이 찍혀야 한다.double hit_cylinder_time(t_ray ray, t_cylinder cy, double *y)
{
t_vec3 v[2];
t_vec3 oc;
double dist[2];
double time[2];
oc = vsub(ray.o, cy.c);
v[0] = vsub(ray.dir, vscale(cy.nv, dot(ray.dir, cy.nv)));
v[1] = vsub(oc, vscale(cy.nv, dot(oc, cy.nv)));
solve_quadratic(length_squared(v[0]),
2 * dot(v[0], v[1]), length_squared(v[1]) - pow(cy.r, 2), time);
dist[0] = dot(cy.nv, vsub(vscale(ray.dir, time[0]), vscale(oc, -1)));
dist[1] = dot(cy.nv, vsub(vscale(ray.dir, time[1]), vscale(oc, -1)));
if (dist[0] >= 0 && dist[0] <= cy.height)
{
*y = dist[0];
return (time[0]);
}
if (dist[1] >= 0 && dist[1] <= cy.height)
{
*y = dist[1];
return (time[1]);
}
return (INFINITY);
}
무한한 실린더
라고 가정했을 때의 교차점이 구해지게 되기 때문이다.정사영
한 값이 0 <= x <= height
를 만족하는 경우에만 실근으로 인정한다.해당 값
을 사용하면 되고더 작은 값
을 사용하면 되고INFINITY
로 처리한다.int hit_cylinder(t_ray *ray, t_figures elem)
{
double time;
double y;
t_cylinder cy;
cy = elem.fig.cy;
time = hit_cylinder_time(*ray, cy, &y);
if (time < INFINITY && ray->hit.time > time)
{
ray->hit.time = time;
ray->hit.point = get_hit_point(*ray);
ray->hit.nv = normalize(vsub(ray->hit.point,
vadd(vscale(cy.nv, y), cy.c)));
ray->hit.elem = elem;
return (1);
}
return (0);
}
P가 위치한 실린더 단면에 생기는 원의 중심
에서 교점 P로 향하는 벡터를 정규화 한 벡터가 된다. normalize()double hit_cone_time(t_ray ray, t_cone con, double *cosine)
{
t_vec3 oc;
double time[2];
double dist[2];
oc = vsub(ray.o, con.c);
*cosine = cos(con.theta / 2 * M_PI / 180);
solve_quadratic(pow(dot(ray.dir, con.nv), 2) - pow(*cosine, 2),
2 * (dot(ray.dir, con.nv) * dot(oc, con.nv)
- dot(ray.dir, oc) * pow(*cosine, 2)),
pow(dot(oc, con.nv), 2) - length_squared(oc) * pow(*cosine, 2),
time);
dist[0] = dot(con.nv, vsub(vscale(ray.dir, time[0]), vscale(oc, -1)));
dist[1] = dot(con.nv, vsub(vscale(ray.dir, time[1]), vscale(oc, -1)));
if (dist[0] >= 0 && dist[0] <= con.height)
return (time[0]);
if (dist[1] >= 0 && dist[1] <= con.height)
return (time[1]);
return (INFINITY);
}
int hit_cone(t_ray *ray, t_figures elem)
{
double time;
double cosine;
t_cone con;
t_vec3 cp;
con = elem.fig.con;
time = hit_cone_time(*ray, con, &cosine);
if (ray->hit.time > time)
{
ray->hit.time = time;
ray->hit.point = get_hit_point(*ray);
cp = vsub(ray->hit.point, con.c);
ray->hit.nv = normalize(vsub(vscale(normalize(cp), cosine), con.nv));
ray->hit.elem = elem;
return (1);
}
return (0);
}
al_clr = cscale(rt->scene.al_clr, rt->scene.al_br);
ALBEDO
x light brightness
x cosine(θ)
double diffuse(t_light light, t_ray ray)
{
t_vec3 p_to_light;
double cos_with_light;
p_to_light = vsub(light.o, ray.hit.point);
cos_with_light = clamp(dot(normalize(p_to_light), ray.hit.nv), 0, 1);
if (cos_with_light > 0)
return (ALBEDO * light.br * cos_with_light);
else
return (0);
}
t_vec3 reflect_ray(t_vec3 p_to_light, t_vec3 hit_normal)
{
return (vsub(vscale(hit_normal, 2 * dot(p_to_light, hit_normal)), \
p_to_light));
}
double specular(t_light light, t_ray ray)
{
t_vec3 p_to_light;
t_vec3 reflected;
t_vec3 p_to_cam;
double scalar;
p_to_light = vsub(light.o, ray.hit.point);
reflected = reflect_ray(normalize(p_to_light), ray.hit.nv);
p_to_cam = vscale(ray.dir, -1);
scalar = dot(normalize(reflected), normalize(p_to_cam));
if (ray.hit.elem.specular > 0 && scalar > 0)
return (light.br * pow(scalar, ray.hit.elem.specular));
else
return (0);
}
int in_shadow(t_minirt *rt, t_hit hit, t_light *light)
{
t_ray shadow;
int ret;
shadow.o = hit.point;
shadow.dir = normalize(vsub(light->o, hit.point));
ret = intersect(rt, &shadow);
if (distance(hit.point, light->o) <= distance(hit.point, shadow.hit.point))
return (0);
return (ret);
}
void uv_mapping_sphere(double *u, double *v, t_vec3 uv_axis[2], t_hit hit)
{
double theta;
double phi;
set_uv_axis(hit.nv, &uv_axis[0], &uv_axis[1]);
theta = acos(-1 * hit.nv.y);
phi = atan2(-1 * hit.nv.z, hit.nv.x) + M_PI;
*u = phi * M_1_PI * 0.5;
*v = theta * M_1_PI;
}
void uv_mapping_cylinder(double *u, double*v, t_vec3 uv_axis[2], t_hit hit)
{
t_vec3 pc;
double theta;
double height;
set_uv_axis(hit.elem.fig.cy.nv, &uv_axis[0], &uv_axis[1]);
pc = vsub(hit.point, hit.elem.fig.cy.c);
theta = atan2(-1 * dot(pc, uv_axis[0]), dot(pc, uv_axis[1])) + M_PI;
height = dot(pc, hit.elem.fig.cy.nv);
*u = theta * M_1_PI * 0.5;
*v = height / hit.elem.fig.cy.height;
}
void uv_mapping_cone(double *u, double *v, t_vec3 uv_axis[2], t_hit hit)
{
t_vec3 pc;
double theta;
double height;
set_uv_axis(hit.elem.fig.con.nv, &uv_axis[0], &uv_axis[1]);
pc = vsub(hit.point, hit.elem.fig.con.c);
theta = atan2(-1 * dot(pc, uv_axis[0]), dot(pc, uv_axis[1])) + M_PI;
height = dot(pc, hit.elem.fig.con.nv);
*u = theta * M_1_PI * 0.5;
*v = height / hit.elem.fig.con.height;
}
int checker_board_pattern_at(double u, double v, t_hit hit)
{
int u2;
int v2;
int ret;
u2 = u * hit.elem.checker_w;
v2 = v * hit.elem.checker_h;
ret = (u2 + v2) % 2;
if (u * v < 0)
ret = !ret;
if (ret)
return (hit.elem.clr);
else
return (complementary_color(hit.elem.clr));
}
int image_mapping(double u, double v, t_xpm_img img_map)
{
int u2;
int v2;
int color;
u2 = u * img_map.w;
v2 = (1.0 - v) * img_map.h;
color = get_pixel_color(img_map, u2, v2);
return (color);
}
t_vec3 normal_mapping(double u, double v, t_vec3 uv_axis[2], t_hit hit)
{
int u2;
int v2;
int color;
t_vec3 normal_color;
u2 = u * hit.elem.tx->bmp_map.w;
v2 = (1.0 - v) * hit.elem.tx->bmp_map.h;
color = get_pixel_color(hit.elem.tx->bmp_map, u2, v2);
normal_color = color_to_vec3(color);
normal_color = vsub(vscale(normal_color, 2), create_vec3(1, 1, 1));
return (change_basis(uv_axis[0], uv_axis[1], hit.nv, normal_color));
}
t_vec3 change_basis(t_vec3 v1, t_vec3 v2, t_vec3 v3, t_vec3 vec)
{
t_vec3 normal;
normal.x = v1.x * vec.x + v2.x * vec.y + v3.x * vec.z;
normal.y = v1.y * vec.x + v2.y * vec.y + v3.y * vec.z;
normal.z = v1.z * vec.x + v2.z * vec.y + v3.z * vec.z;
return (normal);
}