그래픽스를 공부한지 어언 4~5달이 지난것 같다.

mlx는 이미 다 까먹고 새것을 보는 느낌이다.

나만 그런건 아닐거라 믿는다.

 

일단 처음해야할 것 예전에 봤던 mlx를 이용한 예제들을 다시 재구현한다.

대신 이전에는 OpenGL을 사용했는데 이는 정적라이브러리라고 한다.

정적라이브러리는 말만 들어도 뭔가 한꺼번에 사용할 것 같은 느낌.

동적라이브러리인 mms를 사용하는 것이 좋다는 슬랙에서의 말씀들을 듣고 다시 시작이다.

동적라이브러리를 더 찾아보면 좋겠지만 이미 정리를 너무 잘해둔 분들이 있어서 간단명료하게 적기만 할 것이다.

  1. intra에서 tar파일을 다운받고 압축을 푼다.
  2. 디렉토리의 이름을 mlx로 간편하게 바꾼 후 들어간다.
  3. 이제 make를 치면? Warning들과 Error의 향연일 것이다.
  4. 여기서 잘 보면 error중에 "UInt32(1)"에 error가 떠있는데 이것을 boolean_t(1)로 바꿔준다.
  5. 그리고 make를 해보면 "libmlx.dylib"라는 파일이 생긴다.
  6. 그리고 예제를 만들어서 "-L../mlx -Imlx -framework OpenGL -framework ÅppKit" 플래그를 사용하여 컴파일.
  7. 실행파일을 실행하면 Abort가 뜨는데 "dyld: (대충)라이브러리 못찾음" 이런 경고문을 볼 수 있다.
  8. mlx의 절대 경로를 "DYLD_LIBRARY_PATH"라는 환경변수에 지정해주면 실행할 수 있다!

 


예제 시작

 

ex01

#include "../../mlx/mlx.h"

int     main(void)
{
    void    *mlx;
    void    *win;

    mlx = mlx_init();
    win = mlx_new_window(mlx, 500, 500, "mlx_project");
    mlx_loop(mlx);
    return (0);
}

위의 코드를 써보면 이미지를 간단하게 500x500의 사이즈로 열어볼 수 있다.

 

result


ex02

#include "../../mlx/mlx.h"
#include <stdio.h>
#include <stdlib.h>

#define X_EVENT_KEY_PRESS   2
#define X_EVENT_KEY_RELEASE 3
#define X_EVENT_KEY_EXIT    17

#define KEY_ESC             53
#define KEY_Q               12
#define KEY_W               13
#define KEY_E               14
#define KEY_R               15
#define KEY_A               0
#define KEY_S               1
#define KEY_D               2

typedef struct s_param
{
    int     x;
    int     y;
    char    str[3];
}   t_param;

void    param_init(t_param *param)
{
    param->x = 3;
    param->y = 4;
    param->str[0] = 'a';
    param->str[1] = 'b';
    param->str[2] = '\0';
}

int     key_press(int keycode, t_param *param)
{
    static int  a;

    if (keycode == KEY_W)
        param->y += 1;
    else if (keycode == KEY_S)
        param->y -= 1;
    else if (keycode == KEY_A)
        param->x -= 1;
    else if (keycode == KEY_D)
        param->x += 1;
    else if (keycode == KEY_ESC)
        exit(0);
    printf("Current Position : (%d, %d)\n", param->x, param->y);
    return (0);
}

int main(void)
{
    void    *mlx;
    void    *win;
    t_param param;

    param_init(&param);
    mlx = mlx_init();
    win = mlx_new_window(mlx, 500, 500, "mlx_project");
    printf("------------------------------------------\n");
    mlx_hook(win, X_EVENT_KEY_PRESS, 0, &key_press, &param);
    mlx_loop(mlx);
    return (0);
}

위의 코드는 이미지를 열고서 key event를 이용해서 내부의 변수를 조정하는 것을 할 수 있다.

당장 보이는 변화는 없다 그렇지만 이를 통해서 할 수 있는것은 많다고 본다.

result

 

 


ex03

이제는 색을 입히는데 Ray Tracing in One Week를 C언어로 변형해서 따라해보자.

그냥 색을 입히는 것이 아니라 그라데이션을 그려주는 작업을 할 것 이다.

#include <stdio.h>
#include <stdlib.h>
#include "../../mlx/mlx.h"

# define WIN_WIDTH 256
# define WIN_HEIGHT 256

# define IMG_WIDTH 256
# define IMG_HEIGHT 256

typedef struct  s_img
{
    void    *img_ptr;
    char    *data;
    int     size_l;
    int     bpp;
    int     endian;
}   t_img;

typedef struct s_mlx
{
    void    *mlx_ptr;
    void    *win;
}   t_mlx;

void	my_mlx_pixel_put(t_img *img, int x, int y)
{
    int     r;
    int     g;
    int     b;
    int     rgb_color;
    char	*dst;

    r = (int)(255.999 * ((double)x / (IMG_WIDTH - 1)));
    g = (int)(255.999 * ((double)y / (IMG_HEIGHT - 1)));
    b = (int)(255.999 * 0.25);
    rgb_color = (r << 16) | (g << 8) | b;
    dst = img->data + (x * (img->bpp / 8)) + ((IMG_HEIGHT - y - 1) * img->size_l);
	*(unsigned int *)dst = rgb_color;
}

int main(void)
{
    t_mlx   *mlx;
    t_img   img;
    int     count_w;
    int     count_h;

    mlx = malloc(sizeof(t_mlx));
    mlx->mlx_ptr = mlx_init();
    mlx->win = mlx_new_window(mlx->mlx_ptr, WIN_WIDTH, WIN_HEIGHT, "Gadation");
    img.img_ptr = mlx_new_image(mlx->mlx_ptr, IMG_WIDTH, IMG_HEIGHT);
    img.data = mlx_get_data_addr(img.img_ptr, &img.bpp, &img.size_l, &img.endian);
    count_h = IMG_HEIGHT;
    while (--count_h >= 0)
    {
        count_w = -1;
        while (++count_w < IMG_WIDTH)
            my_mlx_pixel_put(&img, count_w, count_h);
    }
    mlx_put_image_to_window(mlx->mlx_ptr, mlx->win, img.img_ptr, 0, 0);
    mlx_loop(mlx->mlx_ptr);
    free(mlx);
    return (0);
}

여기서는 좌표에 따른 값을 RGB로 변환하여 나타내 주는 것이다.

R과 G는 0~ 255.999까지의 범위를 x, y 좌표로 인해 만들어 준다.

B는 255.999 / 4의 값으로 고정이다.

그리고 비트연산 후 위치를 찾아서 넣는다.

result

결과를 보면 x축과 y축이 좌측 하단에 있는것을 확인할 수 있다. ((0, 0)이면 B만 남을테니까.)

이는 img의 data에서 위치를 찾을 때 y축을 반전 시켜주면 된다.


ex04

이번에는 하늘을 표현해보자.

#include <stdio.h>
#include <stdlib.h>
#include "../../mlx/mlx.h"
#include "vector.h"
#include "ray.h"

# define WIN_WIDTH 800
# define WIN_HEIGHT 600

# define IMG_WIDTH 800
# define IMG_HEIGHT 600

typedef struct  s_img
{
    void    *img_ptr;
    char    *data;
    int     size_l;
    int     bpp;
    int     endian;
}   t_img;

typedef struct s_mlx
{
    void    *mlx_ptr;
    void    *win;
}   t_mlx;

void	my_mlx_pixel_put(t_img *img, t_cam cam, int x, int y)
{
	char	*dst;
    double  u;
    double  v;
    double  t;
    int     rgb_color;
    t_ray   ray;
    t_vec   color;

    u = (double)x / (double)(IMG_WIDTH - 1);
    v = (double)y / (double)(IMG_HEIGHT - 1);
    ray = make_ray(cam.origin, sub_vec(add_vec(add_vec(cam.left_bottom_corner, \
                                                mul_vec_(cam.horizontal, u)), \
                                                mul_vec_(cam.vertical, v)), \
                                                cam.origin));
    t = 0.5 * (unit_vec(ray.direction).y + 1.0);
    color = mul_vec_(add_vec(mul_vec_(make_vec(1.0, 1.0, 1.0), (1.0 - t)), \
                    mul_vec_(make_vec(0.5, 0.7, 1.0), t)), 255.999);
    rgb_color = ((int)color.x << 16) | ((int)color.y << 8) | (int)color.z;
	dst = img->data + (x * img->bpp / 8) + ((IMG_HEIGHT - y - 1) * img->size_l);
	*(unsigned int *)dst = rgb_color;
}

int main(void)
{
    t_mlx   *mlx;
    t_img   img;
    t_cam   cam;
    int     count_w;
    int     count_h;
    double  aspect_ratio;

    mlx = malloc(sizeof(t_mlx));
    mlx->mlx_ptr = mlx_init();
    mlx->win = mlx_new_window(mlx->mlx_ptr, WIN_WIDTH, WIN_HEIGHT, "Sky");
    img.img_ptr = mlx_new_image(mlx->mlx_ptr, IMG_WIDTH, IMG_HEIGHT);
    img.data = mlx_get_data_addr(img.img_ptr, &img.bpp, &img.size_l, &img.endian);
    aspect_ratio = 4.0 / 3.0;
    cam = make_cam(make_vec(0, 0, 0), 2.0 * aspect_ratio, 2.0, 1.0);
    count_h = IMG_HEIGHT;
    while (--count_h >= 0)
    {
        count_w = -1;
        while (++count_w < IMG_WIDTH)
            my_mlx_pixel_put(&img, cam, count_w, count_h);
    }
    mlx_put_image_to_window(mlx->mlx_ptr, mlx->win, img.img_ptr, 0, 0);
    mlx_loop(mlx->mlx_ptr);
    free(mlx);
    return (0);
}

우리가 보는 시선, 광선은 3차원의 벡터로 표현이 가능하다.

그렇기에 vector구조체를 만들어주고 각종 연산들을 수행하는 함수들을 만든다.

예를 들면 사칙연산, 내적, 외적, 단위벡터화를 말할 수 있다.

 

Ray라는 구조체를 추가한다. 나는 Ray를 광선의 정보로 생각했다.

Ray는 origin과 direction이라는 두 개의 벡터를 갖고있다.

이는 $P(t) = A + Bt = origin + (direction * t)$와 같은 말이며 해석은 다음과 같다.

  • 광원이 존재해야 광선도 존재한다.
  • 원점으로부터 광원의 위치를 나타내는 벡터가 A(origin)이다.
  • 광원으로부터 뻗어나가는 광선의 벡터가 B이다.
  • 그렇다면 t는 무엇이냐? 이는 광선의 강도이다.

이로서 Ray를 통해 광선의 정보를 알 수 있다.

 

또 다른 cam이라는 구조체를 추가한다. 사람으로 치면 눈이다.

구조체 cam의 구조는 Ray Tracing에 사용되는 변수들을 갖고있다.

viewport_width, viewport_height, focal_length, etc ....

이것을 어떻게 사용해야할지 모르기 때문에 Ray Tracing에 대해 자세히 파고들어가보자.

 

Ray Tracing은 우리가 보고있는 3차원의 형상을 2차원으로 해석한다.

왜냐? 우리가 표현해야할 화면은 2차원이 될테니까.

즉, 동적으로 있는 배경을 정적으로 바라보고 있다고 생각해야 한다.

그렇게 하려면 정지된 세계예서 우리가 보고있는 장면의 크기를 결정해야 한다.

여기서 viewport는 '우리가 보고있는 장면'을 의미한다.

 

또한, 여기서 viewport와 기준점과의 거리를 focal_length라고 할 수 있다.

한 가지 더 생각하면 무조건 화면의 크기와 보고있는 장면이 같다고 볼 수는 없다.

이게 무슨 말이냐 하면 1600x900의 화면에 800x600의 장면으로 나타낼 수 있다는 것이다.

이를 어떻게 하느냐 하면 화면의 기준에서 (0, 0)은 좌측 하단에 존재한다 볼 수 있다.

하지만 장면의 기준에서 좌측 하단이 (0, 0)이 아니기 때문에 계산을 통해 알 수 있다.

$Left\ Bottom\ Corner = origin(현재 눈의 위치) - (\frac{view\ port_w} {2}, \frac{view\ port_h} {2}, focal\ length)$

 

이 정도면 이해가 갈 것이라 생각한다.

 

그렇게 되면 my_mlx_pixel_put에서 ray를 생성하는데 여기서 direction을 계속해서 정의해준다.

이는 픽셀의 기준으로 생각하면 되는데 우리가 보는 viewport는 픽셀들의 조합이다.

즉, 픽셀 하나씩 확인하면서 광원에서의 광선을 재정의하는 과정인 것이다.

 

$t = \frac {unit(ray.direction.y)\ +\ 1} {2}$

위 수식으로 t값을 정의한다. (이거 왜 이렇게 정의 되는지 모르겠음)

 

그리고 선형 혼합(Linear Blend)의 식을 통해서 색을 정해준다.

$선형 혼합 => result = (1 - t) * First\ Color + t * Last\ Color$

이렇게 First Color는 흰색, Last Color는 푸른색이 되어서 색감을 결정해준다.

 

result

 


ex04

이제 우리는 새빨간색의 구를 화면에 표시할 것이다.

이것은 생각했을 때 정말 간단하게 생각하면 된다.

구의 좌표와 크기를 설정해주고 만들어준 뒤 캠에서 색에대한 정보를 정의해줄때 구를 보고 있다면 새빨간색을 정의해주면 되는 것이다.

그러기 위해서는 눈과 픽셀을 정의한 벡터와 구가 만나는지 확인해주면 된다.

 

이를 확인하기 위해선 우리는 교점에 대해 확인하면 된다.

구의 테두리 좌표를 하나씩 전부 갖고 있을 수는 없으니 함수를 사용하자.

$구의 함수 => x^2 + y^2 + z^2 = r^2$

위 함수를 보면 구조체 sphere가 갖고 있어야할 정보는 구의 중심(x, y, z)과 반지름r이다.

typedef struct  s_sphere
{
    t_vec   centor;
    double  radius;
}   t_sphere;

 

그리고 우리는 구와의 교점을 찾을 수식을 계산할 함수를 만들어야 한다.

int         hit_sphere(t_sphere sp, t_ray ray)
{
    t_vec   oc;
    double  a;
    double  b;
    double  c;
    double  discriminant;

    oc = sub_vec(ray.origin, sp.centor);
    a = dot_vec(ray.direction, ray.direction);
    b = dot_vec(oc, ray.direction);
    c = dot_vec(oc, oc) - (sp.radius * sp.radius);
    discriminant = b * b - a * c; // 점화식
    if (discriminant >= 0)
        return (1);
    else
        return (0);
}

조금 어려운 벡터의 개념이므로 잘 작성해보도록 하겠다.

 

일단 구의 중심을($C_1$, $C_2$, $C_3$)라고 생각했을 때의 구의 함수는 아래와 같다.

$(x - C_1)^2 + (y - C_2)^2 + (z - C_3)^2 = r^2$

여기서 반지름r은 눈에서 구의 중심까지의 벡터(C)와 눈에서 구의 테두리 좌표까지의 벡터(P)로 표현할 수 있다.

즉, $r^2 = (P - C) \cdot (P - C)$가 된다.

그리고 위 수식을 Ray와 관련해서 쓰게 된다면 $P(t) = A + tB$로 아래와 같이 표현할 수 있다.

$r^2 = (A + Bt - C) \cdot (A + Bt - C) = (Bt + (A - C))$

$(B \cdot B)t^2 + 2tB \cdot (A - C) + (A - C) \cdot (A - C) - r^2 = 0$

 

위 수식은 t에 관한 2차 방정식으로 근의 공식을 이용하면 t의 값이 존재하는지 확인할 수 있다.

$근의 공식 = \frac {-b \pm \sqrt {b^2 - 4ac}} {2a} = \frac {-b_{half} \pm \sqrt {b_{half}^2 - ac}} {a}$

여기서 $b_{half}^2 - ac >= 0$를 충족한다면 t의 값이 존재하는 것으로 교점이 있다는 것을 확인할 수 있다.

따라서 계산식은 아래와 같다.

$a = (B \cdot B)$

$b = B \cdot (A - C)$

$c = (A - C) \cdot (A - C) - r^2$

 

    if (hit_sphere(img->sp, ray))
        color = mul_vec_(make_vec(1.0, 0.0, 0.0), 255.999);
    else
    {
        t = 0.5 * (unit_vec(ray.direction).y + 1.0);
        color = mul_vec_(add_vec(mul_vec_(make_vec(1.0, 1.0, 1.0), (1.0 - t)), \
                        mul_vec_(make_vec(0.5, 0.7, 1.0), t)), 255.999);
    }

그렇게 되면 결과적으로 my_mlx_pixel_put에서 color를 결정하는 것이 된다.

hit_sphere에서의 결과값이 1이라면 구와 교점이 있는 것으로 붉은색을 칠한다.

 

result

위와 같이 구가 형상화 된다.


ex05

이제 구에도 그라데이션을 그려봅시다!

 

double  hit_sphere(t_sphere sp, t_ray ray)
{
    t_vec   oc;
    double  a;
    double  b;
    double  c;
    double  discriminant;

    oc = sub_vec(ray.origin, sp.centor);
    a = dot_vec(ray.direction, ray.direction);
    b = dot_vec(oc, ray.direction);
    c = dot_vec(oc, oc) - (sp.radius * sp.radius);
    discriminant = b * b - a * c; // 점화식
    if (discriminant < 0)
        return (-1.0);
    else
        return ((-b + sqrt(discriminant)) / a); // 근의 공식
}

바뀐것은 근의 값을 결과값으로 반환한다는 것뿐이다.

 

    t = hit_sphere(img->sp, ray);
    if (t > 0.0)
    {
        tmp = unit_vec(sub_vec(at_ray(ray, t), img->sp.centor));
        color = mul_vec_(add_vec(make_vec(1.0, 1.0, 1.0), tmp), 255.999 * 0.5);
    }
    else
    {
        t = 0.5 * (unit_vec(ray.direction).y + 1.0);
        color = mul_vec_(add_vec(mul_vec_(make_vec(1.0, 1.0, 1.0), (1.0 - t)), \
                        mul_vec_(make_vec(0.5, 0.7, 1.0), t)), 255.999);
    }

이렇게 t의 값을 결정하고 양수일 때 색을 결정하도록 해주면 된다.

 

result

$t = \frac {-b_{half} - \sqrt {b_{half}^2 - ac}} {a}$

 

$t = \frac {-b_{half} + \sqrt {b_{half}^2 - ac}} {a}$

 

사실 근은 2개로 적용된다.

 


 

Phong Lighting Model

다음 예제를 진행하기 전에 phong Lighting Model에 대한 이론적 부분을 알고 가야한다.

phong Lighting Model은 기본적으로 3가지 빛의 요소로 계산이 가능하다.

  1. Ambient Reflection(주변광)
    • 다른 물체에 의한 반사광, 대기 중의 산란광 등을 단순화시킨 요소이다.(빛이 닿지 않아도 어둡진 않다.)
    • $I = k_aI_a$
    • $k_a는 주변광 계수, I_a는 광원의 세기$
  2. Diffuse Reflection(확산광 / 난반사)
    • 같은 방향으로 빛이 들어와도 서로 다른 방향으로 퍼지는 형태이다.
    • $I\ =\ k_dI_dcos\theta\ =\ k_dI_d(\hat N \cdot \hat L)$
    • $I_d는\ 광원의\ 세기,\ k_d는\ 확산광\ 계수,\ \hat N은\ 단위\ 법선\ 벡터,\ \hat L은\ 단위\ 광원\ 벡터$
  3. Specular Reflection(경면광 / 정반사)
    • $I\ =\ k_sI-lcos^n\phi\ =\ k_sI-l(\hat R \cdot \hat V)^n$
    • $k_s는\ 경면광\ 계수,\ I_s는\ 광원의\ 세기,\ ^n는 광택 계수$

 

ex06

일단 ambient Reflection만을 적용시킨다.

그리고 광원이 있어야 하기 때문에 light라는 구조체를 추가함과 동시에 다른 구조체의 요소를 추가해주었다.

typedef struct  s_rec
{
    t_vec   p;
    t_vec   normal;
    double  tmin;
    double  tmax;
    double  t;
    int     front_face;
    t_vec   ambient;
    t_vec   albedo; // 추가
}   t_rec;

typedef struct  s_sphere
{
    t_vec   centor;
    double  radius;
    t_vec   albedo; // 추가
}   t_sphere;

typedef struct s_light // 생성
{
    t_vec   origin;
    t_vec   light_color;
    double  bright_ratio;
}   t_light;

 

표현한 것을 잘 볼 수 있게 바닥처럼 생각할 수 있는 큰 구 하나와 색의 차이가 있는 2개의 구를 그려서 보자!

#include <stdio.h>
#include <stdlib.h>
#include "../../mlx/mlx.h"
#include "vector.h"
#include "ray.h"

# define WIN_WIDTH 800
# define WIN_HEIGHT 600

# define IMG_WIDTH 800
# define IMG_HEIGHT 600

typedef struct  s_img
{
    void        *img_ptr;
    char        *data;
    int         size_l;
    int         bpp;
    int         endian;
    t_sphere    *sp;
}   t_img;

typedef struct s_mlx
{
    void        *mlx_ptr;
    void        *win;
}   t_mlx;

t_vec   phong_lighting(t_light light, t_rec rec)
{
    t_vec   light_color;

    light_color = make_vec(0, 0, 0);
    light_color = add_vec(light_color, rec.ambient);
    return (min_vec(mul_vec(light_color, rec.albedo), make_vec(1, 1, 1)));
}

void	my_mlx_pixel_put(t_img *img, t_cam cam, int x, int y)
{
    char	*dst;
    double  	u;
    double  	v;
    double  	t;
    int     	rgb_color;
    t_ray   	ray;
    t_vec   	color;
    t_vec   	tmp;
    t_rec   	rec;
    t_light 	light;

    u = (double)x / (double)(IMG_WIDTH - 1);
    v = (double)y / (double)(IMG_HEIGHT - 1);
    ray = make_ray(cam.origin, sub_vec(add_vec(add_vec(cam.left_bottom_corner, \
                                                mul_vec_(cam.horizontal, u)), \
                                                mul_vec_(cam.vertical, v)), \
                                                cam.origin));
    rec.ambient = mul_vec_(make_vec(1, 1, 1), 0.1); // ambient set
    if (is_hit_sphere(img->sp, ray, &rec, 9999))
    {// color of phong model
        light = make_light(make_vec(0, 5, 0), make_vec(1, 1, 1), 0.5); // 빛 생성
        tmp = phong_lighting(light, rec); // phong model에 따른 색 분포 벡터 생성
        color = mul_vec_(tmp, 255.999);
    }
    else
    {
        t = 0.5 * (unit_vec(ray.direction).y + 1.0);
        color = mul_vec_(add_vec(mul_vec_(make_vec(1.0, 1.0, 1.0), (1.0 - t)), \
                        mul_vec_(make_vec(0.5, 0.7, 1.0), t)), 255.999);
    }
    rgb_color = ((int)color.x << 16) | ((int)color.y << 8) | (int)color.z;
	dst = img->data + (x * img->bpp / 8) + ((IMG_HEIGHT - y - 1) * img->size_l);
	*(unsigned int *)dst = rgb_color;
}

int main(void)
{
    t_mlx   *mlx;
    t_img   img;
    t_cam   cam;
    int     count_w;
    int     count_h;

    mlx = malloc(sizeof(t_mlx));
    mlx->mlx_ptr = mlx_init();
    mlx->win = mlx_new_window(mlx->mlx_ptr, WIN_WIDTH, WIN_HEIGHT, "Phong Model");
    img.img_ptr = mlx_new_image(mlx->mlx_ptr, IMG_WIDTH, IMG_HEIGHT);
    img.data = mlx_get_data_addr(img.img_ptr, &img.bpp, &img.size_l, &img.endian);
    img.sp = malloc(sizeof(t_sphere) * 3);
    img.sp[0] = make_sphere(make_vec(-1, 0, -5), 2, make_vec(0.5, 0, 0));
    img.sp[1] = make_sphere(make_vec(1, 0, -5), 2, make_vec(0, 0.5, 0));
    img.sp[2] = make_sphere(make_vec(0, -1000, -100), 999, make_vec(1, 1, 1));
    cam = make_cam(make_vec(0, 0, 0), 2.0 * 8.0 / 6.0, 2.0, 1.0);
    count_h = IMG_HEIGHT;
    while (--count_h >= 0)
    {
        count_w = -1;
        while (++count_w < IMG_WIDTH)
            my_mlx_pixel_put(&img, cam, count_w, count_h);
    }
    mlx_put_image_to_window(mlx->mlx_ptr, mlx->win, img.img_ptr, 0, 0);
    mlx_loop(mlx->mlx_ptr);
    free(img.sp);
    free(mlx);
    return (0);
}

설명할 것은 ambient의 수식에 관한 것 뿐이다.

albedo는 $k_a$를 뜻해서 반사광 계수를 벡터화 시킨것이다.(색의 벡터을 뜻하는 것으로 추측)

이것은 make_sphere에서 마지막 변수로 정의되었고 이를 rec가 가져온다.

그리고 처음 light_color는 빛의 색을 말하지만 후에 light_color는 빛의 강도까지 표현되는 것으로 생각하고 있다.(색에도 빛이 있으니까)

 

result

이렇게 보면 두개의 구가 겹쳐있는데 이 두개의 구가 정말 잘...보면 색이 사알짝 다르다.

구분이 안가면 패스!(나도 잘 안감ㅎ)

 


ex07

 

이제 diffuse와 specular를 적용시키면 된다.

참고로 수식이해가 된다면 굉장히 쉽게 적용할 수 있는 상황이 된다.

 

일단 추가되는 함수는 아래에 있다.

t_vec   reflect(t_vec v, t_vec n)
{
    return (sub_vec(v, mul_vec_(n, dot_vec(v, n) * 2.0)));
}

t_vec   specular_lighting(t_light light, t_rec rec, t_ray ray)
{
    t_vec   light_dir;
    t_vec   specular;
    t_vec   view_dir;
    t_vec   reflect_dir;
    double  spec;
    double  ksn;
    double  ks;

    light_dir = unit_vec(sub_vec(light.origin, rec.p));
    view_dir = unit_vec(mul_vec_(ray.direction, -1.0));
    reflect_dir = reflect(mul_vec_(light_dir, -1.0), rec.normal);
    ksn = 64;
    ks = 0.5;
    spec = pow(fmax(dot_vec(view_dir, reflect_dir), 0.0), ksn);
    specular = mul_vec_(mul_vec_(light.light_color, ks), spec);
    return (specular);
}

t_vec   diffuse_lighting(t_light light, t_rec rec)
{
    t_vec   diffuse;
    t_vec   light_dir;
    double  kd;

    light_dir = unit_vec(sub_vec(light.origin, rec.p));
    kd = fmax(dot_vec(rec.normal, light_dir), 0.0);
    diffuse = mul_vec_(light.light_color, kd);
    return (diffuse);
}

 

각각의 vec의 합을 반환하면 된다.

    light_color = add_vec(light_color, rec.ambient);
    light_color = add_vec(light_color, diffuse_lighting(light, rec));
    light_color = add_vec(light_color, specular_lighting(light, rec, ray));
    return (min_vec(mul_vec(light_color, rec.albedo), make_vec(1, 1, 1)));

 

result

 

아래 그림은 ambient와 diffuse를 적용시킨 사진이다.

ambient + diffuse

 

아래 그림은 ambient, diffuse 그리고 specular를 적용시킨 phong model인 것이다.

ambient + diffuse + specular

마지막 그림에서 광원의 존재를 알수 있었다~!

 

예제는 오늘로 끝!

'42일기' 카테고리의 다른 글

CPP [캐스팅]  (0) 2022.02.25
CPP [OOP 상속성]  (0) 2022.02.15
Minishell(3)  (0) 2021.12.20
python Dict, set 활용  (0) 2021.10.15
Philosophers  (2) 2021.07.09

+ Recent posts