https://github.com/Likilee/mini_raytracing_in_c
참고
{f카메라t픽셀}
: 카메라부터(from) 픽셀까지(to)- static : 프로그램 시작시 사용자가 정하고 변하지 않는 세팅값.
00. 과제 개요
C를 이용해 레이트레이서를 만드는 과제. 레이트레이싱 : 물체가 보이는 물리적 현상을 시뮬레이션하는 가장 직접적인 방법
렌더링 : 3차원 공간의 객체를 2차원 화면인 하나의 장면으로 바꾸어 표현하는 것. 2차원으로 표시된 물체를 3차원 물체처럼 보이게 하는것.
- 변환 : 3차원 모델 좌표계의 정점들을 2차원에 투영된 모델로 바꾸는 것
- 색칠 : 객체의 색체/조명/매핑으로 원하는 효과로 표현하는것.
01. RGB PPM
ppm형식에 맞게 이미지 그라데이션을 만드는 실습.
02. 벡터 및 점, 색 자료형/연산 세팅.
필요한 연산 : 벡터 생성, 백터 값 변경, 벡터 크기 연산(double반환), 벡터끼리/좌표와의 합/차, 벡터의 스칼라 곱, 벡터의 스칼라 나누기(역수의 곱), 내적(벡터입력 double반환), 외적(벡터입력 벡터 반환), 단위벡터 구하기.
벡터는 방향과 크기를 가지고 있다.
내적의 의미 : a ⋅ b
연산의 경우,
- 두 벡터간의 사이각을 구할수 있음. -> 좌표값만 아는 경우
a_x * b_x + b_x * b_y = |a||b|cos(θ)
이므로… - 특정 벡터에 투영된 다른 벡터의 크기를 구할수 있다.
a벡터에 투영된 b벡터의 길이는
|b|cos(θ)
이므로,|b|cos(θ) = (a_x * b_x + b_x * b_y) / |a|
- 특정 대상이 특정 관점을 기준으로 누가 앞에 있는지 구할수 있음.
- 특정 각도 내에 대상이 존재하는지 파악 가능.
- 평면과의 최단거리를 구할때 사용 가능.
외적의 의미
- 면의 법선벡터를 구할수 있음.
- 빛의 방향과 평면의 법선사이의 각도를 이용해 평면에 적용될 빛의 영향 결정 가능.
03. 카메라 및 뷰포트
오른손 좌표계 사용할것이다.
3.1 camera시점(f카메라t픽셀
) 벡터 만들기.
f카메라t픽셀
벡터는 카메라에서 뷰포트 픽셀로 나아가는 벡터이다. 이 벡터에 모델이 hit되면, 뷰포트 픽셀에 색을 칠한다. 이 f카메라t픽셀
벡터를 구하기 위해서는 일단 뷰포트의 위치뿐만 아니라 뷰포트의 폭/높이 벡터에 대해 알아야한다.
3.1.1 뷰포트 위치
뷰포트의 위치는 FOV(Field of view, 확각, static)값과 카메라가 바라보는 방향을 통해 알 수 있다. 거리와 방향벡터만 알고 있으면 공간에서 지정을 특정할 수 있기 때문이다.
- 방향벡터 :
.rt
파일에 입력하는 값. - 거리 :
뷰포트폭 / (2 * tan(θ/2)), 이때 θ = PI * 시야각 / 180
3.1.2 뷰포트의 폭/높이 벡터
이를 알기 위해서는 아래의 그림처럼 카메라가 바라보는 방향을 y축으로 하는 기저공간을 만들 필요가 있다. 이 기저공간의 xz평면이 뷰포트이다. 우선, 뷰포트(캠버스)는 회전하지 않는다. 즉 우리가 고개를 들거나 돌리는 화면은 고려하더라도, 고개를 좌우로 꺾는(뷰포트 회전)은 가정하지 않는다. 이러한 가정 하에서 뷰포트의 단위벡터를 구할수 있는 아주 괜찮은 트릭 하나를 얻는다.
- 카메라 방향벡터와 기존 좌표의 z축 단위벡터를 외적한다. 그러면 horizon성분의 뷰포트 벡터를 얻을 수 있다.
- horizon성분의 뷰포트 벡터와 카메라 방향벡터를 외적한다. 그러면 vertical성분의 뷰포트 벡터를 얻을 수 있다.
3.1 {f카메라t픽셀}
벡터 계산
- 뷰포트의 왼쪽 상단부터 우측 하단 픽셀까지 차례대로 채워나간다. 뷰포트 단위벡터들로부터
{f중앙t좌측상단}
벡터를 만든다.- 계산식은
(1 - width)horizon + (height - 1)vertical
.
- 계산식은
- 카메라 방향벡터와 위에서 구한 좌하단을 가리키는 벡터를 더하면 뷰포트 좌 하단 정중앙을 가리키는 벡터를 알수 있다(
{f카메라t픽셀}
벡터).
결과
04. 형태 표현 및 레이트레이싱
04.1 개요
출처 : 위키피디아
원래는 빛이 물체에 부딪혀서 반사된 빛을 보는것이다. 하지만 이 방법을 그대로 하면 카메라에 잡히지 않는 빛까지 모두 계산해야한다. 따라서 반대로 카메라로부터 빛(ray)을 쏘고, 그것이 물체와 충돌하면 이를 뷰포트에 표시하는 방법이다.
- (이전)
{f카메라t픽셀}
벡터를 계산한다. - (4.1)
{f카메라t픽셀}
벡터가 특정 위치의 물체를 hit하는지 판단한다. - (4.2) hit하면 그 위치의 색을 계산하고 출력한다. -> 색의 표현
4.1 교점좌표/법선벡터 계산.
- 직선벡터방정식 :
unit_{f카메라t픽셀}t + src
. (t
: 배율,src
: 시작점) - 원의 방정식 :
(P - C) ⋅ (P - C) = r^2
. (P
: 구 위의 임의의 좌표,C
: 구 중심 좌표)- 교점좌표 : 구 방정식의
P
에 직선방정식 대입 후, 근의 공식 중 작은것. - 법선벡터 :
unit_{f원의중심t교점}
- 참고 : t가 음수인 경우는 제외해야한다. -> t가 음수면 카메라 뒤쪽이기 때문.
- 교점좌표 : 구 방정식의
4.2 색(rgb)의 표현
빛을 추적하여 색을 계산하는 기법에는 여러가지가 있는데 여기서는 퐁 반사 기법을 사용한다.
뷰포트에 표시될 색은 rgb값 각각이 [0, 255]의 범위로 표현되며, 색과 밝기에 영향을 미친다. 퐁 기법에서는 다음과 같이 rgb값이 정해진다.
- rgb값 =
표면의 빛
*표면의 반사율
- 표면에 도달한 빛 =
주변광
+분산광
+반사광
- 주변광 :
주변광 강도(ka)
*주변광 색
+주변광 밝기
- 분산광 :
분산 강도(kd)
*광원
- 반사광 :
반사 강도(ks)
*spec^n
*광원
- 주변광 :
광원은 rgb값으로, 색과 밝기를 포함.
이때 광원, 표면 반사율, 화면의 밝기 등은 .rt
파일에서 설정하거나 정해진 값이므로, 우리가 계산해야 하는 요소는 주변광, 분산광, 반사광이다.
4.3.1 주변광(Ambient)이란?
단일 광원이면서 벽이 흑채인 완벽한 암실이 아닌 일반적인 상황에서 물체는 빛이 들지 않는 부분도 반사/산란에 의해 완벽한 암흑이 아니다. 이러한 일반적인 상황에서 베이스로 깔고 갈 빛을 주변광이라고 한다.
주변광 = 주변광 강도(ka)
* 주변광 색
+ 주변광 밝기
- 주변광 강도(ka) : 주변광의 밝기. static.
- 주변광 색 : 주변광이 어떤색인지. 주로 광원의 색과 같아야 자연스럽다. static.
- 주변광 밝기 : 오프셋 값. static.
4.3.2 분산광(Diffuse)이란?
빛은 입자이면서 파동이다. 입자적으로 생각해본다면 빛이 면에 비스듬할수록 조사되면 크기는 넓어지고 광자의 양은 그대로이므로 빛의 강도가 약해질것이다. 이를 고려한 것이 분산광이며, 분산광을 적용시키는 순간 모델링의 격이 달라진다. 분산광 :
- 분산강도(kd) :
kd = unit_교점법선 ⋅ unit_{f교점t광원}
, 0 이상. - 광원 : rgb값. static.
4.3.3 반사광(Specular)이란?
매끄러운 물체는 광원을 직접적으로 반사한다. 뷰포트방향(바라보는 방향)와 반사광의 방향이 일치할수록 밝기 증가가 기하급수적으로 커진다.
반사광 : 반사강도(ks)
* spec^n
* 광원
- 반사강도(ks) : 표면이 매끈할수록 1에 가까움. static.
- spec :
unit_{f카메라t교점}
⋅unit_반사광
- n : 숫자 n은 Phong 지수라고 하며 표면의 겉보기 매끄러움을 제어하는 값. static.
- 광원 : rgb값. static. 위키피디아 참고
PHONG 종합 결과
그림자 구현
일단 바닥에만 그림자가 적용되게 테스트. 이후 바닥에만 적용하는 것이 아니라, 물체 표면에도 적용시킬 것.
카메라의 시점이 바닥(xy평면)을 바라보고, 물체와 충돌하지 않는다면 그림자가 있는지 확인한다.
- 바닥(xy평면)과 카메라 시점의 교점을 구한다. 식은 위의 그림을 참고한다.
- 교점에서 광원 방향의 직선 벡터식이 물체와 충돌하는지 확인한다.
- 충돌하지 않으면 그냥 바닥색을 출력, 충돌하면 어두운 색을 출력한다.
구현
보완할점
- 충돌 여부를 확인할때, 광원 뒤에 물체가 있는지 확인한다 : 물체-광원-벽 순서여도 벽에 그림자가 생길 것이다. -> t값을 비교하여 해결
- 그림자의 색도 고려해야 할수 있다. : 광원이 백색광이 아니고, 여러개일 때. -> 보너스라서 적용 안함.
- 바닥 색도 고려한 그림자? -> 그림자를 ambient광으로 대체하여 해결.
- 그림자를 표면 검사할 때도 표시되게 한다. -> hit_object함수를 재사용하여 hit여부만 파악하여 해결.