基本概念
體渲染(Volume),是繪制類似煙、霧、雲的效果。這種渲染和之前的表面渲染不同,光線可以在物體內部進行散射。
體渲染的主要特點
1. 可以在物體內部散射。
2. 從進入volume到發生散射的距離,與密度成反比。
3. 光線散射的方向,可以是任意方向。
實現
主要需要解決的問題:在什么位置發生碰撞(scatter),以及scatter ray的方向。
碰撞的問題在hitable
中解決。散射的問題在material
中解決。
class constant_medium : public hitable {
public:
constant_medium(hitable *b, float d, texture *a) : boundary(b), density(d) {
phase_function = new isotropic(a);
}
virtual bool hit(const ray& r, float t_min, float t_max, hit_record& rec) const;
virtual bool bounding_box(float t0, float t1, aabb& box) const {
return boundary->bounding_box(t0, t1, box);
}
hitable *boundary;
float density;
material *phase_function;
};
碰撞函數:
先和volume的輪廓求交,確定和輪廓的兩個交點,這樣體內的碰撞,將發生在兩個交點的連線上。
其在volume內的distance
,不會超過上述近交點和遠交點的距離
,並且與density
成反比。
bool constant_medium::hit(const ray& r, float t_min, float t_max, hit_record& rec) const {
hit_record rec1, rec2;
//直線與盒子的近交點
if (boundary->hit(r, -FLT_MAX, FLT_MAX, rec1)) {
//直線與盒子的遠交點
if (boundary->hit(r, rec1.t + 0.0001, FLT_MAX, rec2)) {
if (debugging) std::cerr << "\nt0 t1 " << rec1.t << " " << rec2.t << '\n';
if (rec1.t < t_min) rec1.t = t_min;
if (rec2.t > t_max) rec2.t = t_max;
if (rec1.t >= rec2.t)
return false;
if (rec1.t < 0)
rec1.t = 0;
//近交點和遠交點的距離
float distance_inside_boundary = (rec2.t - rec1.t) * r.direction().length();
//隨機生成傳輸距離,與密度成反比
float hit_distance = -(1 / density) * log(random_double());
if (hit_distance < distance_inside_boundary) {
//確定最終的碰撞位置
rec.t = rec1.t + hit_distance / r.direction().length();
rec.p = r.point_at_parameter(rec.t);
rec.normal = vec3(1, 0, 0); // arbitrary
rec.normal = unit_vector(vec3(random_double(), random_double(), random_double()));
//決定scatter ray的材質
rec.mat_ptr = phase_function;
return true;
}
}
}
return false;
}
散射函數:
使用random_in_unit_sphere()
表示散射光線為任意方向。
class isotropic : public material {
public:
isotropic(texture *a) : albedo(a) {}
virtual bool scatter(const ray& r_in, const hit_record& rec, vec3& attenuation, ray& scattered) const {
//起點為碰撞點,方向為任意方向
scattered = ray(rec.p, random_in_unit_sphere(), r_in.time());
attenuation = albedo->value(rec.u, rec.v, rec.p);
return true;
}
texture *albedo;
};