89 lines
2 KiB
Julia
89 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
|