RayTracing 学习笔记

参考书目:Ray Tracing in a weekend
参考博文:https://blog.****.net/libing_zeng/article/details/54882373

Chapter 5: Surface normals and multiple objects

RayTracing 学习笔记

考虑法向量:

需要求出Ray与Sphere的交点,如果存在焦点,我们只取最近的一个点。
焦点与球心的Vector既为法向量N

float hit_sphere(Ray r, Vector center, float radius) {
	Vector oc = r.origin - center;
	float a = r.direction.dot(r.direction);
	float b = 2 * oc.dot(r.direction);
	float c = oc.dot(oc) - radius * radius;
	float delta = b * b - 4 * a*c;
	if (delta < 0)
		return -1;
	else
		return (-b - sqrt(delta))*0.5;
}
Vector color( Ray&r) {
	//if hit
	float t = hit_sphere(r, Vector(0, 0, -1), 0.5);
	Vector N = (r.getPoint(t) - Vector(0, 0, -1)).normalize();
	if (t>0)
		return Vector(N.x+1,N.y+1,N.z+1)*0.5;
	
	//Draw background
	Vector unit_direction = r.direction.normalize();
	t = 0.5*(unit_direction.y+1.0);
	return Vector(1, 1, 1)*(1 - t) +  Vector(0.5, 0.7, 1)*t;
}

RayTracing 学习笔记

设置hitable类

由于可能有多个object,设置hitable类方便存取hit信息,如法向量,交点,切平面等

#ifndef HITABLEH
#define HITABLEH
#include "ray.h"

struct hit_record {
	float t;
	Vector p;
	Vector normal;//法向量
};

class hitable
{
public:
	virtual bool hit(const Ray &r, float t_min, float t_max, hit_record& rec)const = 0;
};

#endif // !HITABLEH

hitable_list 存储所有可能碰撞的object

class hitable_list:public hitable
{
public:
	hitable_list(){}
	hitable_list(hitable **list,int n):list(list),list_size(n){}
	hitable **list;
	int list_size;
	virtual bool hit(Ray &r, float t_min, float t_max, hit_record& rec)const;
};

main函数变为:

Vector color( Ray&r,hitable_list *hit_list) {
	//if hit
	//hitable_list hit_list;
	hit_record hit_rec;
	float t;
	if (hit_list->hit(r, 0.0, MAXFLOAT, hit_rec)) {
		//t = hit_rec.t;
		Vector N = hit_rec.normal;
		return Vector(N.x + 1, N.y + 1, N.z + 1)*0.5;
	}
	
	//Draw background
	Vector unit_direction = r.direction.normalize();
	t = 0.5*(unit_direction.y+1.0);
	return Vector(1, 1, 1)*(1 - t) +  Vector(0.5, 0.7, 1)*t;
}


int main() {
	ofstream ofile("c:\\Users\\DXT00\\Desktop\\Graphics\\RayTracing\\ppm.txt");
	//ofile.open


	int nx = 200;
	int ny = 100;
	ofile << "P3\n" << nx << " " << ny << "\n255\n";
	Vector low_left_corner(-2.0, -1.0, -1.0);
	Vector horizontal(4, 0, 0);
	Vector vertical(0, 2, 0);
	Vector origin(0, 0, 0);
	hitable *list[2];
	
	Material *m1 = new Material();
	Material *m2 = new Material();
	list[0] = new Sphere(Vector(0, 0, -1), 0.5, m1);
	list[1] = new Sphere(Vector(0, 0.5, -1), 0.5, m2);
	hitable_list *hit_list=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(j) / float(ny);

			Ray r(origin, low_left_corner +  horizontal*u +  vertical*v);
			Vector col = color(r, hit_list);
			int ir = (int(255.99*col.x));
			int ig = (int(255.99*col.y));
			int ib = (int(255.99*col.z));

			//cout << ir << " " << ig << " " << ib << "\n";
			//if(ofile.is_open())
			ofile << ir << " " << ig << " " << ib << endl;
		}
		
	}
	ofile.close();
}

RayTracing 学习笔记

Chapter 6: Antialiasing

添加Camera类并消除锯齿

对每隔像素点周围随机做100次平均

class Camera
{
public:
	Camera() {
		low_left_corner = Vector(-2.0, -1.0, -1.0);
		horizontal= Vector(4, 0, 0);
		vertical = Vector(0, 2, 0);
		origin = Vector(0, 0, 0);
	}

	Vector low_left_corner;
	Vector horizontal;
	Vector vertical;
	Vector origin;

	Ray getRay(float u, float v) {
		return Ray(origin, low_left_corner + horizontal * u + vertical * v - origin);
	}
};
int main() {
	ofstream ofile("c:\\Users\\DXT00\\Desktop\\Graphics\\RayTracing\\ppm.txt");
	//ofile.open

	srand(time(0));
	int nx = 200;
	int ny = 100;
	int ns = 100;//边界模糊,随机采样100次
	ofile << "P3\n" << nx << " " << ny << "\n255\n";
	
	Camera camera;
	hitable *list[2];
	
	Material *m1 = new Material();
	Material *m2 = new Material();
	list[0] = new Sphere(Vector(0, 0, -1), 0.5, m1);
	list[1] = new Sphere(Vector(0, 0.5, -1), 0.5, m2);
	hitable_list *hit_list=new hitable_list(list, 2);
	
	for (int j = ny-1; j>=0;j--)
	{
		for (int i = 0; i < nx; i++)
		{
			Vector col(0,0,0);
			for (int k = 0; k < ns; k++)
			{
				float u = float(i+ rand() % 100 / (float)101) / float(nx);
				float v = float(j+ rand() % 100 / (float)101) / float(ny);
				Ray r = camera.getRay(u, v);
				 col=col+ color(r, hit_list);
			}
			col = col / ns;
		
			int ir = (int(255.99*col.x));
			int ig = (int(255.99*col.y));
			int ib = (int(255.99*col.z));

		
			ofile << ir << " " << ig << " " << ib << endl;
		}
		
	}
	ofile.close();
}

RayTracing 学习笔记

Chapter 7: Diffuse Materia

漫反射介质

注意:漫射材料的反射光线的方向是随机的,法向量是单位向量

怎么模拟一个随机方向的向量呢?
在交点处单位法向量的基础上加上一个长度小于1的随机向量。

生成长度小于1的随机向量:

Vector random_unit_direction() {
	Vector r;
	do
	{
		r = Vector(rand() % 100 / (float)100, rand() % 100 / (float)100, rand() % 100 / (float)100)*2.0 - Vector(1, 1, 1);
	} while (r.Length() >= 1.0);
	return r;
}

递归求反射颜色:
漫射材料不发光,只吸收和反射环境的光(反射光的方向是随机的),所以将漫射材料的球体的颜色设置为背景颜色乘以某系数是合理的。系数怎么确定呢?光线每被反射一次*1/2(因为光线没被反射一次会被吸收一半)。

Vector color( Ray r,hitable_list *hit_list) {
	//if hit
	//hitable_list hit_list;
	hit_record hit_rec;
	float t;
	if (hit_list->hit(r, 0.0, MAXFLOAT, hit_rec)) {
		t = hit_rec.t;
		Vector OP = hit_rec.p;
		Vector PS = hit_rec.normal;
		Vector OE = random_unit_direction();

		Vector reflect = PS + OE;
		Ray reflect_ray = Ray(OP, reflect);
		/*return Vector(N.x + 1, N.y + 1, N.z + 1)*0.5;*/
		return color(reflect_ray, hit_list)*0.5;
	}
	else {
		//Draw background
		Vector unit_direction = r.direction.normalize();
		float t_ = 0.5*(unit_direction.y + 1.0);
		return Vector(1, 1, 1)*(1 - t_) + Vector(0.5, 0.7, 1)*t_;
	}
	
}

将流程反过来看一遍:

1,起点为原点的光线撞击球C,获得撞击点P和单位法向量PS。

2,产生一个“起点在原点,长度小于1,方向随机”的向量OE。

3,OP+PS+OE-OP=PM,PM即为漫射材料的球体在P点的随机反射方向向量。

4,反射光线是以P为起点,PM为方向向量,所以,我们可以获得反射光线的方程

5,让反射光线去撞击其他球吧(回到第一步)
RayTracing 学习笔记
颜色太黑,进行gamma校正
Note the shadowing under the sphere. This picture is very dark, but our spheres only absorb
half the energy on each bounce, so they are 50% reflectors. If you can’t see the shadow, don’t
worry, we will fix that now. These spheres should look pretty light (in real life, a light grey). The
reason for this is that almost all image viewers assume that the image is “gamma corrected”,
meaning the 0 to 1 values have some transform before being stored as a byte. There are many
good reasons for that, but for our purposes we just need to be aware of it. To a first
approximation, we can use “gamma 2” which means raising the color to the power 1/gamma, or
in our simple case ½, which is just square-root:

for (int j = ny-1; j>=0;j--)
	{
		for (int i = 0; i < nx; i++)
		{
			Vector col(0,0,0);
			for (int k = 0; k < ns; k++)
			{
				float u = float(i+ rand() % 100 / (float)101) / float(nx);
				float v = float(j+ rand() % 100 / (float)101) / float(ny);
				Ray r = camera.getRay(u, v);
				 col=col+ color(r, hit_list);
			}
			col = col / float(ns);

			//gamma校正,让颜色不那么黑
			col = Vector(sqrt(col.x), sqrt(col.y), sqrt(col.z));
	
			int ir = (int(255.99*col.x));
			int ig = (int(255.99*col.y));
			int ib = (int(255.99*col.z));
	
			ofile << ir << " " << ig << " " << ib << endl;
		}
	}

RayTracing 学习笔记

Chapter 8: Metal

添加material类,不同的material反射方式不同。

class Material
{
public:
	Material() {};
	virtual bool scatter(Ray &r_in,hit_record &rec, Vector& attenuation, Ray& scattered)const = 0;

};

class lambertain:public Material
{
public:
	lambertain(const Vector&a) :albedo(a) {};
	virtual bool scatter(Ray &r_in, hit_record &rec, Vector& attenuation, Ray& scattered)const;

	Vector albedo;
};
class mental:public Material
{
public:
	mental(const Vector&a) :albedo(a){}
	virtual bool scatter(Ray &r_in, hit_record& rec, Vector& attenuation, Ray& scattered)const;
	Vector albedo;
};

前面我们所做的是漫反射,现在看看镜面反射:
RayTracing 学习笔记
反射向量: F=V+2*B,计算过程如下:
RayTracing 学习笔记
material.cpp


    bool lambertain::scatter(Ray &r_in, hit_record &rec, Vector& attenuation, Ray& scattered)const {
	Vector target = rec.p + rec.normal + random_unit_direction();
	scattered = Ray(rec.p, target - rec.p);
	attenuation = albedo;
	return true;
}

 bool mental::scatter(Ray &r_in, hit_record& rec, Vector& attenuation, Ray& scattered)const {
	Vector unit_r_direction = r_in.direction.normalize();
	Vector target = unit_r_direction - rec.normal*2.0*(unit_r_direction.dot(rec.normal));
	scattered = Ray(rec.p, target);
	attenuation = albedo;
	return(scattered.direction.dot(rec.normal) > 0);//保证反射vector和法向量间的角度<90度
}


main color函数改为:

Vector color( Ray r,hitable_list *hit_list,int time) {
	//if hit
	//hitable_list hit_list;
	hit_record hit_rec;
	float t;
	if (hit_list->hit(r, 0.00001, MAXFLOAT, hit_rec)) {
		t = hit_rec.t;
		Material *mater = hit_rec.material;

		Ray reflect_ray;// = Ray(OP, reflect);
		Vector attenuation;
		if(time<50&&mater->scatter(r,hit_rec,attenuation,reflect_ray))
			return color(reflect_ray, hit_list,time+1)*attenuation;
		else {
			return Vector(0, 0, 0);
		}
	}
	else {
		//Draw background
		Vector unit_direction = r.direction.normalize();
		float t_ = 0.5*(unit_direction.y + 1.0);
		return Vector(1, 1, 1)*(1 - t_) + Vector(0.5, 0.7, 1)*t_;
	}
}

int main() {
	ofstream ofile("c:\\Users\\DXT00\\Desktop\\Graphics\\RayTracing\\ppm.txt");
	//ofile.open

	srand(time(0));
	int nx = 400;
	int ny = 200;
	int ns = 100;//边界模糊,随机采样100次
	ofile << "P3\n" << nx << " " << ny << "\n255\n";
	
	Camera camera;
	hitable *list[4];
	
	Material *m1 = new mental(Vector(0.5, 0.5, 0.5));
	Material *m2 = new lambertain(Vector(0.5,0.1,0.1));
	Material *m3 = new lambertain(Vector(0.1, 0.1, 0.2));
	Material *m4 = new mental(Vector(0.4, 0.4, 0.2));

	list[0] = new Sphere(Vector(0, 0, -1), 0.5, m1);
	list[1] = new Sphere(Vector(1, 0, -1), 0.5, m3);
	list[2] = new Sphere(Vector(0, -100.5, -1), 100, m2);
	list[3] = new Sphere(Vector(-0.7, 0, -1), 0.2, m4);
	hitable_list *hit_list=new hitable_list(list, 4);
	
	for (int j = ny-1; j>=0;j--)
	{
		for (int i = 0; i < nx; i++)
		{
			Vector col(0,0,0);
			for (int k = 0; k < ns; k++)
			{
				float u = float(i+ rand() % 100 / (float)101) / float(nx);
				float v = float(j+ rand() % 100 / (float)101) / float(ny);
				Ray r = camera.getRay(u, v);
				 col=col+ color(r, hit_list,0);
			}
			col = col / float(ns);

			//gamma校正,让颜色不那么黑
			col = Vector(sqrt(col.x), sqrt(col.y), sqrt(col.z));
		
			
			int ir = (int(255.99*col.x));
			int ig = (int(255.99*col.y));
			int ib = (int(255.99*col.z));

		
			ofile << ir << " " << ig << " " << ib << endl;
		}
		
	}
	ofile.close();
}

RayTracing 学习笔记

Chapter 9: Dielectrics

电介质
Clear materials such as water, glass, and diamonds are dielectrics. When a light ray hits them, it
splits into a reflected ray and a refracted (transmitted) ray. We’ll handle that by randomly
choosing between reflection or refraction and only generating one scattered ray per interaction.

The refraction is described by Snell’s law:
n sin(theta) = n’ sin(theta’)
Where n and n’ are the refractive indices (typically air = 1, glass = 1.3-1.7, diamond = 2.4) and
the geometry is:
RayTracing 学习笔记
计算折射向量:
RayTracing 学习笔记
Ray从电介质到空气 与 从空气到介质有两个变量不同:
1.n1/n2
2.N’方向
RayTracing 学习笔记

class dielectrics:public Material
{
public:
	dielectrics(float ref):ref_idx(ref){}
	virtual bool scatter(Ray &r_in, hit_record& rec, Vector& attenuation, Ray& scattered)const;
	virtual bool refract(Ray & r_in,  Vector & rec_normal_, float nin_to_nout, Vector & refracted) const;
	float ref_idx;
};

bool dielectrics::refract(Ray & r_in, Vector & rec_normal_,float nin_to_nout, Vector & refracted) const
 {
	 Vector I = r_in.direction.normalize();
	 float cos_a1 = -(I.dot(rec_normal_)) ;

	 float discriminant = 1.0 - (1 - cos_a1 * cos_a1)*nin_to_nout*nin_to_nout;
	 if (discriminant > 0) {
		  refracted = I * nin_to_nout + rec_normal_ *(nin_to_nout*cos_a1 - sqrt(discriminant));
		 //=(I+rec_normal_*cos_a1)*nin_to_nout  -rec_normal_*sqrt(discriminant)
		 return true;
	 }
	 else
		 return false;

 }

 bool dielectrics::scatter(Ray & r_in, hit_record & rec, Vector & attenuation, Ray & scattered) const
 {
	 attenuation = Vector(1.0, 1.0, 1.0);
	 Vector reflected = reflect(r_in.direction, rec.normal);
	 float nin_to_nout;// = (rec.normal.dot(r_in.direction) <= 0) ? 1.0 / ref_idx : ref_idx;
	 Vector normal_; //=  <= 0) ? rec.normal : -rec.normal;
	 
	 if (rec.normal.dot(r_in.direction) > 0){//从电介质到空气
		 nin_to_nout=ref_idx;
		 normal_ = -rec.normal;
	 }
	 else {//从空气到电介质
		 nin_to_nout = 1.0/ref_idx;
		 normal_ = rec.normal;
	 }
	 
	 Vector refracted;
	 if (refract(r_in, normal_, nin_to_nout, refracted)) {
		 scattered = Ray(rec.p, refracted);
	 }
	 else {
		 scattered = Ray(rec.p, reflected);
		 return false;
	 }
	 
	return true;
 }

RayTracing 学习笔记
Chapter 10: Positionable camera
设置Camera类:
vfov–广角角度
aspect–画布宽长比

class Camera
{
public:
	Camera(float vfov,float aspect) {//aspect = nx/ny
		canvas_z = -1.0;
		float theta = vfov * M_PI / 180; 
		float half_height = abs(canvas_z)*tan(theta / 2.0);
		float half_width = aspect * half_height;
		low_left_corner = Vector(-half_width, -half_height, canvas_z);
		horizontal= Vector(2*half_width, 0, 0);
		vertical = Vector(0, 2*half_height, 0);
		origin = Vector(0.0, 0.0, 0.0);
	}

	Vector low_left_corner;
	Vector horizontal;
	Vector vertical;
	Vector origin;
	float canvas_z;

	Ray getRay(float u, float v) {
		return Ray(origin, low_left_corner + horizontal * u + vertical * v - origin);
	}
};

vfov=60
RayTracing 学习笔记
vfov=120
RayTracing 学习笔记
未完待续。。。