Skip to content

Commit 4baf102

Browse files
committed
Extract material, vec
1 parent 8afb894 commit 4baf102

File tree

3 files changed

+75
-66
lines changed

3 files changed

+75
-66
lines changed

src/RayTracingWeekend.jl

Lines changed: 2 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,7 @@ export Camera, Dielectric, Hittable, HittableList, HitRecord, Lambertian, Materi
1313
export scene_2_spheres, scene_4_spheres, scene_blue_red_spheres, scene_diel_spheres, scene_random_spheres
1414
export TRNG
1515

16-
const Vec3{T<:AbstractFloat} = SVector{3, T}
17-
18-
# Adapted from @Christ_Foster's: https://discourse.julialang.org/t/ray-tracing-in-a-week-end-julia-vs-simd-optimized-c/72958/40
19-
@inline @inbounds function Base.getproperty(v::SVector{3}, name::Symbol)
20-
name === :x || name === :r ? getfield(v, :data)[1] :
21-
name === :y || name === :g ? getfield(v, :data)[2] :
22-
name === :z || name === :b ? getfield(v, :data)[3] : getfield(v, name)
23-
end
24-
25-
@inline @inbounds function Base.getproperty(v::SVector{2}, name::Symbol)
26-
name === :x || name === :r ? getfield(v, :data)[1] :
27-
name === :y || name === :g ? getfield(v, :data)[2] : getfield(v, name)
28-
end
29-
30-
@inline squared_length(v::Vec3) = v v
31-
@inline near_zero(v::Vec3) = squared_length(v) < 1e-5
32-
@inline rgb(v::Vec3) = RGB(v...)
33-
@inline rgb_gamma2(v::Vec3) = RGB(sqrt.(v)...)
16+
include("vec.jl")
3417

3518
struct Ray{T}
3619
origin::Vec3{T} # Point
@@ -105,33 +88,11 @@ struct Scatter{T<: AbstractFloat}
10588
@inline Scatter(r::Ray{T},a::Vec3{T},reflected=true) where T = new{T}(r,a,reflected)
10689
end
10790

108-
struct Lambertian{T} <: Material{T}
109-
albedo::Vec3{T}
110-
end
111-
11291
"""Compute reflection vector for v (pointing to surface) and normal n⃗.
11392
11493
See [diagram](https://raytracing.github.io/books/RayTracingInOneWeekend.html#metal/mirroredlightreflection)"""
11594
@inline @fastmath reflect(v::Vec3{T}, n⃗::Vec3{T}) where T = v - (2vn⃗)*n⃗
11695

117-
"""Create a scattered ray emitted by `mat` from incident Ray `r`.
118-
119-
Args:
120-
rec: the HitRecord of the surface from which to scatter the ray.
121-
122-
Return `nothing`` if it's fully absorbed. """
123-
@inline @fastmath function scatter(mat::Lambertian{T}, r::Ray{T}, rec::HitRecord{T})::Scatter{T} where T
124-
scatter_dir = rec.n⃗ + random_vec3_on_sphere(T)
125-
if near_zero(scatter_dir) # Catch degenerate scatter direction
126-
scatter_dir = rec.n⃗
127-
else
128-
scatter_dir = normalize(scatter_dir)
129-
end
130-
scattered_r = Ray{T}(rec.p, scatter_dir)
131-
attenuation = mat.albedo
132-
return Scatter(scattered_r, attenuation)
133-
end
134-
13596
@inline @fastmath function hit(s::Sphere{T}, r::Ray{T}, tmin::T, tmax::T)::Union{HitRecord,Nothing} where T
13697
oc = r.origin - s.center
13798
#a = r.dir ⋅ r.dir # unnecessary since `r.dir` is normalized
@@ -176,15 +137,6 @@ end
176137

177138
@inline color_vec3_in_rgb(v::Vec3{T}) where T = 0.5normalize(v) + SA{T}[0.5,0.5,0.5]
178139

179-
struct Metal{T} <: Material{T}
180-
albedo::Vec3{T}
181-
fuzz::T # how big the sphere used to generate fuzzy reflection rays. 0=none
182-
@inline Metal(a::Vec3{T}, f::T=0.0) where T = new{T}(a,f)
183-
end
184-
@inline @fastmath function scatter(mat::Metal{T}, r_in::Ray{T}, rec::HitRecord)::Scatter{T} where T
185-
reflected = normalize(reflect(r_in.dir, rec.n⃗) + mat.fuzz*random_vec3_on_sphere(T))
186-
Scatter(Ray(rec.p, reflected), mat.albedo)
187-
end
188140

189141
"""Compute color for a ray, recursively
190142
@@ -274,10 +226,6 @@ end
274226
normalize(r_out_perp + r_out_parallel)
275227
end
276228

277-
struct Dielectric{T} <: Material{T}
278-
ir::T # index of refraction, i.e. η.
279-
end
280-
281229
@inline @fastmath function reflectance(cosθ, refraction_ratio)
282230
# Use Schlick's approximation for reflectance.
283231
# claforte: may be buggy? I'm getting black pixels in the Hollow Glass Sphere...
@@ -286,19 +234,7 @@ end
286234
r0 + (1-r0)*((1-cosθ)^5)
287235
end
288236

289-
@inline @fastmath function scatter(mat::Dielectric{T}, r_in::Ray{T}, rec::HitRecord{T}) where T
290-
attenuation = SA{T}[1,1,1]
291-
refraction_ratio = rec.front_face ? (one(T)/mat.ir) : mat.ir # i.e. ηᵢ/ηₜ
292-
cosθ = min(-r_in.dirrec.n⃗, one(T))
293-
sinθ = (one(T) - cosθ^2)
294-
cannot_refract = refraction_ratio * sinθ > one(T)
295-
if cannot_refract || reflectance(cosθ, refraction_ratio) > trand(T)
296-
dir = reflect(r_in.dir, rec.n⃗)
297-
else
298-
dir = refract(r_in.dir, rec.n⃗, refraction_ratio)
299-
end
300-
Scatter(Ray{T}(rec.p, dir), attenuation) # TODO: rename reflected -> !absorbed?
301-
end
237+
include("material.jl")
302238

303239
include("scenes.jl")
304240

src/material.jl

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
struct Lambertian{T} <: Material{T}
2+
albedo::Vec3{T}
3+
end
4+
5+
"""Create a scattered ray emitted by `mat` from incident Ray `r`.
6+
7+
Args:
8+
rec: the HitRecord of the surface from which to scatter the ray.
9+
10+
Return `nothing`` if it's fully absorbed. """
11+
@inline @fastmath function scatter(mat::Lambertian{T}, r::Ray{T}, rec::HitRecord{T})::Scatter{T} where T
12+
scatter_dir = rec.n⃗ + random_vec3_on_sphere(T)
13+
if near_zero(scatter_dir) # Catch degenerate scatter direction
14+
scatter_dir = rec.n⃗
15+
else
16+
scatter_dir = normalize(scatter_dir)
17+
end
18+
scattered_r = Ray{T}(rec.p, scatter_dir)
19+
attenuation = mat.albedo
20+
return Scatter(scattered_r, attenuation)
21+
end
22+
23+
struct Metal{T} <: Material{T}
24+
albedo::Vec3{T}
25+
fuzz::T # how big the sphere used to generate fuzzy reflection rays. 0=none
26+
@inline Metal(a::Vec3{T}, f::T=0.0) where T = new{T}(a,f)
27+
end
28+
29+
@inline @fastmath function scatter(mat::Metal{T}, r_in::Ray{T}, rec::HitRecord)::Scatter{T} where T
30+
reflected = normalize(reflect(r_in.dir, rec.n⃗) + mat.fuzz*random_vec3_on_sphere(T))
31+
Scatter(Ray(rec.p, reflected), mat.albedo)
32+
end
33+
34+
"Dielectric, i.e. transparent/refractive material"
35+
struct Dielectric{T} <: Material{T}
36+
ir::T # index of refraction, i.e. η.
37+
end
38+
39+
@inline @fastmath function scatter(mat::Dielectric{T}, r_in::Ray{T}, rec::HitRecord{T}) where T
40+
attenuation = SA{T}[1,1,1]
41+
refraction_ratio = rec.front_face ? (one(T)/mat.ir) : mat.ir # i.e. ηᵢ/ηₜ
42+
cosθ = min(-r_in.dirrec.n⃗, one(T))
43+
sinθ = (one(T) - cosθ^2)
44+
cannot_refract = refraction_ratio * sinθ > one(T)
45+
if cannot_refract || reflectance(cosθ, refraction_ratio) > trand(T)
46+
dir = reflect(r_in.dir, rec.n⃗)
47+
else
48+
dir = refract(r_in.dir, rec.n⃗, refraction_ratio)
49+
end
50+
Scatter(Ray{T}(rec.p, dir), attenuation) # TODO: rename reflected -> !absorbed?
51+
end

src/vec.jl

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Operations on 2D and 3D vectors, and colors
2+
3+
const Vec3{T<:AbstractFloat} = SVector{3, T}
4+
5+
# Adapted from @Christ_Foster's: https://discourse.julialang.org/t/ray-tracing-in-a-week-end-julia-vs-simd-optimized-c/72958/40
6+
# Christ added this feature directly in StaticArrays.jl on 2021-12-17, so
7+
# this will soon be no longer needed.
8+
@inline @inbounds function Base.getproperty(v::SVector{3}, name::Symbol)
9+
name === :x || name === :r ? getfield(v, :data)[1] :
10+
name === :y || name === :g ? getfield(v, :data)[2] :
11+
name === :z || name === :b ? getfield(v, :data)[3] : getfield(v, name)
12+
end
13+
14+
@inline @inbounds function Base.getproperty(v::SVector{2}, name::Symbol)
15+
name === :x || name === :r ? getfield(v, :data)[1] :
16+
name === :y || name === :g ? getfield(v, :data)[2] : getfield(v, name)
17+
end
18+
19+
@inline squared_length(v::Vec3) = v v
20+
@inline near_zero(v::Vec3) = squared_length(v) < 1e-5
21+
@inline rgb(v::Vec3) = RGB(v...)
22+
@inline rgb_gamma2(v::Vec3) = RGB(sqrt.(v)...)

0 commit comments

Comments
 (0)