这是真实感图像渲染系列的第三篇文章。
场景初始化的必要性
不同于普通的编程作业,图像渲染的场景结构非常复杂,比如同样是物体,一个球体需要读入中心坐标以及半径,而平面则只需要一个定位坐标以及法线方向就能够确定。如何初始化一个场景,为每一个参数赋予正确的初始值是非常重要的。
在参考过往代码发现场景的初始化无非两种方式:一种是将场景作为一个类写在源代码中;另一种是将场景写在文本文件中,对于所有需要初始化的类写一个接口来读取文件。在我的代码中,使用了第二种初始化方式。就编译效率上来说,第二种方式可以防止每次修改模型都重新编译。这在后期可以优化出很多时间。
场景初始化方式
一方面,我使用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
}