看Ray Tracing in One Weekend的散射模型的時候,是這樣做的:

為了模擬散射效果,我們在光線入射點正上方取一個單位球體,在其內部隨機取一個點作為其反射的方向,然後從入射點方向再次發射一條射線進行採樣。

實際上這個散射模型應該是所謂的『郎伯』物體,它其實應該完全遵守郎伯餘弦定理( Lamberts cosine law),其實完全按照這個來實現也ok,但是我們go with the author.按照上述quote來模擬。

寫 Python 代碼:

def random_in_unit_sphere():
p = 2 * Vector3(random(), random(), random()) - Vector3(1, 1, 1)
p.normalize()
return p

def color(ray, world):
for item in world:
if item.hit(ray, 0, inf):
t, p, normal = item.hit(ray, 0, inf)
target = p + normal + random_in_unit_sphere()
return 0.5 * color(Ray(p, target - p), world)

unit_direction = ray.direction.normalize()
t = 0.5 * (unit_direction.y + 1)
return (1 - t) * Vector3(1, 1, 1) + t * Vector3(0.5 ,0.7, 1.0)

random_in_unit_sphere

首先明白函數 random_in_unit_sphere(), 這個函數是隨機產生在一個單位球體中的矢量,之所以我們不能隨意用 Vector3(random(), random(), random()) 是因為這樣只會產生三個屬於 $[0,1)$之間的隨機變數,這樣只會在『第一象限』?

color(ray, world)

之前我們的 color 函數是這樣:

def color(ray, world):
for item in world:
if item.hit(ray, 0, inf):
t, p, normal = item.hit(ray, 0, inf)
return 0.5 * Vector3(normal.x + 1, normal.y + 1, normal.z + 1)

unit_direction = ray.direction.normalize()
t = 0.5 * (unit_direction.y + 1)
return (1 - t) * Vector3(1, 1, 1) + t * Vector3(0.5 ,0.7, 1.0)

這裡我們採用的是當光線與物體相遇的時候,我們在物體表面繪製的是它『法線』的顏色。

而這裡我們做的主要更改是:

t, p, normal = item.hit(ray, 0, inf)
target = p + normal + random_in_unit_sphere()
return 0.5 * color(Ray(p, target - p), world)

這裡實際上長這樣:

target 我們的計算為 p + normal + random_in_unit_sphere(),此時我們又再從 hit point p發出一條射線,然後再來採樣,看到這個代碼,我的擔心有兩點:

  • 這裡存在color call color,會不會遞歸有問題?
  • 為什麼這樣就是對的?

不會有遞歸問題,因為雖然我們的p點是剛好射線和球相遇的點,我們生成的射線S是起點P,方向S,但是在 Sphere 中計算相遇的hit函數我們 t_min 設定是0,而計算的時候我們要求是 t > t_min.所以不會有遞歸的狀況產生。

實際上如果擔心也可以把射線和球相遇的點列印出來,然後可以看到遞歸當然沒有發生。

這樣是對的的原因是因為漫反射是隨機反射,之前我們畫了球體的『法向量』。現在我們畫的是隨機反射的方向,這裡我們還沒有到考慮任何『顏色』所以是『對的』。

看點有意思的圖片,顏色就用隨機的 target 位置:

函數這樣:

def color(ray, world):
for item in world:
if item.hit(ray, 0, inf):
t, p, normal = item.hit(ray, 0, inf)
target = p + normal + random_in_unit_sphere()
return 0.5 * Vector3(target.x+1, target.y+1, target.z+1)

圖片長這樣 ↓:

噪音的感覺

如果就按之上描述的方法做的話,圖片長這樣 ↓:

採取gamma修正之後的效果 ↓:

能夠看到比較清晰的小球的陰影

gamma函數就是物理光強和主觀灰度之間的映射關係,因為人眼中的灰度並非線性,而gamma函數恰好可以用一個冪函數表示。因此這裡使用了開平方的方式來提高亮度。

推薦閱讀:

相关文章