Q104:怎么用ray tracing画基于磨边楔形的“花环(Rosette)”

来源:互联网 发布:贵州省政法委数据维稳 编辑:程序博客网 时间:2024/04/30 12:35

0,引入

在Q103中,咱了解了磨边的楔形物体。
接下来,咱用这种楔形物体来拼出一个“花环”。

先看看,咱要画的花环是长什么样子:
这里写图片描述

从上图来看,我们需要完成两件事情:
1,用磨边楔形拼出“花环”的几何模型;
2,给“花环”中的每一个磨边楔形设置不同/随机的大理石纹理。

1,花环的几何模型

回忆一下磨边楔形的构造函数中的相关参数:

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

从如上参数来看:
纵向控制楔形形状的是内经r0和外径r1;
横向控制楔形形状的是起始方位角phi0和终止方位角phi1。

所以,为了是所有的楔形刚好拼成一个“花环”,我们的做法是:
根据单层环上楔形的个数来等分圆周;
根据花环的层数来等分花环的内经和外径之间的距离(宽度)。

C++代码实现:

新建一个称为“Rosette”的类,该类是继承于Grid:

class Rosette: public Grid {    public:        Rosette(void);                                      Rosette(    const int       _num_rings,                     const double    _hole_radius,                    const double    _ring_width,                    const double    _rb,                    const double    _y0,                    const double    _y1);           Rosette(const Rosette& bb);                         virtual Rosette*                                        clone(void) const;        Rosette&                                        operator= (const Rosette& rhs);             virtual                                                     ~Rosette(void);        void        construct_rosette(void);     private:        double      num_rings;      // maximum of 6        double      hole_radius;        double      ring_width;        double      rb;             // bevel radius        double      y0, y1;         // y axis extents        static const int num_wedges[6];  // number of wedges in each ring};

具体实现construct_rosette(void)成员方法:

const int Rosette::num_wedges[6] = {10, 12, 15, 18, 24, 36};  // these numbers exactly divide into 360// ------------------------------------------------------------------------------ construct_rosette// this function constructs the wedges in a rosette pattern and stores them in a grid// this is the regular version, for Figure 21.11voidRosette::construct_rosette(void) {    for (int k = 0; k < num_rings; k++) {        for (int j = 0; j < num_wedges[k]; j++) {            double angle_width = 360 / num_wedges[k];  // the azimuth angle extent of each wedge            double r0 = hole_radius + k * ring_width;            double r1 = hole_radius + (k + 1) * ring_width;            double phi0 = j * angle_width;            double phi1 = (j + 1) * angle_width;            BeveledWedge* wedge_ptr = new BeveledWedge(y0, y1, r0, r1, rb, phi0 , phi1);            add_object(wedge_ptr);        }    }}

2,给“花环”中的每一个磨边楔形设置不同/随机的大理石纹理。

相关代码片段如下:

    // put a different random marble texture on each wedge    // ramp image:    Image* image_ptr = new Image;    image_ptr->read_ppm_file(".\\TextureFiles\\ppm\\BlueMarbleRamp.ppm");    // marble texture parameters    int num_octaves = 4;    float lacunarity = 2.0;    float gain = 0.5;    float perturbation = 3.0;    int num_objects = rosette_ptr->get_num_objects();    for (int j = 0; j < num_objects; j++) {        // noise:        CubicNoise* noise_ptr = new CubicNoise;        noise_ptr->set_num_octaves(num_octaves);        noise_ptr->set_gain(gain);          // not relevant when num_octaves = 1        noise_ptr->set_lacunarity(lacunarity);     // not relevant when num_octaves = 1        // marble texture:        FBmTextureRamp* marble_ptr = new FBmTextureRamp(image_ptr);        marble_ptr->set_noise(noise_ptr);        marble_ptr->set_perturbation(perturbation);        // transformed marble texture        InstanceTexture* wedge_marble_ptr = new InstanceTexture(marble_ptr);        set_rand_seed(j * 10);        wedge_marble_ptr->scale(0.25);        wedge_marble_ptr->rotate_x(20.0 * (2.0 * rand_float() - 1.0));        wedge_marble_ptr->rotate_y(30.0 * (2.0 * rand_float() - 1.0));        wedge_marble_ptr->rotate_z(45.0 * (2.0 * rand_float() - 1.0));        wedge_marble_ptr->translate(10.0 * (2.0 * rand_float() - 1.0), 20.0 * (2.0 * rand_float() - 1.0), 30.0 * (2.0 * rand_float() - 1.0));        // marble material        SV_Matte* sv_matte_ptr = new SV_Matte;        sv_matte_ptr->set_ka(0.35);        sv_matte_ptr->set_kd(0.75);        sv_matte_ptr->set_cd(wedge_marble_ptr);        rosette_ptr->store_material(sv_matte_ptr, j);   // store material in the specified wedge    }

(Rosette是继承于Grid的,Grid中按顺序保存了每一个楔形的对象指针。先获取Rosette中总的楔形个数num_objects,然后依次给每一楔形设置随机的大理石纹理。)

3,Debug

在当前代码的基础上(主要指的是BeveledWedge),生成的“花环”图形是这样的:
这里写图片描述
当前的问题是:有些磨边楔形的磨边处的圆环面缺失了。

老实说,针对这个问题,本人算是付出了“惨重”的代价:
其一:一开始竟然没有发现这个问题,然后将Rosette用到了后面的一个场景中,由于该场景涉及的图形较多,生成高分辨率的图形耗时长达4~5个小时,本人花费了几十个小时生成了若干个不同距离的图形,后来发现Rosette的问题,一切重来;
其二:在发现Rosette存在部分缺失时,小生愚钝,又花了好几个小时才调试好。

出现该问题的原因是什么?

官网提供的BeveledWedge的相关代码,对应的phi是标准的“与+Z轴的夹角”(但又不是通常情况那样以+Z轴为起点顺时针旋转,而是逆时针旋转),而本人的“部分圆环”实现代码对应的phi用的是“与+X轴的夹角”(因为,我们一般是从+Z轴向着-Z的视角,选“与+X轴的夹角更为直观”)。所以,在给“部分圆环”传参数时,需要将“与+Z轴的夹角”转换为“与+X的夹角”。

怎么解决这个问题呢?

当前的做法是:直接减个90度。

这样,在减90度之后的phi_min或者phi_max都有可能出现负数。
而“部分圆环”中判断撞击点是否有效是依据phi是否在该[phi_min,phi_max]内,这个phi的范围是[0,360]。
所以,当phi_min或者phi_max中出现一个负数时,咱就给phi_min和phi_max分别加上360。
但是,这还有一个问题:考虑到phi_max原本可能是正数,而加360之后的值就大于了360度。而“部分圆环”中尚未考虑这种情况。
所以,“部分圆环”中判断phi值是否有效时,添加一种情况:若phi+360之后还小于phi_max,则phi值肯定有效。

代码修改

查这个问题花了很长时间,但是需要修改的代码却不多:

在给“部分圆环”传参数时,添加如下绿框标注的代码:
这里写图片描述

在“部分圆环”中判断phi是否有效时,添加如下绿框标注的代码:
这里写图片描述

4,其他说明

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

1 0
原创粉丝点击