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

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

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.11

void
Rosette::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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值