RayTracing 学习笔记

参考书目:Ray Tracing in a weekend
参考博文:https://blog.csdn.net/libing_zeng/article/details/54882373
完整代码:https://github.com/DXT00/RayTracing-in-a-Weekend/tree/master/RayTracing

Chapter 5: Surface normals and multiple objects

在这里插入图片描述

考虑法向量:

需要求出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;
}

在这里插入图片描述

设置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();
}

在这里插入图片描述

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();
}

在这里插入图片描述

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,让反射光线去撞击其他球吧(回到第一步)
在这里插入图片描述
颜色太黑,进行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;
		}
	}

在这里插入图片描述

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;
};

前面我们所做的是漫反射,现在看看镜面反射:
在这里插入图片描述
反射向量: F=V+2*B,计算过程如下:
在这里插入图片描述
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();
}

在这里插入图片描述

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:
在这里插入图片描述
计算折射向量:
在这里插入图片描述
Ray从电介质到空气 与 从空气到介质有两个变量不同:
1.n1/n2
2.N’方向
在这里插入图片描述

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;
 }

在这里插入图片描述
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
在这里插入图片描述
vfov=120
在这里插入图片描述
Chapter 11: Defocus Blur

在这里插入图片描述
因此长和宽都要乘以 dist_to_focus
camara.h变为:

Camera(Vector lookfrom, Vector lookat, Vector vup, float vfov, float aspect,float aperture_,float dist_to_focus) { // vfov is top to bottom in degrees
		float theta = vfov * M_PI / 180;
		float half_height = tan(theta / 2)*dist_to_focus;
		float half_width = aspect * half_height;
		origin = lookfrom;
		aperture = aperture_;
		w = (lookfrom - lookat).normalize();
		u = vup.cross( w).normalize();
		v = w.cross( u);
		lower_left_corner = origin -  u* half_width  -  v*half_height  - w*dist_to_focus;
		horizontal = u*2 * half_width;
		vertical = v *2 * half_height;
	}
	Ray getRay(float s, float t) { 
		Vector rd =  random_unit_direction()*aperture;
		Vector offset = u*rd.x +v *rd.y;
		return Ray(origin+offset, lower_left_corner +  horizontal*s + vertical*t - (origin +offset));
	
	}
	Vector u, v, w;
	Vector lower_left_corner;
	Vector horizontal;
	Vector vertical;
	Vector origin;
	float aperture;
};

aperture=0.5
在这里插入图片描述
aperture=1
在这里插入图片描述
aperture=2
在这里插入图片描述
未完待续。。。

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值