[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上.

下次见!

相关文章
相关标签/搜索