RT next week:ch08实例变换

Instance Transform 实例变换

盒子box

六个面拼接起来,没什么值得一提的。
我在这里做了一点小小的改进:在内部做了一个BVH结构,实测能快一倍

class box : public hitable  {
    public:
        box() {}
        box(const vec3& p0, const vec3& p1, shared_ptr<material> ptr);
        virtual bool hit(const ray& r, double t_min, double t_max, record& rec) const override;
        virtual bool bounding_box(AABB& output_box) const override {
            bvh.bounding_box(output_box);
            return true;
        }
    public:
        vec3 box_min;
        vec3 box_max;
        
        bvh_node bvh;
};
inline box::box(const vec3& p0, const vec3& p1, shared_ptr<material> ptr) {
    box_min = p0;
    box_max = p1;
    hitable_list sides;
    sides.add(make_shared<xy_rect>(p0.x(), p1.x(), p0.y(), p1.y(), p1.z(), ptr));
    sides.add(make_shared<xy_rect>(p0.x(), p1.x(), p0.y(), p1.y(), p0.z(), ptr));
    sides.add(make_shared<xz_rect>(p0.x(), p1.x(), p0.z(), p1.z(), p1.y(), ptr));
    sides.add(make_shared<xz_rect>(p0.x(), p1.x(), p0.z(), p1.z(), p0.y(), ptr));
    sides.add(make_shared<yz_rect>(p0.y(), p1.y(), p0.z(), p1.z(), p1.x(), ptr));
    sides.add(make_shared<yz_rect>(p0.y(), p1.y(), p0.z(), p1.z(), p0.x(), ptr));
    bvh = bvh_node(sides);
}
inline bool box::hit(const ray& r, double t_min, double t_max, record& rec) const {
    return bvh.hit(r, t_min, t_max, rec);
}

位移

在光追器中,让一个物体位移不一定真的需要修改物体的坐标,改变光的位置也可以!

把光的起点加上一个负的位移分量,计算相交,然后把交点再加回位移分量就行了

class translate : public hitable {
public:
  translate(std::shared_ptr<hitable> &p, const vec3 &displacement)
      : tar(p), offset(displacement) {}
  bool bounding_box(AABB &box_out) const override;
  bool hit(const ray &r, double t_min, double t_max,
           record &rec) const override;

private:
  std::shared_ptr<hitable> tar;
  vec3 offset;
};

inline bool translate::hit(const ray &r, double t_min, double t_max,
                           record &rec) const {
  ray ray_moved(r.origin() - offset, r.dir);
  if (!tar->hit(ray_moved, t_min, t_max, rec)) {
    return false;
  }
  rec.p = rec.p + offset;
  set_face_normal(ray_moved, rec.normal, rec);
  return true;
}

inline bool translate::bounding_box(AABB &box_out) const {
  if (!tar->bounding_box(box_out))
    return false;
  box_out = AABB(box_out.min() + offset, box_out.max() + offset);
  return true;
}

旋转

和上面的思路大致相同
先把起点转过去,再把方向转一转,最后把交点转回来.........
本质上就是个旋转矩阵

class rotate_y : public hitable{
public:
        rotate_y(shared_ptr<hitable> p, double angle);
        virtual bool hit(
            const ray& r, double t_min, double t_max, record& rec) const override;
        virtual bool bounding_box(AABB& output_box) const override {
            output_box = bbox;
            return hasbox;
        }

    public:
        shared_ptr<hitable> ptr;
        double sin_theta;
        double cos_theta;
        bool hasbox;
        AABB bbox;
};
inline rotate_y::rotate_y(shared_ptr<hitable> p, double angle) : ptr(p) {
    auto radians = d2radian(angle);
    sin_theta = sin(radians);
    cos_theta = cos(radians);
    hasbox = ptr->bounding_box(bbox);
    vec3 min( Infinity,  Infinity,  Infinity);
    vec3 max(-Infinity, -Infinity, -Infinity);
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 2; j++) {
            for (int k = 0; k < 2; k++) {
                auto x = i*bbox.max().x() + (1-i)*bbox.min().x();
                auto y = j*bbox.max().y() + (1-j)*bbox.min().y();
                auto z = k*bbox.max().z() + (1-k)*bbox.min().z();
                auto newx =  cos_theta*x + sin_theta*z;
                auto newz = -sin_theta*x + cos_theta*z;
                vec3 tester(newx, y, newz);
                for (int c = 0; c < 3; c++) {
                    min[c] = fmin(min[c], tester[c]);
                    max[c] = fmax(max[c], tester[c]);
                }
            }
        }
    }
    bbox = AABB(min, max);
}
inline bool rotate_y::hit(const ray& r, double t_min, double t_max, record& rec) const {
    auto origin = r.origin();
    auto direction = r.direction();
    origin[0] = cos_theta*r.origin()[0] - sin_theta*r.origin()[2];
    origin[2] = sin_theta*r.origin()[0] + cos_theta*r.origin()[2];
    direction[0] = cos_theta*r.direction()[0] - sin_theta*r.direction()[2];
    direction[2] = sin_theta*r.direction()[0] + cos_theta*r.direction()[2];
    ray rotated_r(origin, direction);
    if (!ptr->hit(rotated_r, t_min, t_max, rec))
        return false;
    auto p = rec.p;
    auto normal = rec.normal;
    p[0] =  cos_theta*rec.p[0] + sin_theta*rec.p[2];
    p[2] = -sin_theta*rec.p[0] + cos_theta*rec.p[2];
    normal[0] =  cos_theta*rec.normal[0] + sin_theta*rec.normal[2];
    normal[2] = -sin_theta*rec.normal[0] + cos_theta*rec.normal[2];
    rec.p = p;
    set_face_normal(rotated_r, normal,rec);
    return true;
}

#结果

后记

这章节怎么说呢.....
有点扯淡,每次旋转的计算量大上天了,真不如直接把点进行位移
我猜测可能是因为作者觉得这只是本入门书籍所以没写这么多吧,毕竟模型变换一讲就是一大章节。
后续我会把我在魔改rayTracer里做的那个变换系统移植过来,大概...在这本书做完之后8

END

留言