[MetalKit]12-Ray-tracing-in-a-Swift-playground3射線追蹤3

本系列文章是對 metalkit.org 上面MetalKit內容的全面翻譯和學習.git

MetalKit系統文章目錄github


讓咱們繼續上週的工做完成ray tracer射線追蹤器.若是咱們想要用更多不一樣材料來渲染球體,Peter Shirley推薦建立一個囊括了行爲的抽象材料類.我本身做爲一個計算機科學家,再贊成不過了!swift

material類能讓咱們產生一個擴散射線並用它的反射係數來計算計算吸取多少減弱多少.讓咱們建立一個新文件命名爲material.swift或其餘你喜歡的名字.在這個文件裏邊,讓咱們建立一個protocol協議,以適用於Swift中的抽象類:dom

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模糊因子,能夠用它來調節材料表面反射,從高反射率到幾乎不反射均可調整:post

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深度因子,這樣當射線接觸到物體時咱們就可以經過遞歸調用這個函數來更精確地計算顏色:ui

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類來建立多個物體:spa

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主頁面中,看看新產生的圖像:翻譯

raytracing7.png

敬請期待本系列的下一部分,咱們將會深刻研究不一樣類型的材料及如何旋轉攝像機來得到更好的觀察角度,這樣兩邊的球體將不會看起來扭曲了. 源代碼source code 已發佈在Github上.

下次見!

相關文章
相關標籤/搜索