ray tracing in swift

Ray tracing is a powerful technique for rendering photorealistic images. In Swift, you can implement ray tracing by following these general steps:

  1. Define the scene that you want to render, including its geometry and lighting.

  2. Cast rays from the viewpoint of the camera through each pixel in the image plane.

  3. For each ray, determine the closest intersection with an object in the scene.

  4. Calculate the color of the closest intersected object at the intersection point, taking into account the lighting in the scene.

  5. Assign the calculated color to the corresponding pixel in the image.

Here's an example Swift implementation of ray tracing that renders a simple scene consisting of a sphere and a plane:

main.swift
struct Ray {
    let origin: Vector3
    let direction: Vector3
}

struct Vector3 {
    let x, y, z: Double

    init(_ x: Double, _ y: Double, _ z: Double) {
        self.x = x
        self.y = y
        self.z = z
    }

    func dot(_ other: Vector3) -> Double {
        return x * other.x + y * other.y + z * other.z
    }

    func normalized() -> Vector3 {
        let length = sqrt(x * x + y * y + z * z)
        return Vector3(x / length, y / length, z / length)
    }
}

struct Sphere {
    let center: Vector3
    let radius: Double

    func intersect(_ ray: Ray) -> Double? {
        let oc = ray.origin - center
        let a = ray.direction.dot(ray.direction)
        let b = 2.0 * oc.dot(ray.direction)
        let c = oc.dot(oc) - radius * radius
        let discriminant = b * b - 4 * a * c
        if discriminant < 0 {
            return nil
        } else {
            return (-b - sqrt(discriminant)) / (2 * a)
        }
    }

    func normal(_ point: Vector3) -> Vector3 {
        return (point - center).normalized()
    }
}

struct Plane {
    let normal: Vector3
    let distance: Double

    func intersect(_ ray: Ray) -> Double? {
        let denom = normal.dot(ray.direction)
        if abs(denom) > 1e-6 {
            let t = (distance - normal.dot(ray.origin)) / denom
            if t >= 0 {
                return t
            }
        }
        return nil
    }

    func normal(_ _: Vector3) -> Vector3 {
        return normal.normalized()
    }
}

func castRay(_ ray: Ray, objects: [Intersectable]) -> Vector3 {
    let hits = objects.compactMap { $0.intersect(ray) }
    if let t = hits.min(), let object = objects.first(where: { $0.intersect(ray) == t }) {
        let point = ray.origin + ray.direction * t
        let normal = object.normal(point)
        let light = Vector3(-1, -1, -1).normalized()
        let diffuse = max(normal.dot(light), 0.0)
        return Vector3(diffuse, diffuse, diffuse)
    } else {
        return Vector3(0, 0, 0)
    }
}

protocol Intersectable {
    func intersect(_ ray: Ray) -> Double?
    func normal(_ point: Vector3) -> Vector3
}

let sphere = Sphere(center: Vector3(0, 0, -3), radius: 1)
let plane = Plane(normal: Vector3(0, 1, 0), distance: 0)
let objects: [Intersectable] = [sphere, plane]

let imageWidth = 16
let imageHeight = 9

var pixels = [Vector3]()

for y in 0..<imageHeight {
    for x in 0..<imageWidth {
        let u = Double(x) / Double(imageWidth - 1)
        let v = Double(y) / Double(imageHeight - 1)
        let ray = Ray(origin: Vector3(0, 0, 0), direction: Vector3(u * 2 - 1, v * 2 - 1, -1).normalized())
        let color = castRay(ray, objects: objects)
        pixels.append(color)
    }
}

// Output the image as an ASCII art
for pixel in pixels {
    if pixel.x + pixel.y + pixel.z > 1.5 {
        print("█", terminator: "")
    } else if pixel.x + pixel.y + pixel.z > 0.75 {
        print("▓", terminator: "")
    } else if pixel.x + pixel.y + pixel.z > 0.25 {
        print("▒", terminator: "")
    } else {
        print("░", terminator: "")
    }
}
3063 chars
116 lines

This code creates a 16x9 image by casting rays through each pixel in the image plane, and outputs the resulting image as an ASCII art. The sphere and plane are defined as Intersectable structs that implement the intersect and normal methods. The castRay function casts a ray and calculates the color of the closest object that the ray intersects, taking into account the shading model. Finally, the main loop iterates over all the pixels in the image, casts a ray from the camera viewpoint through each pixel, and outputs the corresponding ASCII character depending on the color of the closest object.

gistlibby LogSnag