笑死,光线追踪终于有光了。
实际上是对一些常规的物体,让它发射出光线。
Emissive Materials(发光材质)
首先,我们来制作一种发光材料。我们需要添加一个发射函数(我们也可以将这个函数添加到hit_record中——这取决于设计偏好)。就像背景一样,它只告诉光线它是什么颜色,并且不进行反射。
class diffuse_light : public material{
public:
diffuse_light(shared_ptr<texture> tex) : tex(tex){}
diffuse_light(const color& emit) : tex(make_shared<solid_color>(emit)) {}
color emitted(double u,double v,const Point3& p){
return tex->value(u,v,p);
}
private:
shared_ptr<texture> tex;
};
对于其它的材质,不具备发射光的能力,自然当光线打到这个物体上的时候,他们不应该有颜色的发聩(逆光路)。
class material{
public:
virtual ~material() = default;
virtual color emitted(double u,double v,const Point3& p) const{
return color(0,0,0);
}
virtual bool scatter(
const Ray& r_in,const hit_record& rec,color& attenuation,Ray& scattered
) const {
return false;
}
};
同时之前我们定义,当没有打到物体的时候,返回了一个颜色,理论上这里就像是背景在发光的意思,所以我们需要让背景为黑色。
color ray_color(const Ray& r,int depth,const hittable& world) const{
if(depth <= 0 ){
return color(0,0,0);
}
hit_record rec;
if(world.hit(r,interval(0.001,infinity),rec)){
color attenuation;
Ray scattered;
if(rec.mat->scatter(r,rec,attenuation,scattered)){
return attenuation*(ray_color(scattered,depth-1,world));
}
return color(0,0,0);
}
double a=0.5*(1.0+r.direction().y());
return (1.0-a)*color(1.0,1.0,1.0)+a*color(0.5,0.7,1.0);
}
class material{
public:
virtual ~material() = default;
virtual color emitted(double u,double v,const Point3& p) const{
return color(0,0,0);
}
virtual bool scatter(
const Ray& r_in,const hit_record& rec,color& attenuation,Ray& scattered
) const {
return false;
}
};
现在我们需要让背景为清一色的黑色
class camera{
public:
double aspect_ratio=16.0/9.0;
int image_width=400;
int samples_per_pixel=10;
int max_depth=10;
double vfov = 90;
Point3 lookfrom = Point3(0,0,0);
Point3 lookat = Point3(0,0,-1);
vec3 vup = vec3(0,1,0);
double defocus_angle = 0;
double focus_dist = 10;
color background;
hit_record rec;
if(world.hit(r,interval(0.001,infinity),rec)){ return background; }
同时我们的光线还需要判断,如果击中的是光源,那么只需要返回其emmit的颜色,如果击中的不是光源,那么就需要一路的往下的传播,同时还需要判断一个东西,就是有可能某些光源它不仅能发光还能继续发散光线出去。
color ray_color(const Ray& r,int depth,const hittable& world) const{
if(depth <= 0 ){
return color(0,0,0);
}
hit_record rec;
if(!world.hit(r,interval(0.001,infinity),rec)){ return background; }
color attenuation;
Ray scattered;
color color_from_emission = rec.mat->emitted(rec.u,rec.v,rec.p);
if(!rec.mat->scatter(r,rec,attenuation,scattered)){
return color_from_emission;
}
color color_from_scatter = attenuation * ray_color(scattered,depth-1,world);
return color_from_emission + color_from_scatter;
// if(world.hit(r,interval(0.001,infinity),rec)){
// color attenuation;
// Ray scattered;
// if(rec.mat->scatter(r,rec,attenuation,scattered)){
// return attenuation*(ray_color(scattered,depth-1,world));
// }
// return color(0,0,0);
// }
// double a=0.5*(1.0+r.direction().y());
// return (1.0-a)*color(1.0,1.0,1.0)+a*color(0.5,0.7,1.0);
}
#include "util/rtweekend.h"
#include "util/sphere.h"
#include "util/camera.h"
#include "util/hittable.h"
#include "util/hittable_list.h"
#include "util/bvh.h"
#include "util/texture.h"
#include "util/quad.h"
void bouncing_spheres(){
hittable_list world;
shared_ptr<check_texture> check = make_shared<check_texture>(0.32,color(.2,.3,.1),color(0.9,0.9,0.9));
auto material_ground = make_shared<lambertian>(check);
world.add(make_shared<sphere>(Point3(0,-1000,0),1000,material_ground));
for(int a =-11;a<11;a++){
for(int b=-11;b<11;b++){
double choose_mat = random_double();
Point3 center(a+0.9*random_double(),0.2,b+0.9*random_double());
if((center - Point3(4,0.2,0)).length() > 0.9){
shared_ptr<material> sphere_material;
if(choose_mat < 0.8){
//diffuse
color albedo = color::random() * color::random();
sphere_material = make_shared<lambertian>(albedo);
Point3 center2 = center+vec3(0,random_double(0,0.5),0);
world.add(make_shared<sphere>(center,center2,0.2,sphere_material));
}
else if(choose_mat < 0.95){
//metal
color albedo = color::random(0.5,1);
double fuzz = random_double(0,0.5);
sphere_material =make_shared<metal>(albedo,fuzz);
world.add(make_shared<sphere>(center,0.2,sphere_material));
}
else{
//glass
sphere_material = make_shared<dielectric>(1.5);
world.add(make_shared<sphere>(center,0.2,sphere_material));
}
}
}
}
auto material1 = make_shared<dielectric>(1.50);
world.add(make_shared<sphere>(Point3(0,1,0),1.0,material1));
auto material2 = make_shared<lambertian>(color(0.4,0.2,0.1));
world.add(make_shared<sphere>(Point3(-4,1,0),1.0,material2));
auto material3 = make_shared<metal>(color(0.7,0.6,0.5),0.0);
world.add(make_shared<sphere>(Point3(4,1,0),1.0,material3));
// double R = std::cos(pi/4);
// auto material_left = make_shared<lambertian>(color(0,0,1));
// auto material_right=make_shared<lambertian>(color(1,0,0));
// world.add(make_shared<sphere>(Point3(-R , 0 , -1), R, material_left));
// world.add(make_shared<sphere>(Point3(R,0,-1),R,material_right));
bvh_node bvh_root(world.objects,0,world.objects.size()-1);
camera cam;
cam.background = color(0.70, 0.80, 1.00);
cam.aspect_ratio=16.0/9.0;
cam.image_width = 1200;
cam.samples_per_pixel=100;
cam.max_depth=50;
cam.vfov = 20;
cam.lookfrom = Point3(13,2,3);
cam.lookat = Point3(0,0,0);
cam.vup = vec3(0,1,0);
cam.defocus_angle = 0.6;
cam.focus_dist=10.0;
cam.render(bvh_root);
}
void checkered_sphere(){
hittable_list world;
auto checker = make_shared<check_texture>(0.32,color(.2,.3,.1),color(.9,.9,.9));
world.add(make_shared<sphere>(Point3(0,-10,0),10,make_shared<lambertian>(checker)));
world.add(make_shared<sphere>(Point3(0,10,0),10,make_shared<lambertian>(checker)));
camera cam;
cam.background = color(0.70, 0.80, 1.00);
cam.aspect_ratio = 16.0 / 9.0;
cam.image_width = 400;
cam.samples_per_pixel = 100;
cam.max_depth = 50;
cam.vfov = 20;
cam.lookfrom = Point3(13,2,3);
cam.lookat = Point3(0,0,0);
cam.vup = vec3(0,1,0);
cam.defocus_angle = 0;
cam.render(world);
}
void earth(){
hittable_list world;
shared_ptr<image_texture> earth_texture = make_shared<image_texture>("../images/earthmap.jpg");
shared_ptr<lambertian> earth_surface = make_shared<lambertian>(earth_texture);
shared_ptr<sphere> globe = make_shared<sphere>(Point3(0,0,0),2,earth_surface);
world.add(globe);
camera cam;
cam.background = color(0.70, 0.80, 1.00);
cam.aspect_ratio = 16.0 / 9.0;
cam.image_width = 400;
cam.samples_per_pixel = 100;
cam.max_depth = 50;
cam.vfov = 20;
cam.lookfrom = Point3(0,0,12);
cam.lookat = Point3(0,0,0);
cam.vup = vec3(0,1,0);
cam.defocus_angle = 0;
cam.render(world);
}
void perlin_spheres(){
hittable_list world;
shared_ptr<noise_texture> pertex = make_shared<noise_texture>(4);
world.add(make_shared<sphere>(Point3(0,-1000,0),1000,make_shared<lambertian>(pertex)));
world.add(make_shared<sphere>(Point3(0,2,0),2,make_shared<lambertian>(pertex)));
camera cam;
cam.background = color(0.70, 0.80, 1.00);
cam.aspect_ratio = 16.0 / 9.0;
cam.image_width = 400;
cam.samples_per_pixel = 100;
cam.max_depth = 50;
cam.vfov = 20;
cam.lookfrom = Point3(13,2,3);
cam.lookat = Point3(0,0,0);
cam.vup = vec3(0,1,0);
cam.defocus_angle = 0;
cam.render(world);
}
void quads(){
hittable_list world;
// Materials
auto left_red = make_shared<lambertian>(color(1.0, 0.2, 0.2));
auto back_green = make_shared<lambertian>(color(0.2, 1.0, 0.2));
auto right_blue = make_shared<lambertian>(color(0.2, 0.2, 1.0));
auto upper_orange = make_shared<lambertian>(color(1.0, 0.5, 0.0));
auto lower_teal = make_shared<lambertian>(color(0.2, 0.8, 0.8));
// Quads
world.add(make_shared<quad>(Point3(-3,-2, 5), vec3(0, 0,-4), vec3(0, 4, 0), left_red));
world.add(make_shared<quad>(Point3(-2,-2, 0), vec3(4, 0, 0), vec3(0, 4, 0), back_green));
world.add(make_shared<quad>(Point3( 3,-2, 1), vec3(0, 0, 4), vec3(0, 4, 0), right_blue));
world.add(make_shared<quad>(Point3(-2, 3, 1), vec3(4, 0, 0), vec3(0, 0, 4), upper_orange));
world.add(make_shared<quad>(Point3(-2,-3, 5), vec3(4, 0, 0), vec3(0, 0,-4), lower_teal));
camera cam;
cam.background = color(0.70, 0.80, 1.00);
cam.aspect_ratio = 1.0;
cam.image_width = 400;
cam.samples_per_pixel = 100;
cam.max_depth = 50;
cam.vfov = 80;
cam.lookfrom = Point3(0,0,9);
cam.lookat = Point3(0,0,0);
cam.vup = vec3(0,1,0);
cam.defocus_angle = 0;
cam.render(world);
}
int main(){
perlin_spheres();
return 0;
}
Turning Objects into Lights(光照物体)
void simple_light(){
hittable_list world;
auto pertex = make_shared<noise_texture>(4);
world.add(make_shared<sphere>(Point3(0,-1000,0),1000,make_shared<lambertian>(pertex)));
world.add(make_shared<sphere>(Point3(0,2,0),2,make_shared<lambertian>(pertex)));
auto difflight = make_shared<diffuse_light>(color(4,4,4));
world.add(make_shared<quad>(Point3(3,1,-2),vec3(2,0,0),vec3(0,2,0),difflight));
camera cam;
cam.aspect_ratio = 16.0 / 9.0;
cam.image_width = 400;
cam.samples_per_pixel = 100;
cam.max_depth = 50;
cam.background = color(0,0,0);
cam.vfov = 20;
cam.lookfrom = Point3(26,3,6);
cam.lookat = Point3(0,2,0);
cam.vup = vec3(0,1,0);
cam.defocus_angle = 0;
cam.render(world);
}
int main(){
simple_light();
return 0;
}
光线比(1,1,1)更亮(4,4,4)。这使得它足够亮,可以照亮物体。也可以尝试将一些球体变成光源。
auto difflight = make_shared<diffuse_light>(color(4,4,4));
world.add(make_shared<quad>(Point3(3,1,-2),vec3(2,0,0),vec3(0,2,0),difflight));
Creating an Empty “Cornell Box”("创建一个空的“康奈尔盒子”)
图形学的一个非常经典的盒子空间。这里的“康奈尔盒子”(Cornell Box)是一个在计算机图形学中广泛使用的测试模型,用于研究光线在不同材质表面的反射和折射效果。它通常由几个不同颜色的平面组成,并且包含一个光源,用来模拟光线在封闭空间中的传播和交互。
void cornell_box(){
hittable_list world;
auto red = make_shared<lambertian>(color(.65, .05, .05));
auto white = make_shared<lambertian>(color(.73, .73, .73));
auto green = make_shared<lambertian>(color(.12, .45, .15));
auto light = make_shared<diffuse_light>(color(15, 15, 15));
world.add(make_shared<quad>(Point3(555,0,0),vec3(0,555,0),vec3(0,0,555),green));
world.add(make_shared<quad>(Point3(0,0,0),vec3(0,555,0),vec3(0,0,555),red));
world.add(make_shared<quad>(Point3(343,544,332),vec3(-130,0,0),vec3(0,0,-105),light));
world.add(make_shared<quad>(Point3(0,0,0),vec3(555,0,0),vec3(0,0,555),white));
world.add(make_shared<quad>(Point3(555,555,555),vec3(-555,0,0),vec3(0,0,-555),white));
world.add(make_shared<quad>(Point3(0,0,555),vec3(555,0,0),vec3(0,555,0),white));
camera cam;
cam.aspect_ratio = 1.0;
cam.image_width = 600;
cam.samples_per_pixel = 200;
cam.max_depth = 50;
cam.background = color(0,0,0);
cam.vfov = 40;
cam.lookfrom = Point3(278, 278, -800);
cam.lookat = Point3(278, 278, 0);
cam.vup = vec3(0,1,0);
cam.defocus_angle = 0;
cam.render(world);
}
这里灯的y坐标写下来了一点。
图片看起来有很多的噪声点,因为灯非常小,大多数的随机射线没有打到灯光上。就会是背景色。