
유전체는 더 큰 범주의 절연체임
절연체는 아예 전기가 흐르지 못하고, 전극이 와도 분극되지 못하고 그냥 아예 전기가 막힘
유전체는 전기의 흐름을 방해하는 의미에서 절연체임
유전체는 전극이 흐르면 이를 +, -로 분극을 함
따라서 좋은 유전체는 좋은 절연체이지만,
역은 성립하지 않을 수 있음

= 매질에 따른 굴절율,
= 법선으로부터의 입사각, 굴절각
스넬 법칙:
빛은 매질이 바뀔때, 빛이 닿은 경계면의 평행한 방향의 성분만 유지가 된다는걸 의미함.
각 물체는 굴절률이 다름
빛이 물체에 닿았을때 굴절이 되는데, 빛의 방향의 성분중에서 빛이 닿은 경계면과 평행한 성분만 유지가 되고, 나머지는 굴절율에 의해 굴절이 되는걸을 의미함.

노란 벡터 = 빛 방향,
파란 벡터 = 경계면에서의 Normal
노란 점선 벡터 = 굴절된 후의 빛 벡터
회색 점선 벡터 = 경계면과 평행한 방향(방향만 유지된거임)
이렇게 경계면과 평행한 방향은 유지하면서, 다른 각도로 굴절되는 것이
스넬 법칙임
중요!!!
입사 벡터, 법선 벡터는 모두 정규화된 벡터임!!
이 식을 살펴보자
우리에게 필요한 것은 임
왜냐고?
표면 바깥에서 굴절되어 표면 내부로 들어올 것이기 때문에ㅇㅇ
따라서 료이키 텐카이를 통해 만 남기고 이항 정리를 함
가 됨

= 경계에 닿은 빛이 굴절된 벡터
= 굴절각
= 법선 에서 수직인 벡터
= 법선 에서 평행인 벡터
위의 사진에서
이라는 것을 확인할 수 있음
핵심은 입사각 을 이용하는 거임
현재 우리는 을 알고있음
이라는 걸 알 수 있음
위에처럼 ㅇㅇ

여기서 우리는 을 구할 수 있음
Metal재질 반사각 전개
이 링크의 내용을 이용해 을 찾은 후 값을 찾아주면됨
아래에서 다시 설명함
= 직각삼각형의 밑변 / 빗변
빗변 = 정규화된 벡터 = 1
= 밑변 =입사 벡터 (내적) 법선 = 입사벡터 * 법선벡터
입사벡터 = 정규화된 벡터 = 1
법선벡터 = 정규화된 벡터 = 1
입사 벡터 법선 =입사 벡터() 법선() = =
하지만 내적은 스칼라값이므로, 정규화된 벡터인 법선과 곱해서 정확한 밑변 벡터를 찾음
->
=
따라서 식을 전개해보면
이 되는걸 알 수 있음
여기서 은 과 방향이 동일하고
두 수직성분의 비율은 두 굴절율의 비율과 같음
위에서 살펴본 위 특성에 의해
가 되는 것을 알 수 있음
2번은 경계와 평행한 성분인 수직성분을 구함
이번에는 경계와 수직인 성분인 평행성분을 구할 차례임
안타깝게도 수직인 성분에 대해서는
두 굴절률의 비율에 따라 값이 변화하지 않음
따라서 피타고라스 공식을 이용해서 구해주면 됨
피타고라스 정리
- 빗변 = 밑변 + 높이
이때 각 변은 스칼라 값임
그냥 크기라는 거임따라서 값을 구해주면, 나중에 벡터를 곱해서 방향을 만들어줘야함
빗변 = =정규화된 벡터 = 1
밑변 = || = ||
높이 = ||
이걸 정리해보면
이 됨
이항정리를 하면
위처럼 정리가 됨
그리고 이미 구한 을 이용하면
이라는 식을 구할 수 있음

다시 이 사진을 보면
이라는 것을 알 수 있음
//Vector3.h...
inline Vector3 refract(const Vector3& uv, const Vector3& n, double etai_over_etat)
{
auto cos_theta = std::fmin(dot(-uv, n), 1.0);
Vector3 r_out_perp = etai_over_etat * (uv + cos_theta*n);
Vector3 r_out_parallel = -std::sqrt(std::fabs(1.0 - r_out_perp.length_squared())) * n;
return r_out_perp + r_out_parallel;
}
먼저 를 구하고,
그걸 이용해서 수직성분, 평행성분을 구해준 후 더하면
굴절된 벡터인 를 구하는거임!
Dielectric머티리얼 구현
class Dielectric : public Material
{
public:
Dielectric(double refraction_index) : refraction_index(refraction_index) {}
bool scatter(
const Ray& r_in,
const HitRecord& rec,
Color& attenuation,
Ray& scattered) const override
{
attenuation = Color(1,1,1);
double ratio_of_refraction_index = rec.front_face ? 1.0 / refraction_index : refraction_index;
Vector3 unit_direction = unit_vector(r_in.direction());
Vector3 refracted = refract(unit_direction, rec.normal, ri);
scattered = Ray(rec.p, refracted);
return true;
}
private:
//굴절률
double refraction_index;
};
여기서 핵심은
double ratio_of_refraction_index = rec.front_face ? 1.0 / refraction_index : refraction_index;
임!
공기의 굴절률은 1에 수렴함
공기에서 물체의 내부로 들어가는건 front_face인거임
따라서 굴절률 비율은
인거임!
그리고 내부에서 외부로 나올때는 front_face가 아니므로
가 되는거임
이제 사용해보자

한번 다이아몬드의 굴절률을 사용해보자
auto material_left = make_shared<Dielectric>(2.417);
이렇게 수정하고 렌더링을 해보면...

![]() | ![]() |
|---|
밀도가 높은 물질에서 밀도가 낮은 물질로 빛이 나가면 어떻게 될까
무조건 굴절이 될까?
절대 그렇지 못함
위의 세션인 스넬 법칙에서 살펴봤듯
처럼 식이 정리 가능하다.
밀도 높은 물질의 굴절률 = = 2.4(다이아몬드)
밀도 낮은 물질의 굴절률 = = 1(공기)
라고 해보자
이 됨
근데 이러면 문제가 있음
가 대략 0.45정도만 되어도 가 1을 넘어가버림
는 최대값이 1임
따라서 물질 내부에서 굴절이 안되는 각도의 빛은 반사가 됨
즉, 가 1이 넘어가는 경우, 임계각을 넘어가는 경우는 굴절이 아닌 반사를 해줘야함
는 피타고라스 정리를 이용해서 구할 수 있음
임
위의 스넬법칙에서 이라는 것도 알아냄
따라서 Dielectric머티리얼의 scatter메서드를 조금 수정해주면 됨
class Dielectric : public Material
{
public:
Dielectric(double refraction_index) : refraction_index(refraction_index) {}
bool scatter(
const Ray& r_in,
const HitRecord& rec,
Color& attenuation,
Ray& scattered) const override
{
attenuation = Color(1,1,1);
double ratio_of_refraction_index = rec.front_face ? 1.0 / refraction_index : refraction_index;
Vector3 unit_direction = unit_vector(r_in.direction());
//add
double cos_theta = std::fmin(dot(-unit_direction, rec.normal), 1.0); //코사인 세타
double sin_theta = std::sqrt(1 - cos_theta * cos_theta);
bool cant_refract = ratio_of_refraction_index * sin_theta > 1;
Vector3 direction;
if (cant_refract)
{
direction = reflect(unit_direction, rec.normal);
}
else
{
direction = refract(unit_direction, rec.normal, ratio_of_refraction_index);
}
scattered = Ray(rec.p, direction);
return true;
}
private:
//굴절률
double refraction_index;
};
cos_theta를 이용해서 sin_thete를 구하고
굴절 가능한지 판별을 한 후
reflect/refract를 수행함
대충 물속(1.33)의 공기(1)이라고 가정하고
auto material_left = make_shared<Dielectric>(1.00/1.33);
과 같은 코드를 짬ㅇㅇ
그럼 다음과 같은 이미지 차이가 생김
왼쪽은 전반사 코드 추가 전/ 오른족은 전반사 코드 추가 후
전반사 추가 전 | 전반사 추가 후 |
|---|
이렇게 반사가 다르게 되는걸 볼 수 있음
프레넬 효과는 유니티 쉐이더그래프를 하며 여러번 다뤄서 익숙함

프레넬은 빛의 입사각에 따라 반사되는 빛의 양이 달라지는 것을 의미함
그리고 모든 물체는 프레넬을 가짐
위 사진을 보면
프레넬을 가진 주전자는 입사각이 수직에 가까울때는 비교적 어둡고, 더 완만할때는 빛의 반사가 많은 것을 볼 수 있음(외곽부분)
우리가 만든 구체는 저렇게 프레넬 효과가 적용되지 않은 상태여서,
약간 이상하게 보임
프레넬을 구할때,
프레넬 방정식과 슐릭 근사 중 하나를 골라서 사용하면 된다
: 입사 매질 굴절률
: 투과 매질 굴절률
: 입사각
: 굴절각
: 빛이 표면에 수직으로 입사했을때 반사율
: 입사각(0과 가까울수록 수직, 1과 가까울수록 평행)
: 굴절률
코드 구현의 난이도로 보면 슐릭근사가 더 빠르고 쉽게 계산됨
class Dielectric : public Material
{
//...
bool scatter(
const Ray& r_in,
const HitRecord& rec,
Color& attenuation,
Ray& scattered) const override
{
//...
if (cant_refract || schlick_approximate(cos_theta, ratio_of_refraction_index) > random_double())
{
direction = reflect(unit_direction, rec.normal);
}
//...
}
private:
//...
static double schlick_approximate(double cos, double ratio_of_refraction_index)
{
double r0 = (1 - ratio_of_refraction_index) / (1 + ratio_of_refraction_index);
r0 = r0 * r0;
return r0 + (1 - r0) * std::pow((1 - cos), 5);
}
};
여기서 random_double과 비교하는 이유가 있음
특정 입사각에서 반사되어야 하는 빛의 양이 0.5라고 해보자
- random_double()을 통해 0.4가 나왔다면
- 반사되어야 할때
무조건 반사- 굴절되어야 할때
조건을 만족하지 못하므로 굴절- random_double()을 통해 0.6이 나왔다면
- 반사되어야 할때
무조건 반사- 굴절되어야 할때
조건을 만족하므로 반사이렇게 조건에 따라 확률적으로 빛을 반사시켜 프레넬 효과를 만드는거임
이걸 이용해서 기존의 왼쪽 구체를 hollow유리 구체로 만들고,
그 구체 앞에 작은 다이아몬드 구체를 만들어보도록 하겠음
유리의 굴절률은 1.5정도임
#include "Camera.h"
#include "Color.h"
#include "HittableList.h"
#include "hittable.h"
#include "RTWeekend.h"
#include "Sphere.h"
using namespace std;
int main()
{
HittableList world;
auto material_ground = make_shared<Lambertian>(Color(0.8, 0.8, 0.0));
auto material_center = make_shared<Lambertian>(Color(0.1, 0.2, 0.5));
//hollow glass
auto material_left = make_shared<Dielectric>(1.5);
auto material_bubble = make_shared<Dielectric>(1.00/1.5);
//diamond sphere
auto material_left_front = make_shared<Dielectric>(2.417);
auto material_right = make_shared<Metal>(Color(0.8, 0.6, 0.2), 0.5);
world.add(make_shared<Sphere>(Point3( 0.0, -100.5, -1.0), 100.0, material_ground));
world.add(make_shared<Sphere>(Point3( 0.0, 0.0, -1.2), 0.5, material_center));
//hollow glass
world.add(make_shared<Sphere>(Point3(-1.0, 0.0, -1.0), 0.5, material_left));
world.add(make_shared<Sphere>(Point3(-1.0, 0.0, -1.0), 0.45, material_bubble));
//diamond sphere
world.add(make_shared<Sphere>(Point3(-1.0, 0.0, -0.3), 0.1, material_left_front));
world.add(make_shared<Sphere>(Point3( 1.0, 0.0, -1.0), 0.5, material_right));
Camera cam;
cam.aspect_ratio = 16.0 / 9.0;
cam.image_width = 1200;
cam.samples_per_pixel = 300;
cam.max_depth = 50;
cam.render(world);
}
이렇게 수정함
그러고 렌더링 해보면...
