Chapter 6:Rectangles and Lights
今天,我们来学习长方形区域光照
先看效果
light
首先我们需要设计一个发光的材质
/// light.hpp // ----------------------------------------------------- // [author] lv // [begin ] 2019.1 // [brief ] the areaLight-class for the ray-tracing project // from the 《ray tracing the next week》 // ----------------------------------------------------- #pragma once namespace rt { //the statement of areaLight class class areaLight :public material { public: areaLight() { } areaLight(texture* mat) :_emit(mat) { } virtual bool scatter(const ray& InRay, const hitInfo& info, rtvec& attenuation, ray& scattered)const { return false; } virtual rtvec emitted(rtvar u, rtvar v, const rtvec& p)const { return _emit->value(u, v, p); } private: texture* _emit; }; } // rt namespace
关于设计方面,我们需要把发光函数设为可继承虚函数,基类也要添加,但是不是所有的材质都需要发光,所以,基类中的发光函数并不需要设置为纯虚
/// material.hpp // ----------------------------------------------------- // [author] lv // [begin ] 2018.12 // [brief ] the material-class for the ray-tracing project // from the 《ray tracing in one week》 // ----------------------------------------------------- #pragma once namespace rt { // the statement of material class class material { public: /* @brief: produce a scattered ray @param: InRay -> Incident light info -> the information of intersect-point(hit-point) attenuation -> when scattered, how much the ray should be attenuated by tis reflectance R scattered -> as we talk, it is a new sight; or it is the scattered ray with the intersect-point @retur: the function calculate a scattered ray or not */ virtual bool scatter(const ray& InRay, const hitInfo& info, rtvec& attenuation, ray& scattered)const = 0; /* @brief: 自发光 @param: 纹理所需信息 @retur: 纹理像素值 */ virtual rtvec emitted(rtvar u, rtvar v, const rtvec& p)const { return rtvec(); } }; }
这样的话,一般的材质继承之后,发光为黑色即不发光,较为合理
我们既然添加了光照,那么计算插值函数时候也要将它加进去
到此,我们的发光材质就设置妥当了
rectangle
我们定义的长方形均为平行于轴的
(引用书上一张图)
假设长方形位于 z = k 平面,x和y边界如上,交点为P(x,y,k)
我们如何确定光线参数t?
已知:
光线:p(t) = eye + t * direction
则,z方向的方程为:z(t) = eye.z + t * direction.z
那么,若满足z = k,则
t = (k - eye.z) / direction.z
同理可得x和y的等式
如果,得到的x坐标或者y坐标不在边界之内,那么就没有相交,反之则光线和长方形相交
上面的代码都比较简单,那个 hit 呢,就是,根据已知的一个分量求出t,然后,把这个解带入求出对应的其他两个分量,如果其他两个分量不在边界内,那么返回false
反之,我们求取该点的纹理坐标,以及其他碰撞点信息记录之
获取包围盒嘛,理论上面无厚薄,线无粗细,但是实际中面有厚薄,我们可以将厚度设置为0.0002,以此模拟理论厚度
这个没什么问题,我们就往下进行
我们来做Cornell box
相机参数设置:
得到的图如下:
有几个面是黑色的??也就是根本没画出来
我们细细看一下,发现,长方形的法向量是关键
比如画出来的红墙,对面与之平行的面的法线是朝左边的,展现在我们视线中的是背面
所以,我们有时候需要反转一下法向量
/// flip_normal.hpp // ----------------------------------------------------- // [author] lv // [begin ] 2019.1 // [brief ] the flip_normal-class for the ray-tracing project // from the 《ray tracing the next week》 // ----------------------------------------------------- #pragma once namespace rt { class flip_normal: public intersect { public: flip_normal(intersect * p) :_p(p) { } virtual bool hit(const ray& sight, rtvar t_min, rtvar t_max, hitInfo& info)const override { if (_p->hit(sight, t_min, t_max, info)) { info._n = -info._n; return true; } return false; } virtual aabb getbox()const override { return _p->getbox(); } private: intersect* _p; }; } // rt namespace
这样就可以了,我们改一下场景
如下:
此外,我们还需要注意的是,light对应的纹理中的数值越大光强越强
我们可以试一下
material * light = new areaLight(new constant_texture(rtvec(20, 20, 20)));
如下:
可以看出来两张图对比之下,第二张亮多了
但是我们依旧看着很不舒服,好多黑点点,太难受了
我想啊想,为什么这么多黑点??可能是因为背景是黑色的,毕竟是漫反射,如果随机反射失败那就是黑色,所以随机反射点可能产生好多黑色小点,你千万别想着换成镜面材质,那个更无语
所以啊,我想了下,把背景改为白色,那样更好,毕竟色彩中掺杂一点白色,无伤大雅
如是,我改了下,效果可观
感谢您的阅读,生活愉快~
原文地址:https://www.cnblogs.com/lv-anchoret/p/10303112.html