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