SBWO/SimpleRayTracer.jl

88 lines
2 KiB
Julia

module SimpleRayTracer
using LinearAlgebra
using Images
using GeometryBasics
using Rotations
export OrthogonalCamera
export World, Ray
export Sphere
abstract type AbstractCamera end
abstract type AbstractObject end
struct OrthogonalCamera{T<:AbstractFloat} <: AbstractCamera
origin::Vec3{T}
rotation::RotXYZ{T}
size::Tuple{T, T}
# function OrthogonalCamera(o, θ, s)
# new(o, RotXYZ(0.0, 0.0, 0.0), s)
# end
end
function get_rays(camera::OrthogonalCamera, resolution::Tuple{I, I}) where {I<:Integer}
X, Y = camera.size
rx, ry = resolution
origins = [Vec3(x, y, 0.0) + camera.origin for x = LinRange(-X, X, rx), y = LinRange(-Y, Y, ry)]
[Ray(o, Vec3(0.0, 0.0, 1.0)) for o in origins]
end
struct Ray{T<:AbstractFloat}
origin::Vec3{T}
direction::Vec3{T}
# function Ray{T}(o, d) where {T<:AbstractFloat}
# new(o, d |> normalize)
# end
end
struct HitInfo
hit::Bool
color::RGB{N0f8}
end
struct World
objects::Vector
background::RGB{N0f8}
function World(color)
new([], color)
end
end
Base.push!(w::World, x::AbstractObject) = push!(w.objects, x)
struct Sphere{T<:AbstractFloat} <: AbstractObject
origin::Vec3{T}
radius::T
color::RGB{N0f8}
end
function check_hit(ray::Ray{T}, sphere::Sphere{T})::Tuple{Bool, T} where {T <: AbstractFloat}
p = ray.origin - sphere.origin
a = dot(ray.direction, ray.direction)
b = 2 * dot(p, ray.direction)
c = dot(p, p) - sphere.radius^2
Δ = b^2 - 4a*c
Δ < 0.0 && return false, -1.0
x, z = -b/(2a), Δ/(2a)
t = x - z
t = t < eps() ? x + z : t
return t < eps() ? (false, -1.0) : (true, t)
end
function trace_ray(world::World, ray::Ray)::HitInfo
mindist = Float64 |> typemax |> prevfloat
hit = false
color = world.background
for obj in world.objects
hit, dist = check_hit(ray, obj)
if hit && dist < mindist
mindist = dist
hit = true
color = obj.color
end
end
return HitInfo(hit, color)
end
end