这是真实感图像渲染系列的第三篇文章。
场景初始化的必要性
不同于普通的编程作业,图像渲染的场景结构非常复杂,比如同样是物体,一个球体需要读入中心坐标以及半径,而平面则只需要一个定位坐标以及法线方向就能够确定。如何初始化一个场景,为每一个参数赋予正确的初始值是非常重要的。
在参考过往代码发现场景的初始化无非两种方式:一种是将场景作为一个类写在源代码中;另一种是将场景写在文本文件中,对于所有需要初始化的类写一个接口来读取文件。在我的代码中,使用了第二种初始化方式。就编译效率上来说,第二种方式可以防止每次修改模型都重新编译。这在后期可以优化出很多时间。
场景初始化方式
一方面,我使用json格式储存场景,json格式的读取在C++中确实不算简单,需要安装jsoncpp库。jsoncpp库提供了一个数据类型Json::Value ,这个数据类型支持整数下表访问(用于访问列表),以及字符串下表访问(用于访问字典),以及各种转换函数如val.asDouble() 等。最终场景的json文件附在本文末尾。
jsoncpp的简单使用可以参考 通过jsoncpp库的CharReaderBuilder解析字符串
我使用伪·visitor设计模式初始化场景,对于每一个需要初始化的类申明一个成员函数void accept(const Json::Value& val); 接受一个Json::Value 用于初始化自身。
例如,对于vector来说,accept函数如下。
void Vector :: accept(const Json::Value& val) { if (!val.isMember("x") || !val.isMember("y") || !val.isMember("z")) std::cout<<"The vector not found..."<<std::endl; x = val["x"].asDouble(); y = val["y"].asDouble(); z = val["z"].asDouble(); }
而一个包含vector的sphere类,可以拥有如下accept函数
void Sphere::accept(const Json::Value& val) { Object::accept(val); O.accept(val["center"]); radius = val["radius"].asDouble(); }
可见,至少在安装了库之后,json的读取是足够简单的。
为场景添加功能
场景本身会被后续不同的渲染算法所使用,为了增加代码复用性,我们可以把渲染算法公共的一些函数放在场景中,例如求一个光线最近碰撞到的物体的函数findCollidedObject 等。最终场景类如下
class Scene { protected: Camera *camera; std::vector<Light*> lights; std::vector<Object*> objects; Color bg_color; public: Scene(); ~Scene(); void accept(const Json::Value& val,int _rx,int _ry); const Object* findCollidedObject(const Vector &rayO,const Vector& rayD,Collision& coll); const Light* findCollidedLight(const Vector& rayO,const Vector& rayD, Collision& coll); };
后续的光线追踪等代码,都会直接继承scene来使用scene的这些接口。
源代码
场景json文件
{ "rx": 512, "ry": 1024, "_rx": 5, "_ry": 10, "photon_num": 20000, "total_round": 80, "total_brightness": 1310.0, "round_decay": 0.95, "initial_r": 5, "r_decay": 0.8, "camera": { "type": "default", "position": { "x": 20, "y": -10, "z": 0 }, "dx": { "x": -10, "y": 0, "z": 0 }, "dy": { "x": 0, "y": 20, "z": 0 }, "origin": { "x": 40, "y": 0, "z": -50 }, "fdepth": 0.02 }, "lights": [ { "type": "point_light", "name": "light1", "brightness": 40, "position": { "x": 12, "y": -5, "z": 27 }, "texture": { "type": "pure", "r": 1, "g": 1, "b": 1 }, "material": { "refl": 0, "diff": 0, "spec": 0, "refr": 0.0, "refr_k": 1.0 } }, { "type": "point_light", "name": "light1", "brightness": 50, "position": { "x": 37, "y": 0.6, "z": -45 }, "texture": { "type": "pure", "r": 1, "g": 1, "b": 1 }, "material": { "refl": 0, "diff": 0, "spec": 0, "refr": 0.0, "refr_k": 1.0 } } ], "objects": [ { "type": "sphere", "name": "marble_ball", "center": { "x": 3, "y": 0, "z": 30 }, "radius": 1.7, "texture": { "type": "picture", "filename": "materials/marble.bmp", "rx": 2, "ry": 3.14 }, "absorb": { "type": "pure", "r": 0.5, "g": 0.7, "b": 0.5 }, "material": { "refl": 0.3, "diff": 0.8, "spec": 0.6, "refr": 0.0, "refr_k": 1.3 } }, { "type": "sphere", "name": "marble_ball", "center": { "x": 3, "y": -6, "z": 29 }, "radius": 1.7, "texture": { "type": "picture", "filename": "materials/marble2.jpg", "rx": 2, "ry": 3.14 }, "absorb": { "type": "pure", "r": 0.5, "g": 0.7, "b": 0.5 }, "material": { "refl": 0.3, "diff": 0.8, "spec": 0.6, "refr": 0.0, "refr_k": 1.3 } }, { "type": "sphere", "name": "ball", "center": { "x": 3, "y": 0, "z": 22 }, "radius": 2, "texture": { "type": "pure", "r": 0.7, "g": 0, "b": 0.7 }, "absorb": { "type": "pure", "r": 0.4, "g": 0.1, "b": 0.01 }, "material": { "refl": 0.1, "diff": 0.0, "spec": 0.6, "refr": 0.9, "refr_k": 1.3 } }, { "type": "sphere", "name": "marble_ball", "center": { "x": 35, "y": 1, "z": -40 }, "radius": 0.2, "texture": { "type": "picture", "filename": "materials/marble2.jpg", "rx": 2, "ry": 3.14 }, "absorb": { "type": "pure", "r": 0.5, "g": 0.7, "b": 0.5 }, "material": { "refl": 0, "diff": 1.0, "spec": 0.6, "refr": 0, "refr_k": 1.3 } }, { "type": "bazier_curves", "name": "cup", "position": { "x": 0, "y": 0, "z": 30 }, "ctrl_pts": [ [ [ 0.0, 1.1, 1.2, 2.0 ], [ 0.04, 0.04, -0.0, -0.0 ] ], [ [ 2.0, 2.2, 2.0, 1.9 ], [ -0.0, -0.0, 0.1, 0.15 ] ], [ [ 1.9, 1.73, 0.4, 0.2 ], [ 0.15, 0.23, -0.2, 0.4 ] ], [ [ 0.2, -0.1, 3.8, 3.8 ], [ 0.4, 1.3, 1.4, 2.0 ] ], [ [ 3.8, 3.8, 3.6, 3.6 ], [ 2.0, 2.2, 2.2, 2.0 ] ], [ [ 3.6, 3.6, 0.25, 0.0 ], [ 2.0, 1.9, 1.3, 1.3 ] ] ], "texture": { "type": "pure", "r": 0.7, "g": 0.7, "b": 0.3 }, "absorb": { "type": "pure", "r": 0.1, "g": 0.1, "b": 0.5 }, "material": { "refl": 0.0, "diff": 0.0, "spec": 0.0, "refr": 0.9, "refr_k": 1.3 } }, { "type": "plane", "name": "floor", "position": { "x": 0, "y": -10, "z": 25 }, "dx": { "x": 0, "y": 0, "z": 30 }, "dy": { "x": 0, "y": 20, "z": 0 }, "texture": { "type": "picture", "filename": "materials/floor.jpg", "rx": 8, "ry": 8 }, "material": { "refl": 0.4, "diff": 0.6, "spec": 0.0, "refr": 0.0, "refr_k": 1.0 }, "border": false } ], "bg_color": { "type": "pure", "r": 0.0, "g": 0.0, "b": 0.0 }, "max_depth": 20, "max_jump": 20, "start_rows": 0, "start_cols": 0 }