《Ray Tracing in One Weekend》笔记 - 【Chapter 8】镜面材质

前言

本节中尝试将材质抽象为一个抽象类,而后在次基础上新增镜面反射材质,继而在场景中再添加两个不同材质的小球。

正文

1. 定义一个 material抽象类
// ------------ material.h ------------ //

#ifndef MATERIAL_H
#define MATERIAL_H

#include "hitable.h"

class material
{
public:
	// 定义材质抽象方法:作用: 1.确定反射光线的方向 2.确定反射系数
	virtual bool scatter(const ray& r_in, const hit_record& rec, vec3& attenuation, ray& scattered) const = 0;
};
#endif // !MATERIAL_H

并对hitable.h 做相应修改,在hit_record结构体中记录材质的类型。这里涉及两个头文件相互引用的问题。(所以material 在这里采用了指针类型?)
在这里插入图片描述
// ------------ material.cpp ------------ //

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

class material;
// 使用结构体记录交点信息  与光源的距离 t,坐标向量p, 交点表mian法向量
struct  hit_record {
	float t;
	vec3 p;
	vec3 normal;
	material* mat_ptr;
};


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

2. 定义一个镜面反射材质类(metal)与一个漫射材质类(lambertian),继承于材质类

(1)漫反射的类参考前面在main函数中的实现
// -------- lambertian.h -------- //

#ifndef _LAMBERTIAN_H_
#define _LAMBERTIAN_H_

#include "material.h"

class lambertian :public material {
public:
	lambertian(const vec3& a) : albedo(a) {}
	virtual bool scatter(const ray& r_in, const hit_record& rec, vec3& attenuation, ray& scattered) const;
	vec3 albedo;    // 衰减系数
};

#endif // !_LAMBERTIAN_H_


// -------- lambertian.cpp -------- //

#include "lambertian.h"
float random01() {
	return (rand() % 101) / 100.0;
}
vec3 random_in_unit_sphere() {
	vec3 p;
	do {
		p = 2.0 * vec3(random01(), random01(), random01()) - vec3(1, 1, 1);
	} while (p.squared_length() >= 1.0);
	return p;
}

bool lambertian::scatter(const ray& r_in, const hit_record& rec, vec3& attenuation, ray& scattered)const {
	vec3 target = rec.p + rec.normal + random_in_unit_sphere();
	scattered = ray(rec.p, target-rec.p);
	attenuation = albedo;
	return true;
}

(2)镜面反射材质类中
结构大体与漫反射类相似,不同之处在于光线在镜面反射材质表面,反射方向由入射方向与发现方向唯一确定。
// ------------ metal.h ------------ //

#ifndef METAL_H
#define METAL_H

#include "material.h"

class metal {
public:
	metal(const vec3& a) :albedo(a) {}
	virtual bool scatter(const ray& r_in, const hit_record& rec, vec3& attenuation, ray& scattered)const;
	vec3 albedo;
};
#endif

知入射光线与法线方向的范围向量,求反射方向

在这里插入图片描述
// ------------ metal.cpp ------------ //

#include "metal.h"

vec3 reflect(const vec3& v, const vec3& n) {
	return v - 2 * dot(v, n) * n;
}

bool metal::scatter(const ray& r_in, const hit_record& rec, vec3& attenuation, ray& scattered)const{
	vec3 reflected = reflect(unit_vector(r_in.getDirection()), rec.normal);
	scattered = ray(rec.p, reflected);
	attenuation = albedo;
	return (dot(scattered.getDirection(), rec.normal) > 0);
}

小球类也需要做相应修改,加入被抽象出的材质属性
// ------------ sphere.h ------------ //

#ifndef SPHERE
#define SPHERE

#include "hitable.h"
#include "material.h"
class sphere :public hitable {
public:
	sphere() {}
	sphere(vec3 cen, float r, material *m) : center(cen), radius(r), ma(m) {};
	virtual bool hit(const ray& r, float t_min, float t_max, hit_record& rec) const;
	vec3 center;
	float radius;
	material *ma;
};
#endif // !SPHERE

// ------------ sphere.cpp ------------ //

#include "sphere.h"
bool sphere::hit(const ray& r, float t_min, float t_max, hit_record& rec) const {
	vec3 oc = r.getOrigin() - center;
	float a = dot(r.getDirection(), r.getDirection());
	float b = 2 * dot(oc, r.getDirection());
	float c = dot(oc, oc) - radius * radius;
	float discriminant = b * b - 4 * a * c;
	if (discriminant > 0) {
		// -------- 光线与小球有两个交点 -------- //
		float t = (-b - sqrt(discriminant)) / (2.0 * a);
		// 1. 先判断小 t, 若满足条件,则交点信息存储到rec中 
		if (t < t_max && t > t_min) {
			rec.t = t;
			rec.p = r.getPointAtParameter(t);
			rec.normal = (rec.p - center) / radius;
			rec.mat_ptr = ma;
			return true;
		}
		// 2. 再判断大 t
		t = (-b + sqrt(discriminant)) / (2.0 * a);
		if (t < t_max && t > t_min) {
			rec.t = t;
			rec.p = r.getPointAtParameter(t);
			rec.normal = (rec.p - center) / radius;
			rec.mat_ptr = ma;
			return true;
		}
	}
	return false;
}

main.cpp中修改color 函数,并在main函数中添加新的小球
// ------------ main.cpp ------------- //

#include <iostream>
#include <fstream>
#include "vec3.h"
#include "ray.h"
#include "hitable_list.h"
#include "sphere.h"
#include <float.h>
#include "camera.h"
#include "lambertian.h"
#include "metal.h"

float random01();
vec3 random_in_unit_sphere();
// -------- color 函数用于返回背景的颜色 -------- //
vec3 color(const ray& r, hitable *world, int depth) {

	hit_record rec;
	// 判断光线是否与圆小交
	if (world -> hit(r, 0.001, FLT_MAX, rec)) {
		// 渲染漫反射效果
		ray scattered;
		vec3 attenuation;
		if (depth < 50 && rec.mat_ptr->scatter(r, rec, attenuation, scattered)) {
			return attenuation * color(scattered, world, depth + 1);
		}
		else {
			return vec3(0,0,0);
		}
	}
	else {
		vec3 unit_direction = unit_vector(r.getDirection());
		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);//white, light blue
	}
}

void main() {
	int nx = 200;
	int ny = 100;
	int ns = 100; // 每个像素点的随机采样次数
    std::ofstream outfile("PPMTest.txt", std::ios_base::out);
	outfile << "P3\n" << nx << " " << ny << "\n255\n";
	//std::cout << "P3\n" << nx << " " << ny << "\n255\n";

	// 添加几个新的材质球



	hitable *list[4];
	list[0] = new sphere(vec3(0,0,-1), 0.5, new lambertian(vec3(0.8, 0.3, 0.3)));
	list[1] = new sphere(vec3(0, -100.5, -1), 100, new lambertian(vec3(0.8, 0.8, 0.0))); // 在半部分放一个大球作为地面
	list[2] = new sphere(vec3(1, 0, -1), 0.5, new metal(vec3(0.8, 0.6, 0.2)));
    list[3] = new sphere(vec3(-1, 0, -1), 0.5, new metal(vec3(0.8, 0.8, 0.8)));


	hitable *world = new hitable_list(list, 4);
	camera cam;

	bool randSample = true;
	for (int j = ny - 1; j >= 0; --j) {
		for (int i = 0; i < nx; ++i) {
			vec3 col = vec3(0,0,0);
			if (randSample) {
				// 随机采样
				for (int s = 0; s < ns; ++s) {
					// 对每一个像素点随机采样100次
					float u = float(i + (rand() % 101) / 100.0) / float(nx);
					float v = float(j + (rand() % 101) / 100.0) / float(ny);

					ray r = cam.get_ray(u, v);
					vec3 p = r.getPointAtParameter(2.0);
					col += color(r, world, 0);
				}
				col /= float(ns);
			}
			col = vec3(sqrt(col[0]), sqrt(col[1]), sqrt(col[2]));
			int ir = int(255.99 * col[0]);
			int ig = int(255.99 * col[1]);
			int ib = int(255.99 * col[2]);
			
			outfile << ir << " " << ig << " " << ib << "\n";
			std::cout << ir << " " << ig << " " << ib << "\n";
		}
	}
}

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值