【图形学】Ray Tracing in a Weekend [2]
1.光线追踪
实现类:vec3,sphere(球体),ray(射线,由射线起始点+射线方向向量组成)
camera.h hitable.h hitable_list.h
ray.cpp + ray.h
sphere.h
vec3.h + vec3.cpp
图显示工具:ffplay.exe
sphere.h
#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; } 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
着色消除锯齿
增加光照反射
vec3 color(const ray& r, hitable* world) { hit_record rec; if (world->hit(r, 0.001, FLT_MAX, rec)) { vec3 rnd = random_in_unit_shpere(); vec3 target = rec.p + rec.normal + rnd; // printf("%f %f %f\n", rnd[0], rnd[1], rnd[2]); return 0.5 * color(ray(rec.p, target - rec.p), world); } 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); } }
增加纹理
#ifdef MATERIAL_H #define MATERIAL_H #include "ray.h" #include "hitable.h" vec3 reflect(const vec3& v, const vec3& n) { return v - 2 * dot(v, n) * n; } class material { public: virtual bool scatter(const ray& r_in, const hit_record& rec, vec3& attenuation, ray& scattered) const = 0; }; 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 target = rec.p + rec.normal + random_in_unit_sphere(); scattered = ray(rec.p, target - rec.p); attenuation = albedo; return true; } vec3 albedo; }; class metal: public material { public: metal(const vec3& a): albedo(a) {} virtual bool scatter(const ray& r_in, const hit_record& rec, vec3& attenuation, ray& scattered)const { vec3 reflected = reflect(unit_vector(r_in.direction()), rec.normal); scattered = ray(rec.p, reflected); attenuation = albedo; return dot(scattered.direction(), rec.normal) > 0; } vec3 albedo; // float fuzz; }; #endif
增加球体个数
int main() { int nx = 200; int ny = 100; int ns = 100; 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[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; for (int j = ny - 1; j >= 0; j--) { for (int i = 0; i < nx; i++) { vec3 col(0, 0, 0); for (int s = 0; s < ns; s++) { float u = float(i + drand48()) / float(nx); float v = float(j + drand48()) / float(ny); ray r = cam.get_ray(u, v); vec3 p = r.point_at_parameter(2.0); col += color(r, world, 0); } col /= float(ns); col = vec3(sqrt(col[0]), sqrt(col[1]), sqrt(col[2])); // cout << u << " " << v << endl; // 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]); cout << ir << " " << ig << " " << ib << endl; } } return 0; }
带金属光泽的纹理
class metal: public material { public: metal(const vec3& a, float f): albedo(a) {if (f < 1) fuzz = f; else fuzz = 1;} virtual bool scatter(const ray& r_in, const hit_record& rec, vec3& attenuation, ray& scattered)const { vec3 reflected = reflect(unit_vector(r_in.direction()), rec.normal); scattered = ray(rec.p, reflected + fuzz * random_in_unit_sphere()); attenuation = albedo; return dot(scattered.direction(), rec.normal) > 0; } vec3 albedo; float fuzz; };
9. 加入Dielectrics 折射
10. 视角切换
12. 随机场景scene的渲染