[MirRT]#10平面実装[追加]


実際には平面を表す部分がないため,既存のコード,構造体に平面の関数を増やした.やっとインターンシップから抜け出した…!

追加されたコード


structures.h

struct s_plane
{
	t_point3    center; // 평면의 어느 한 지점.
	t_vec3      dir; // 평면이 가리키는 방향, 어떻게 기울여져 있는지
};
平面情報を含む構造体を作成します.平面は、ある点であるベクトルに無限に延びる形状であるため、範囲は存在しない.

hit.c

#include "trace.h"

t_bool hit(t_object *world, t_ray *ray, t_hit_record *rec)
{
    t_bool  hit_anything;
    t_hit_record *temp_rec;

    temp_rec = rec;
    hit_anything = FALSE;
    while (world) // world->next로 훑어가며 등록된 구조체를 전부 체크함
    {
        if (hit_obj(world, ray, temp_rec))
        {
            hit_anything = TRUE;
            temp_rec->tmax = temp_rec->t;
            rec = temp_rec;
        }
        world = world->next;
    }

    return (hit_anything);
}

t_bool hit_obj(t_object *world, t_ray *ray, t_hit_record *rec)
{
    t_bool hit_result;

    hit_result = FALSE;
    if (world->type == SP)
        hit_result = hit_sphere(world, ray, rec);
    else if (world->type == PL)
        hit_result = hit_plane(world, ray, rec);
    else if (world->type == CY)
        hit_result = hit_cylinder(world, ray, rec);
    return (hit_result);
}
グラフィックによっては衝突チェック式が異なるので、条件文で分岐します.

hit_plane.c

#include "structures.h"
#include "utils.h"
#include "trace.h"

t_bool	hit_plane(t_object *pl_obj, t_ray *ray, t_hit_record *rec)
{
	t_plane	*pl;
	float	numrator;
	float	denominator;
	float	root;

	pl = pl_obj->element;
	denominator = vdot(ray->dir, pl->dir);
	if (fabs(denominator) < EPSILON)
		return (FALSE);
	numrator = vdot(vminus(pl->center, ray->origin), pl->dir);
	root = numrator / denominator;
	if (root < rec->tmin || rec->tmax < root)
		return (FALSE);
	rec->t = root;
	rec->p = ray_at(ray, root);
	rec->normal = pl->dir;
	set_face_normal(ray, rec);
	rec->color = pl_obj->color;
	return (TRUE);
}

数式



垂直ベクトル間の内積結果は0



ベクトルの内積がまた現れた.互いに垂直なベクトルを内積すると結果値は0になりますが、考えてみれば当然の結果です.内積とは、あるベクトルが別のベクトルの方向にどれだけ役立つかを意味し、垂直であれば何の役にも立たないが、干渉もないので、0の値を得ることができる.

ソース:https://ansohxxn.github.io/c++ games/chapter3-2-2/

判別式を使用した競合式


今もう一度写真を見ましょう.

ベクトルPは、カメラから発せられる任意の光線である.
ベクトルP 0は、パラメータとして与えられる平面上のある点である.平面の一部とも言える.
ベクトルNは、因子によって供給される平面の方向ベクトルである.この方向ベクトルの垂直方向には、平面が伸びています.
上述したように,互いに垂直な2つのベクトルを内積すると0が得られることを知っている.
ベクトルNに垂直なベクトルは、平面の一部P 0であり、平面を通過する点Pに接するベクトルであり、ベクトルP−ベクトルP 0である.
次のように数式で表します.
(P⃗−P⃗0)⋅N⃗=0(\vec P -\vec P_0)\cdot\vec N = 0(P−P0​)⋅N=0
次のように式を展開します.
(O+D⃗∗t−P⃗0)⋅N⃗=0(O +\vec D * t -\vec P_0)\cdot\vec N = 0(O+D∗t−P0​)⋅N=0
t(D⃗⋅N⃗)+(O−P⃗0)⋅N⃗=0t(\vec D\cdot\vec N) + (O -\vec P_0)\cdot\vec N = 0t(D⋅N)+(O−P0​)⋅N=0
Oは原点、ベクトルDはベクトルPの単位ベクトル、tはPまでの距離(スカラー)!
この式を距離tの式に展開します.以下に示します.
t(D⃗⋅N⃗)=−(O−P⃗0)⋅N⃗t(\vec D\cdot\vec N)= -(O -\vec P_0)\cdot\vec Nt(D⋅N)=−(O−P0​)⋅N
t=−(O−P⃗0)⋅N⃗D⃗⋅N⃗=(P⃗0−O)⋅N⃗D⃗⋅N⃗t = { -(O -\vec P_0)\cdot\vec N\over\vec D\cdot\vec N}= { (\vec P_0 - O)\cdot\vec N\over\vec D\cdot\vec N}t=D⋅N−(O−P0​)⋅N​=D⋅N(P0​−O)⋅N​
上記の式が判別式、tが負数であれば、その光線、ベクトルPは平面と交差しないベクトルとなる.
平面のベクトルがカメラのベクトルと逆であっても、衝突と判定します.絶対値で判別式の条件をチェックします.

コード実装を使用する場合

t_bool      hit_plane(t_object *pl_obj, t_ray *ray, t_hit_record *rec)
{
    t_plane *pl = pl_obj->element;
    float numrator; // 판별식의 분자
    float denominator; // 판별식의 분모
    float root;
    denominator = vdot(ray->dir, pl->normal);
    if (denominator < EPSILON) // 분모의 값이 0보다 작거나 같을 경우 충돌하지 않는다.
        return (FALSE);
    numrator = vdot(vminus(pl->point, ray->orig), pl->normal);
    root = numrator / denominator;
    if (root < rec->tmin || rec->tmax < root) 
        return (FALSE);
    rec->t = root;
    rec->p = ray_at(ray, root);    
    // rec->normal = pl->normal;
    rec->albedo = pl_obj->albedo;
    // print_vec(normal);
    return (TRUE);
}
t=−(O−P⃗0)⋅N⃗D⃗⋅N⃗=(P⃗0−O)⋅N⃗D⃗⋅N⃗t = { -(O -\vec P_0)\cdot\vec N\over\vec D\cdot\vec N}= { (\vec P_0 - O)\cdot\vec N\over\vec D\cdot\vec N}t=D⋅N−(O−P0​)⋅N​=D⋅N(P0​−O)⋅N​
  • ベクトルO:光線起点、ray->orig
  • ベクトルD:光線の方向ベクトル(単位ベクトル)、ray→dir
  • ベクトルN:平面のベクトル(単位ベクトル)、pl→normal
  • ベクトルP 0:平面のある点、pl→point
  • リファレンス


    IllusionCatalyst
    Ray-Plane Intersection
    https://bigpel66.oopy.io/library/42/inner-circle/5