本系列文章是對 metalkit.org 上面MetalKit內容的全面翻譯和學習.
讓我們繼續上週的工作完成ray tracer射線追蹤器
.如果我們想要用更多不同材料來渲染球體,Peter Shirley
推薦建立一個囊括了行為的抽象材料類.我自己作為一個電腦科學家,再同意不過了!
material
類能讓我們產生一個擴散射線並用它的反射係數來計算計算吸收多少減弱多少.讓我們建立一個新檔案命名為material.swift或其他你喜歡的名字.在這個檔案裡邊,讓我們建立一個protocol協議
,以適用於Swift
中的抽象類:
protocol material {
func scatter(ray_in: ray, _ rec: hit_record, inout _ attenuation: float3, inout _ scattered: ray) -> Bool
}
複製程式碼
現在我們有了material
的藍圖,我們可以渲染我們的漫反射(Lambertian郎伯特
)球體,只要用一個遵守material
協議的新的類就可以了.我們給它一個衰減因子,一個初始化方法,並實現協議中的scatter
方法:
class lambertian: material {
var albedo: float3
init(a: float3) {
albedo = a
}
func scatter(ray_in: ray, _ rec: hit_record, inout _ attenuation: float3, inout _ scattered: ray) -> Bool {
let target = rec.p + rec.normal + random_in_unit_sphere()
scattered = ray(origin: rec.p, direction: target - rec.p)
attenuation = albedo
return true
}
}
複製程式碼
對於metallic金屬質地
材料,射線並不是像Lambertian
材料那樣隨機擴散到其它方向,而是幾乎以入射角相同的度數沿法線進行反射,同樣的,我們的類有一個衰減因子,一個初始化方法,scatter
函式,還有一個fuzz模糊
因子,可以用它來調節材料表面反射,從高反射率到幾乎不反射都可調整:
class metal: material {
var albedo: float3
var fuzz: Float
init(a: float3, f: Float) {
albedo = a
if f < 1 {
fuzz = f
} else {
fuzz = 1
}
}
func scatter(ray_in: ray, _ rec: hit_record, inout _ attenuation: float3, inout _ scattered: ray) -> Bool {
let reflected = reflect(normalize(ray_in.direction), n: rec.normal)
scattered = ray(origin: rec.p, direction: reflected + fuzz * random_in_unit_sphere())
attenuation = albedo
return dot(scattered.direction, rec.normal) > 0
}
}
複製程式碼
我們還需要在objects.swift
檔案中的hit_record結構體中,加一個指向material
顏色的指標.當我們稍後計算出顏色後可以更新指標:
var mat_ptr: material
複製程式碼
下一步,我們需要調整ray.swift
檔案中的color() 函式,將material
指標考慮進去.注意我們還新增了一個depth深度
因子,這樣當射線接觸到物體時我們就能夠通過遞迴呼叫這個函式來更精確地計算顏色:
func color(r: ray, _ world: hitable, _ depth: Int) -> float3 {
var rec = hit_record()
if world.hit(r, 0.001, Float.infinity, &rec) {
var scattered = r
var attenuantion = float3()
if depth < 50 && rec.mat_ptr.scatter(r, rec, &attenuantion, &scattered) {
return attenuantion * color(scattered, world, depth + 1)
} else {
return float3(x: 0, y: 0, z: 0)
}
} else {
let unit_direction = normalize(r.direction)
let t = 0.5 * (unit_direction.y + 1)
return (1.0 - t) * float3(x: 1, y: 1, z: 1) + t * float3(x: 0.5, y: 0.7, z: 1.0)
}
}
複製程式碼
最後,在pixel.swift
檔案中,我們可以用我們新的material
類來建立多個物體:
var object = sphere(c: float3(x: 0, y: -100.5, z: -1), r: 100, m: lambertian(a: float3(x: 0, y: 0.7, z: 0.3)))
world.add(object)
object = sphere(c: float3(x: 1, y: 0, z: -1.1), r: 0.5, m: metal(a: float3(x: 0.8, y: 0.6, z: 0.2), f: 0.7))
world.add(object)
object = sphere(c: float3(x: -1, y: 0, z: -1.1), r: 0.5, m: metal(a: float3(x: 0.8, y: 0.8, z: 0.8), f: 0.1))
world.add(object)
object = sphere(c: float3(x: 0, y: 0, z: -1), r: 0.5, m: lambertian(a: float3(x: 0.3, y: 0, z: 0)))
world.add(object)
複製程式碼
在playground主頁面中,看看新產生的影像:
敬請期待本系列的下一部分,我們將會深入研究不同型別的材料及如何旋轉攝像機來獲得更好的觀察角度,這樣兩邊的球體將不會看起來扭曲了. 原始碼source code 已釋出在Github上.
下次見!