Write a Path Tracer
- C++ fast, portable, most renders use
inheritance and operator overloading is useful
Chapter 1 Output an image
-
PPM example: an example of a color RGB image stored in PPM
need PPM viewer to run the code.P3 // Magic Number means type is pixmap and colors are coding with ASCII 3 2 // 3 columns, 2 rows 255 // max colors ------声明部分------- --------------------- 255 0 0 0 255 0 0 0 255 255 255 0 255 255 255 0 0 0
Chapter 2 vec3 Class
4D vector
- 3D + a coord for geometry
- RGB + Alpha transparency channel
[vec3 → color, locations, directions, offsets]
define a vec3 class by self, and overload the operator( +, -, +=, -=, *=, /=, etc )
[Key word "inline": 指定内联函数,修饰函数,用于函数定义。
目的是消除函数调用时的时间开销,常应用于频繁执行的函数。]
Chapter 3 Rays, camera, background
Ray Class & A computation of color
Ray(射线) Function: P(t) = A + t*B
P : 3D postion
A : ray origin(起点,原点)
B : ray direction
t : real number(常数), float
( t > 0, half-line, ray; t < 0, go any where of 3D line.)
Core of Ray tracer
- send rays through pixels
- compute what color is seen in direction of those rays
Calculate form
- which ray goes from the eyes to pixel
- compute what that ray intersects(横断,贯穿)
- compute a color for that intersections point
Color(Ray) function - linear blends/ linear interpolation/ lerp(线性插值)
blended_value = ( 1 - t ) * start_value + t * end_value ( 0.0 < t < 1.0 )
Chapter 4 Adding a sphere(范围、领域、球)
原点为球心,基于半径R的球体方程:x² + y² + z² = R²
for any ( x, y, z ), if x² + y² + z² = R², then ( x, y, z ) is on the sphere and otherwise it is not.
球心原点为( cx, cy, cz ) 时,球体方程为 (x-cx)² + (y-cy)² + (z-cz)² = R²
球心到球上任意一点的向量为(P - C)
其中C = ( cx, cy, cz ), P = ( x, y, z )
可得到 dot((P - C), (P - C)) =(x-cx)² + (y-cy)² + (z-cz)², dot((P - C), (P - C)) = R²
any point P that satisfies the equation is on the sphere
Whether the ray P(t) = A + t*B hits the sphere anywhere?
If hit,
dot((P(t) - C), (P(t) - C)) = R² → dot(A + t*B - C), (A + t*B - C)) = R²
所以,t²*dot(B, B) + 2t*dot(B, A-C) + dot(A-C, A-C) - R² = 0(t和R均为常数)
该方程可以看做是关于t的二次方程, 有两个有效解、一个有效解、无解三种情况,分别可以看做是射线和球体有两个交点、一个交点和不相交。
但,若在该方程无解时,将圆心的z坐标取在反方向,将会获得位于摄像机背后的图像。
Chapter 5 Surface normals and multiple objects
Surface Normals 法线
- points out
- unit length
For a sphere, the normal is in the direction of hitpoint minus the center.
- Assume N(visualizing normal) is a unit length vector-so each component is between -1 to 1.
Map each component to the interval from 0 to 1.
Map x/y/z to r/g/b.
Assume the closest hit point ( smallest t )
Then, make an abstract class for anything might hit by ray, and make both a sphere and a list of spheres just something you can hit.
called it Hitable( an object of object oriented )
The Hitable abstract class:
#ifndef HITABLEH
#define HITABLEH
#include "ray.h"
struct hit_record {
float t;
vec3 p;
vec3 normal;
};
class hitable {
public:
virtual bool hit( const ray& r, float t_min, float t_max, hit_record& rec) const = 0;
};
#endif
The sphere:
#ifndef SPHEREH
#define SPHEREH
#include "hitable.h"
class sphere: public hitable {
public:
sphere( ) { }
sphere( vec3 cen, float r ) : center ( cen ), radius( r ) { };
virtual bool hit ( const ray& r, float tmin, float tmax, hit_record& rec ) const;
vec3 center;
float radius;
};
bool sphere :: hit ( const ray& r, float t_min, float t_max, hit_record& rec ) const {
vec3 oc =r.origin() - center;
float a = dot( r.direction( ), r.direction( ) );
float b = dot( oc, r.direction( ) );
float c = dot( oc,oc ) - radius*radius;
float discriminant = b*b - a*c;
if ( discriminant > 0 ) {
float temp = ( -b - sqrt( b*b - a*c ) ) / a;
if ( temp < t_max && temp > t_min ) {
rec.t = temp;
rec.p = r.point_at_parameter( rec.t );
rec.normal = ( rec.p - center ) / radius;
return true;
}
}
return false;
}
#endif
The list of objects:
#ifndef HITABLELISTH
#define HITABLELISTH
#include "hitable.h"
class hitable_list : public hitable {
public:
hitable_list( ) { };
hitable_list( hitable **l, int n) { list = l; list_size = n; }
virtual bool hit( const ray& r, float tmin, float tmax, hit_record& rec ) const;
hitable **list;
int list_size;
};
bool hitable_list :: hit( const ray& r, float t_min, float t_max, hit_record& rec) const {
hit_record temp_rec;
bool hit_anything = false;
double closest_so_far = t_max;
for ( int i =0; i < list_size; i++ ) {
if ( list[i] -> hit ( r, t_min, closest_so_far, temp_rec)) {
hit_anything = true;
closest_so_far = temp_rec.t;
rec = temp_rec;
}
}
return hit_anything;
}
#endif
The main:
#include
#include "sphere.h"
#include "hitable_list.h"
#include "float.h"
vec3 color( const ray& r, hitable *world) {
hit_record rec;
if( world -> hit (r, 0, 0, MAXFLOAT, rec ) ) {
return 0.5*vec3( rec.normal.x( ) + 1, rec.normal.y( ) + 1, rec.normal.z( ) + 1 );
}
else {
vec3 unit_direction = unit_vector ( r.direction( ) );
float t = 0.5 * (unit_direction.y( ) + 1.0);
return ( 1.0 - t ) * vec3 ( 1.0, 1.0, 1,0 ) + t * vec3 ( 0.5, 0.7, 1.0 );
}
}
int main ( ) {
int nx = 200;
int ny = 100;
std :: cout << "P3\n" << nx << " " << ny << "\n255\n";
vec3 lower_left_corner( -2.0, -1.0, -1.0 );
vec3 horizontal ( 4.0, 0.0, 0.0 );
vec3 vertical ( 0.0, 2.0, 0.0 );
vec3 origin ( 0.0, 0.0, 0.0 );
hitable *list[2];
list[0] = new sphere( vec3( 0, 0, -1 ), 0.5 );
list[1] = new sphere( vec3( 0, -100, 5 ), 100);
hitable *world = new hitable_list( list, 2 );
for( int j = ny - 1; j >= 0; j-- ) {
for( int i = 0; i <( nx; i++ ) {
float u = float(i) / float(nx);
float v = float(i) / float(ny);
ray r( origin, lower_left_corner + u*horizontal + v*vertical );
vec3 p = r.point_at_parameter( 2, 0 );
vec3 col = color( r, world );
int ir = int( 255.99 * col[0] );
int ig = int( 255.99 * col[1] );
int ib = int( 255.99 * col[2] );
std :: cout << ir << " " << ig << " " << ib << "\n";
}
}
}