Q103:磨边的物体(Beveled Objects)

198 篇文章 12 订阅
195 篇文章 27 订阅

0,引入

其实,这一章节的内容是之前落下的。
当时,觉得“磨边物体”只是磨边前的物体与部分圆环、部分球面组成的复合物体(Compound Objects)。感觉太简单了,所以没有仔细看。
我们在前面的某些章节中已经有用到过“磨边”的概念,比如:
“Q91:真实地模拟透明材质(Realistic Transparency)(4)——Fish Bowl”中的鱼缸的边沿;
http://blog.csdn.net/libing_zeng/article/details/65442187
“ Q101:真实地模拟一个玻璃酒杯(Wine Glass)(回旋曲面)”中酒杯的边沿;
http://blog.csdn.net/libing_zeng/article/details/69791261

1,简单的磨边的物体

另外,我们再看一些简单几何图形的磨边后的图形(如下三张图片来自《Ray Tracing from the Ground Up》官网):
磨边前:
这里写图片描述
磨边后:
这里写图片描述

看看其中磨边封闭圆柱面的各个部分分离的图形:
这里写图片描述

注意到:
“磨边”的意思就是:
面与面相交为曲线时,添加一个圆环面来过渡;
面与面相交为直线时,添加一个开放圆柱面来过渡;
线与线相交时,添加一个球面来过渡;

所以,
磨边的封闭圆柱面是由:1个开放圆柱面、2个底面、2个圆环组成;
磨边的长方体是由:磨边前长方体对应的8个矩形、长方体的边对应的12个圆环、长方体的顶点对应的8个球面组成。

2,磨边的楔形物体(Beveled Wedges)

2.1 理论分析

我们在前面看到的都是对简单物体的磨边。但是,楔形物体的磨边貌似没有如此简单。

这里写图片描述
(相关参数:内经r0=1.5,外径r1=3,磨边半径rb=0.25)

这样一个磨边的楔形是由哪些基本图形组成的呢?
第一部分:磨边前的面:前后2个矩形、左右2个部分圆柱面、上下2个annulus;
第二部分:面与面的过渡:圆柱面和上下两个annulus相交过渡的4个圆环、圆柱面和矩形相交过渡的4个圆柱面、矩形和annulus相交过渡的4个圆柱面;
第三部分:线与线的过渡:8个小球面
总共是2+2+2+4+4+4+8=26个基本图形。

但是,书上说是一共是30个基本图形,另外4个是什么呢?在哪呢?
书上说,另外4个是上下面上的patch annuli(annuli补丁)。
“annuli补丁”,也就是说,磨边后的楔形的上下表面若分别只用一个annulus的话,则是缺一块的。所以,才需要“补丁”。

若将代码中的补丁图形注释掉一个,则得到的图形是:
这里写图片描述
看到木有?上表面是不是缺一块哈?
为了,看得更清晰些,咱改一下楔形的参数:内经r0=0.5,外径r1=3,磨边半径rb=0.5。得到的图形是:
这里写图片描述

磨边楔形俯视图如下:
这里写图片描述

参数的具体计算,咱在此不做推导(初中几何的常见内容)。
咱特别指出的是patch annulus,因为这个特别容易被忽略。

2.2 C++代码实现

// ------------------------------------------------------------------------------ constructor

BeveledWedge::BeveledWedge( const double _y0,       // minimum y value
                            const double _y1,       // maximum y value
                            const double _r0,       // inner radius
                            const double _r1,       // outer radius
                            const double _rb,       // bevel radius
                            const double _phi0,     // minimum azimuth angle in degrees
                            const double _phi1)     // maximum azimuth angle in degrees

    :   y0(_y0),
        y1(_y1),
        r0(_r0),
        r1(_r1),
        rb(_rb),
        phi0(_phi0),
        phi1(_phi1)
{
    double sin_phi0 = sin(phi0 * PI_ON_180);  // in radians
    double cos_phi0 = cos(phi0 * PI_ON_180);  // in radians
    double sin_phi1 = sin(phi1 * PI_ON_180);  // in radians
    double cos_phi1 = cos(phi1 * PI_ON_180);  // in radians

    double sin_alpha = rb / (r0 + rb);
    double cos_alpha = sqrt(r0 * r0 + 2.0 * r0 * rb) / (r0 + rb);
    double sin_beta = rb / (r1 - rb);
    double cos_beta = sqrt(r1 * r1 - 2.0 * r1 * rb) / (r1 - rb);

    double xc1 = (r0 + rb) * (sin_phi0 * cos_alpha + cos_phi0 * sin_alpha);
    double zc1 = (r0 + rb) * (cos_phi0 * cos_alpha - sin_phi0 * sin_alpha);

    double xc2 = (r1 - rb) * (sin_phi0 * cos_beta + cos_phi0 * sin_beta);
    double zc2 = (r1 - rb) * (cos_phi0 * cos_beta - sin_phi0 * sin_beta);

    double xc3 = (r0 + rb) * (sin_phi1 * cos_alpha - cos_phi1 * sin_alpha);
    double zc3 = (r0 + rb) * (cos_phi1 * cos_alpha + sin_phi1 * sin_alpha);

    double xc4 = (r1 - rb) * (sin_phi1 * cos_beta - cos_phi1 * sin_beta);
    double zc4 = (r1 - rb) * (cos_phi1 * cos_beta + sin_phi1 * sin_beta);


    // corner spheres -------------------------------------------------------------------------------

    // bottom spheres

    Sphere* bottom_c1 = new Sphere(Point3D(xc1, y0 + rb, zc1), rb);
    objects.push_back(bottom_c1);

    Sphere* bottom_c2 = new Sphere(Point3D(xc2, y0 + rb, zc2), rb);
    objects.push_back(bottom_c2);

    Sphere* bottom_c3 = new Sphere(Point3D(xc3, y0 + rb, zc3), rb);
    objects.push_back(bottom_c3);

    Sphere* bottom_c4 = new Sphere(Point3D(xc4, y0 + rb, zc4), rb);
    objects.push_back(bottom_c4);


    // top spheres

    Sphere* top_c1 = new Sphere(Point3D(xc1, y1 - rb, zc1), rb);
    objects.push_back(top_c1);

    Sphere* top_c2 = new Sphere(Point3D(xc2, y1 - rb, zc2), rb);
    objects.push_back(top_c2);

    Sphere* top_c3 = new Sphere(Point3D(xc3, y1 - rb, zc3), rb);
    objects.push_back(top_c3);

    Sphere* top_c4 = new Sphere(Point3D(xc4, y1 - rb, zc4), rb);
    objects.push_back(top_c4);


    // vertical cylinders ------------------------------------------------------------------------------

    Instance* bottom_c1_cylinder = new Instance(new OpenCylinder(y0 + rb, y1 - rb, rb));
    bottom_c1_cylinder->translate(xc1, 0.0, zc1);
    bottom_c1_cylinder->transform_texture(false);
    objects.push_back(bottom_c1_cylinder);

    Instance* bottom_c2_cylinder = new Instance(new OpenCylinder(y0 + rb, y1 - rb, rb));
    bottom_c2_cylinder->translate(xc2, 0.0, zc2);
    bottom_c2_cylinder->transform_texture(false);
    objects.push_back(bottom_c2_cylinder);

    Instance* bottom_c3_cylinder = new Instance(new OpenCylinder(y0 + rb, y1 - rb, rb));
    bottom_c3_cylinder->translate(xc3, 0.0, zc3);
    bottom_c3_cylinder->transform_texture(false);
    objects.push_back(bottom_c3_cylinder);

    Instance* bottom_c4_cylinder = new Instance(new OpenCylinder(y0 + rb, y1 - rb, rb));
    bottom_c4_cylinder->translate(xc4, 0.0, zc4);
    bottom_c4_cylinder->transform_texture(false);
    objects.push_back(bottom_c4_cylinder);


    // inner curved surface ---------------------------------------------------------------------------------

    // the azimuth angle range has to be specified in degrees

    double alpha = acos(cos_alpha);  // radians
    double phi_min = phi0 + alpha * 180.0 / PI;
    double phi_max = phi1 - alpha * 180.0 / PI;

    OpenCylinderPartConcave* inner_cylinder_ptr = new OpenCylinderPartConcave(y0 + rb, y1 - rb, r0, phi_min, phi_max);
    objects.push_back(inner_cylinder_ptr);


    // outer curved surface -----------------------------------------------------------------------------------

    // the azimuth angle range has to be specified in degrees

    double beta = acos(cos_beta);  // radians
    phi_min = phi0 + beta * 180.0 / PI;
    phi_max = phi1 - beta * 180.0 / PI;

    OpenCylinderPartConvex* outer_cylinder_ptr = new OpenCylinderPartConvex(y0 + rb, y1 - rb, r1, phi_min, phi_max);
    objects.push_back(outer_cylinder_ptr);


    // phi0 vertical rectangle

    double s1 = sqrt(r0 * r0 + 2.0 * r0 * rb);
    double s2 = sqrt(r1 * r1 - 2.0 * r1 * rb);
    Point3D p1(s1 * sin_phi0, y0 + rb, s1 * cos_phi0);
    Point3D p2(s2 * sin_phi0, y0 + rb, s2 * cos_phi0);
    Vector3D a = p2 - p1;
    Vector3D b(0, y1 - y0 - 2.0 * rb, 0);

    Rectangle* phi0_rectangle_ptr = new Rectangle(p1, a, b);
    objects.push_back(phi0_rectangle_ptr);


    // phi1 vertical rectangle

    Point3D p3(s1 * sin_phi1, y0 + rb, s1 * cos_phi1);
    Point3D p4(s2 * sin_phi1, y0 + rb, s2 * cos_phi1);
    a = p3 - p4;

    Rectangle* phi1_rectangle_ptr = new Rectangle(p4, a, b);
    objects.push_back(phi1_rectangle_ptr);



    // the tori --------------------------------------------------------------------------------------------

    // inner bottom

    phi_min = phi0 - 90 + alpha * 180.0 / PI; // "-90" transform angle of against z-axis to x-axis
    phi_max = phi1 - 90 - alpha * 180.0 / PI;

    Instance* inner_bottom_torus = new Instance(new TorusPartConvex(r0 + rb, rb, phi_min, phi_max, 0, 360));
    inner_bottom_torus->translate(0.0, y0 + rb, 0.0);
    inner_bottom_torus->transform_texture(false);
    objects.push_back(inner_bottom_torus);


    // inner top

    Instance* inner_top_torus = new Instance(new TorusPartConvex(r0 + rb, rb, phi_min, phi_max, 0, 360));
    inner_top_torus->translate(0.0, y1 - rb, 0.0);
    inner_top_torus->transform_texture(false);
    objects.push_back(inner_top_torus);


    // outer bottom

    phi_min = phi0 - 90 + beta * 180.0 / PI;
    phi_max = phi1 - 90 - beta * 180.0 / PI;

    Instance* outer_bottom_torus = new Instance(new TorusPartConvex(r1 - rb, rb, phi_min, phi_max, 0, 360));
    outer_bottom_torus->translate(0.0, y0 + rb, 0.0);
    outer_bottom_torus->transform_texture(false);
    objects.push_back(outer_bottom_torus);


    // outer top

    Instance* outer_top_torus = new Instance(new TorusPartConvex(r1 - rb, rb, phi_min, phi_max, 0, 360));
    outer_top_torus->translate(0.0, y1 - rb, 0.0);
    outer_top_torus->transform_texture(false);
    objects.push_back(outer_top_torus);


    // horizontal cylinders ----------------------------------------------------------------------------------

    // phi0 bottom cylinder

    Instance* phi0_bottom_cylinder_ptr = new Instance(new OpenCylinder(0, s2 - s1, rb));
    phi0_bottom_cylinder_ptr->rotate_x(90);
    phi0_bottom_cylinder_ptr->rotate_y(phi0);
    phi0_bottom_cylinder_ptr->translate(xc1, y0 + rb, zc1);
    phi0_bottom_cylinder_ptr->transform_texture(false);
    objects.push_back(phi0_bottom_cylinder_ptr);


    // phi0 top cylinder

    Instance* phi0_top_cylinder_ptr = new Instance(new OpenCylinder(0, s2 - s1, rb));
    phi0_top_cylinder_ptr->rotate_x(90);
    phi0_top_cylinder_ptr->rotate_y(phi0);
    phi0_top_cylinder_ptr->translate(xc1, y1 - rb, zc1);
    phi0_top_cylinder_ptr->transform_texture(false);
    objects.push_back(phi0_top_cylinder_ptr);


    // phi1 bottom cylinder

    Instance* phi1_bottom_cylinder_ptr = new Instance(new OpenCylinder(0, s2 - s1, rb));
    phi1_bottom_cylinder_ptr->rotate_x(90);
    phi1_bottom_cylinder_ptr->rotate_y(phi1);
    phi1_bottom_cylinder_ptr->translate(xc3, y0 + rb, zc3);
    phi1_bottom_cylinder_ptr->transform_texture(false);
    objects.push_back(phi1_bottom_cylinder_ptr);


    // phi1 top cylinder

    Instance* phi1_top_cylinder_ptr = new Instance(new OpenCylinder(0, s2 - s1, rb));
    phi1_top_cylinder_ptr->rotate_x(90);
    phi1_top_cylinder_ptr->rotate_y(phi1);
    phi1_top_cylinder_ptr->translate(xc3, y1 - rb, zc3);
    phi1_top_cylinder_ptr->transform_texture(false);
    objects.push_back(phi1_top_cylinder_ptr);


    // top flat surface -----------------------------------------------------------------------------------

    // main part

    Point3D center(0, y1, 0);
    Normal normal(0, 1, 0);
    double r_min = r0 + rb;
    double r_max = r1 - rb;
    phi_min = phi0 + alpha * 180.0 / PI;
    phi_max = phi1 - alpha * 180.0 / PI;

    AnnulusPart* top_main_part_ptr = new AnnulusPart(center, normal, r_min, r_max, phi_min, phi_max);
    objects.push_back(top_main_part_ptr);


    // small phi0 side patch

    r_min = 0.0;
    r_max = s2 - s1;
    phi_min = 0.0;
    phi_max = alpha * 180.0 / PI;

    Instance* top_phi0_patch_ptr = new Instance(new AnnulusPart(center, normal, r_min, r_max, phi_min, phi_max));
    top_phi0_patch_ptr->rotate_y(phi0);
    top_phi0_patch_ptr->translate(xc1, 0.0, zc1);
    top_phi0_patch_ptr->transform_texture(false);
    objects.push_back(top_phi0_patch_ptr);


    // small phi1 side patch

    phi_min = 360.0 - alpha * 180.0 / PI;
    phi_max = 360.0;

    Instance* top_phi1_patch_ptr = new Instance(new AnnulusPart(center, normal, r_min, r_max, phi_min, phi_max));
    top_phi1_patch_ptr->rotate_y(phi1);
    top_phi1_patch_ptr->translate(xc3, 0.0, zc3);
    top_phi1_patch_ptr->transform_texture(false);
    objects.push_back(top_phi1_patch_ptr);



    // bottom flat surface ---------------------------------------------------------------------------------

    // main part

    center = Point3D(0, y0, 0);
    normal = Normal(0, -1, 0);
    r_min = r0 + rb;
    r_max = r1 - rb;
    phi_min = phi0 + alpha * 180.0 / PI;
    phi_max = phi1 - alpha * 180.0 / PI;

    AnnulusPart* bottom_main_part_ptr = new AnnulusPart(center, normal, r_min, r_max, phi_min, phi_max);
    objects.push_back(bottom_main_part_ptr);


    // small phi0 side patch

    r_min = 0.0;
    r_max = s2 - s1;
    phi_min = 0.0;
    phi_max = alpha * 180.0 / PI;

    Instance* bottom_phi0_patch_ptr = new Instance(new AnnulusPart(center, normal, r_min, r_max, phi_min, phi_max));
    bottom_phi0_patch_ptr->rotate_y(phi0);
    bottom_phi0_patch_ptr->translate(xc1, 0.0, zc1);
    bottom_phi0_patch_ptr->transform_texture(false);
    objects.push_back(bottom_phi0_patch_ptr);


    // small phi1 side patch

    phi_min = 360.0 - alpha * 180.0 / PI;
    phi_max = 360.0;

    Instance* bottom_phi1_patch_ptr = new Instance(new AnnulusPart(center, normal, r_min, r_max, phi_min, phi_max));
    bottom_phi1_patch_ptr->rotate_y(phi1);
    bottom_phi1_patch_ptr->translate(xc3, 0.0, zc3);
    bottom_phi1_patch_ptr->transform_texture(false);
    objects.push_back(bottom_phi1_patch_ptr);



    // compute the bounding box

    double x[4] = {xc1, xc2, xc3, xc4};
    double z[4] = {zc1, zc2, zc3, zc4};


    // first, assume that the wedge is completely inside a quadrant, which will be true for most wedges

    // work out the maximum and minimum values

    double x0 = kHugeValue;
    double z0 = kHugeValue;

    for (int j = 0; j <= 3; j++)  {
        if (x[j] < x0)
            x0 = x[j];
    }

    for (int j = 0; j <= 3; j++) {
        if (z[j] < z0)
            z0 = z[j];
    }

    double x1 = -kHugeValue;
    double z1 = -kHugeValue;

    for (int j = 0; j <= 3; j++) {
        if (x[j] > x1)
            x1 = x[j];
    }

    for (int j = 0; j <= 3; j++) {
        if (z[j] > z1)
            z1 = z[j];
    }

    // assign values to the bounding box

    bbox.x0 = x0 - rb;
    bbox.y0 = y0;
    bbox.z0 = z0 - rb;
    bbox.x1 = x1 + rb;
    bbox.y1 = y1;
    bbox.z1 = z1 + rb;

    bool spans90 = phi0 < 90 and phi1 > 90;
    bool spans180 = phi0 < 180 and phi1 > 180;
    bool spans270 = phi0 < 270 and phi1 > 270;

    if (spans90 && spans180 && spans270) {
        bbox.x0 = -r1;
        bbox.z0 = -r1;
        bbox.x1 = r1;
        bbox.z1 = max(zc2, zc4);
    }
    else if (spans90 && spans180) {
        bbox.x0 = xc4 - rb;
        bbox.z0 = -r1;
        bbox.x1 = r1;
        bbox.z1 = zc2 + rb;
    }
    else if (spans180 && spans270) {
        bbox.x0 = -r1;
        bbox.z0 = -r1;
        bbox.x1 = xc2 + rb;
        bbox.z1 = zc4 + rb;
    }
    else if (spans90) {
        bbox.x0 = min(xc1, xc3);
        bbox.z0 = zc4 - rb;
        bbox.x1 = r1;
        bbox.z1 = zc2 + rb;
    }
    else if (spans180) {
        bbox.x0 = xc4 - rb;
        bbox.z0 = -r1;
        bbox.x1 = xc2 + rb;
        bbox.z1 = max(zc1, zc3);
    }
    else if (spans270) {
        bbox.x0 = -r1;
        bbox.z0 = zc2 - rb;
        bbox.x1 = max(xc1, xc3);
        bbox.z1 = zc4 + rb;
    }
}

3,其他说明

完整代码下载链接:
http://download.csdn.net/detail/libing_zeng/9815179

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值