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.youkuaiyun.com/detail/libing_zeng/9815623