SBWO/SimpleRayTracer.jl
2024-01-23 22:51:29 +01:00

129 lines
3.1 KiB
Julia
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

module SimpleRayTracer
using LinearAlgebra
using StaticArrays
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}
size::Tuple{T, T}
rotation::RotXYZ{T}
end
function get_rays(camera::OrthogonalCamera, resolution::Tuple{I, I}) where {I<:Integer}
R = camera.rotation
X, Y = camera.size
Nx, Ny = resolution
screen_pixel_position = [R*Vec3(x, y, 0.0) + camera.origin for y = LinRange(Y/2, -Y/2, Ny), x = LinRange(-X/2, X/2, Nx)]
[Ray(p, R*Vec3(0.0, 0.0, 1.0)) for p in screen_pixel_position]
end
struct PinHoleCamera{T<:AbstractFloat} <: AbstractCamera
origin::Vec3{T}
distance::T
base::SMatrix{3, 3, T}
size::Tuple{T, T}
end
function PinHoleCamera(origin, lookAt, up, distance, size)
w = origin - lookAt |> normalize
u = up × w |> normalize
v = w × u
B = [u v w]
PinHoleCamera(origin, distance, B, size)
end
function get_rays(camera::PinHoleCamera, resolution::Tuple{I, I}) where {I<:Integer}
origin = camera.origin
d = camera.distance
X, Y = camera.size
B = camera.base
Nx, Ny = resolution
[Ray(origin, B*Vec3(x, y, -d)) for y = LinRange(Y/2, -Y/2, Ny), x = LinRange(X/2, -X/2, Nx)]
end
struct Ray{T<:AbstractFloat}
origin::Vec3{T}
direction::Vec3{T}
function Ray(o::Vec3{T}, d::Vec3{T}) where {T<:AbstractFloat}
new{T}(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 = ray.direction ray.direction
b = 2 * (p ray.direction)
c = 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
struct Plane{T<:AbstractFloat} <: AbstractObject
point::Vec3{T}
normal::Vec3{T}
color::RGB
end
function check_hit(ray::Ray{T}, plane::Plane{T})::Tuple{Bool, T} where {T <: AbstractFloat}
t = (plane.point - ray.origin) plane.normal / (ray.direction plane.normal)
return t > eps() ? (true, t) : (false, -1.0)
end
function trace_ray(world::World, ray::Ray)::HitInfo
maxdist = Float64 |> typemax |> prevfloat
hit = false
color = world.background
for obj in world.objects
hit, dist = check_hit(ray, obj)
if hit && dist < maxdist
maxdist = dist
hit = true
color = obj.color
end
end
return HitInfo(hit, color)
end
function render(world, camera, resolution)
rays = get_rays(camera, resolution)
[trace_ray(world, ray).color for ray in rays];
end
end