这是真实感图像渲染系列的第二篇文章。
颜色
颜色,向量与物体是各种渲染算法的基础。
颜色定义为三个取值在0-1之间的浮点数,分别表示RGB分量。由于分量的值恰好为0-1之间的浮点数,颜色不仅可以理解为每种颜色的强度,还可以理解为每种颜色光线的穿透率。换句话来说,这样定义的颜色同时支持乘法和加法。
class Color { private: double r,g,b; public: Color(); Color(double r,double g,double b); Color operator += (const Color&a); void accept(const Json::Value& val); double getR()const {return r;} double getG()const {return g;} double getB()const {return b;} friend Color operator +(const Color& a,const Color &b); friend Color operator *(const Color& a,double k); friend Color operator *(const Color& a,const Color& b); Color exp()const; std::string description()const; Color adjust()const; };
特别的,对于透明物体的折射,光线穿过物体,存在一定的吸收,该吸收率可以看做是距离的指数函数。即,如在红光吸收率0.1的物体中穿过2单位距离,则射出的红光应该为原来射入红光强度的
.
向量
向量的定义,也是三个0-1之间的浮点数,表示xyz坐标分量。向量同时具有表征方向和表征位置的功能,坐标本身在某种意义上也是坐标原点O加上某个表示方向的向量。
一个光线,我们就可以用两个向量和
分别表示他的起始位置以及方向,
是一个单位向量,某点
在光线上当且仅当
,且
。
class Vector { double x,y,z; public: Vector(); Vector(double x,double y,double z); Vector(const Eigen::Vector3d vec); double getX() const {return x;} double getY() const {return y;} double getZ() const {return z;} virtual void accept(const Json::Value &sl); friend Vector operator * (const Vector &a,const Vector &b); friend double operator ^ (const Vector &a,const Vector &b); friend Vector operator + (const Vector &a,const Vector &b); friend Vector operator - (const Vector &a,const Vector &b); friend Vector operator - (const Vector &a); friend Vector operator * (double k,const Vector &a); friend Vector operator * (const Vector &a,double k); friend bool operator == (const Vector &a,const Vector &b); friend Vector operator /(const Vector& a,double k); friend Vector each_min(const Vector& v1,const Vector& v2); friend Vector each_max(const Vector& v1,const Vector& v2); Vector unit()const; Vector reverse()const; double len()const; double sqrlen()const; bool isUnit()const; std::string description()const; Eigen::Vector3d eigen()const; static Vector randomVectorOnSphere(); };
物体
由于C++的多态机制,物体本身是一个抽象类,只需要支持:
- 判断和某个光线的交点
- 判断某个位置对应的贴图颜色
- 提取出诸如漫反射率等表面材质的性质
- 询问物体hash值(用于抗锯齿等)
struct Material { double refl;//reflection ratio double diff;//diffusion ratio double spec;//high light diffusion double refr;//refraction ratio double refr_k; void accept(const Json::Value& val); }; class Object { protected: unsigned hash; Material material; std::string name; Texture* texture; Texture* absorb; public: Object(); virtual ~Object(); virtual void accept(const Json::Value& val); unsigned getHash()const {return hash;} const Material& getMaterial()const {return material;} virtual bool collideWith(const Vector& rayO, const Vector& rayD,Collision& coll) = 0; virtual Color getColor(const Vector &pos)const = 0; std::string getName()const{return name;} const Texture& getAbsorb()const; };
碰撞
碰撞是渲染里面非常核心也很复杂的一个模块,我们使用一个专门的类记录碰撞的信息,实际上,对于碰撞,可以通过碰撞位置,碰撞法线方向
和如何光线方向
完全表述。有了这三个量,我们可以简单的计算出这个碰撞的反射、折射方向(其中折射方向需要物体折射率)。这里讲一个小的注意点,就是反射,折射光线的起始点不应该都设置为
,反射需要保证光线的起始位置和入社光线位置同侧,而折射恰恰相反,由于精度误差,可能
实际在物体表面的某一侧而我们用
作为新光线的起点是不妥的。解决方案很简单,对于反射,我们使用
,折射则使用
作为光线的其实位置。
class Object; struct Collision { Vector C;//The Center of Collision Vector N;//The normal of the plane Vector I;//The direction of reflaction double dist; bool face; Object* belongs; std::string description()const; void refraction(Vector& resO, Vector& resD)const; void reflection(Vector& resO, Vector& resD)const; void diffusion(Vector& resO, Vector& resD)const; void diffusion_hl(Vector& resO, Vector& resD)const; Vector getSurfaceC()const; Vector getBackfaceC()const; };