《Ray Tracing in One Weekend》笔记 - 【Chapter 5】:Surface normals and multiple objects

Chapter 5:在小球表面进行利用法相量的值着色;在场景总中构建多个球

前言

本节大致可分为两个模块内容:

  1. 将球体表面的法向量,以着色形式显示到小球表面。
  2. 定义了球体类,方便在场景中创建多个小球。另外定义了抽象类hitable,帮助检测场景中物体与光线碰撞情况。

另外,本节将中将“能够与光线相交”这一特性抽象为一个类,辅助后续光线与物体的求交判断。
C++ 虚函数,纯虚函数,抽象类相关概念

正文

1.将法向量值映射到小球表面上
注: 代码中将hit_sphere 函数的返回值修改为float型
// ------------ main.cpp ------------ //

float hit_sphere(const vec3& center, float radius, const ray& r) {
	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) {
		return -1.0;
	}
	else {
		return (- b - sqrt(discriminant)) / (2.0 * a);
	}
	

}

// -------- color 函数用于返回背景的颜色 -------- //
vec3 color(const ray& r) {
	// 如果光线穿过小球,将其设为红色.小球位于(0,0,-1),半径为0.5
	float t = hit_sphere(vec3(0, 0, -1), 0.5, r);
	
	if (t > 0.0) {
		// 求出交点处的法向量
		vec3 N = unit_vector(r.getPointAtParameter(t) - vec3(0,0,-1));
		return 0.5 * vec3(N.x() + 1, N.y() + 1, N.z() + 1);
	}
	
	vec3 unit_direction = unit_vector(r.getDirection());
    // 标准化之后的y值值域为[-1, 1], 将其映射到[0, 1]
	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);
}

在这里插入图片描述

2.新增球体类
首先将于光线发生碰撞这一特征抽象为一个抽象类,由场景中的物体来继承。
// -------- hitable.h,用于检测光线是否与物体碰撞 -------- //

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

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


class hitable {
public:
// 抽象方法的输入参数为 光线对象,光线与物体两个交点对应的距离 t_min, t_max, 交点处的相关信息。
	virtual bool hit(const ray &r, float t_min, float t_max, hit_record &rec) const = 0;
};
#endif

// -------- sphere.h -------- //

#ifndef SPHERE
#define SPHERE
#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 t_min, float t_max, hit_record& rec) const;
	vec3 center;
	float radius;
};
#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;
			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;
			return true;
		}
	}
	return false;
}

// -------- hitable_list.h -------- //
// -------- 碰撞物体列表类, 用于记录场景中所有物体与光线的相交情况 -------- //

#ifndef HITABLELISTH
#define HITABLELISTH
#include "hitable.h"
class hitable_list :public hitable {
public:
	hitable_list() { list = nullptr; list_size = 0; }
	hitable_list(hitable** l, int n) { list = l; list_size = n; }
	virtual bool hit(const ray& r, float t_min, float t_max, hit_record& rec) const;
	hitable** list;
	int list_size;
}; // hitable_list 是所有能被光线撞击到的物体列表

#endif // ! HITABLELISTH

// -------- hitable_list.cpp -------- //

#include "hitable_list.h"

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,rec = temp_rec
			hit_anything = true;
			closest_so_far = temp_rec.t;
			rec = temp_rec;
		}
	}
	return hit_anything;
}

// -------- main.cpp -------- //

#include <iostream>
#include <fstream>
#include "vec3.h"
#include "ray.h"
#include "hitable_list.h"
#include "sphere.h"
#include <float.h>
// -------- color 函数用于返回背景的颜色 -------- //
vec3 color(const ray& r, hitable *world) {
    hit_record rec;
	if (world->hit(r, 0.0, FLT_MAX, rec)) {
		return 0.5 * vec3(rec.normal.x() + 1, rec.normal.y() + 1, rec.normal.z() + 1);
	}
	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);
	}
}

void main() {
	int nx = 200;
	int ny = 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";

	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, -1), 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(j) / float(ny);

			ray r(origin, lower_left_corner + u * horizontal + v * vertical);
			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]);
			
			outfile << ir << " " << ig << " " << ib << "\n";
			std::cout << ir << " " << ig << " " << ib << "\n";
		}
	}
}

运行结果
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值