如果我们想要每个对象都有自己的材质,我们可以这样设计。又一个不错的方法是,我们可以使用具有大量参数和不同材料类型的通用材料,只是将这些参数中的一部分清零。或者我们可以有一个封装行为的抽象材料类,我赞成第二种方法,对于编程材料,我们需要做两件事:
1.产生散射光线(或说它吸收了入射光线)
2.如果分散,说明射线应该衰减多少
撞击记录是为了避免一堆争论,所以我们可以填写任何我们想要的信息。您可以使用参数替代; 这取决于你的喜好。
可撞击性和材料需要彼此了解,因此参考文献中有一定的循环性。 在C ++中,你只需要提醒编译器该指针指向一个类,下面这个hitable类中的“class material”会这样做:
对于我们已经具有的理想散射情况下,它可以总是散射并且按照其反射率R衰减,或者它可以散射而没有衰减,但吸收光线的分数1-R。或者,它可能是这些战略的混合物。 对于朗伯材料,我们得到这个简单的课程:
照射光滑的金属的光线不会被随机散布,数学关键是:光线如何从金属镜面反射出来,这里就轮到我们的朋友-矢量数学登场了。
v与B夹角假设为α
则v与N夹角为180-α
反射光线方向为红色矢量,(v+2B) N为单位向量,v不是,所以B=dot(v,N)
(即,v在N方向上的投影与N的模(1)的乘积)
=》 |B| = |v|*cos α
同时,
B=|V|*|N|*cos(π-α)
=> B=|V|*|N|*(cosπ*cosα+sinπ*sinα)
=> -|V|*|N|cosα
V.N=-|V|*|N|cosα
cosα=-(v.N)/(|V|*|N|)
|B|=-(v.N)/(|N|)
B=-(v.N)*N
=》反射向量为 v+2B
=》v-2(v.N)*N
class metal(material): def __init__(self, a): self.albedo = a def relfect(self, v, n): return v - (2 * v.dot(n) * n) def scatter(self, r_in, rec, attenuation, scattered): reflected = self.relfect(r_in.direction().unit_vector(), rec.normal) scattered = ray(rec.p, reflected) attenuation.e = self.albedo.e return scattered.direction().dot(rec.normal) > 0
金属材质反射光线时,使用如下材质:
我们需要修改颜色函数来使用它
并添加一些金属球
我们也可以通过使用一个小球体并为射线选择一个新的端点来随机化反射方向:
球体越大,反射会越模糊。 这意味着要添加一个模糊度参数,这个参数就是球体的半径(因此零是无扰动)。 问题在于,对于大球体或放射线,我们可能会散射到表面之下。 我们可以用表面将其吸收。 我们会在球体的半径上放置最大值1,这会产生:
def color(r, world, depth): rec = hitable.hit_record if world.hit(r, 0.001, 2001100, rec): scattered = ray(vec3(1, 1, 1), vec3(1, 1, 1)) attenuation = vec3(1, 1, 1) if depth < 50 and rec.mat_ptr.scatter(r, rec, attenuation, scattered): ccc=color(scattered, world, depth + 1) return attenuation *ccc else: return vec3(0,0,0) else: unit_direction = r.direction().unit_vector() t = 0.5 * (unit_direction.y() + 1.0) return (1.0 - t) * vec3(1.0, 1.0, 1.0) + t * vec3(0.5, 0.7, 1.0)