Skip to content

Commit aeaa63c

Browse files
committed
a few improvements
1 parent faf5ad1 commit aeaa63c

File tree

5 files changed

+106
-280
lines changed

5 files changed

+106
-280
lines changed

docs/code/TraceMakie.jl

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ function extract_material(plot::Plot, color_obs::Observable)
4242
return
4343
end
4444
elseif color isa Colorant || color isa Union{String,Symbol}
45-
tex = Trace.ConstantTexture(to_spectrum(color))
45+
tex = Trace.ConstantTexture(to_spectrum(to_color(color)))
4646
elseif color isa Nothing
4747
# ignore!
4848
nothing
@@ -193,9 +193,6 @@ function render_gpu(mscene::Makie.Scene, ArrayType; samples_per_pixel=8, max_dep
193193
return Array(gpu_img)
194194
end
195195

196-
function render(w::Whitten5, scene)
197-
198-
end
199196

200197
function render_interactive(mscene::Makie.Scene, ArrayType; max_depth=5)
201198
scene, camera, film = convert_scene(mscene)

src/film.jl

Lines changed: 86 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,45 @@ struct Pixel
44
splat_xyz::Point3f
55
end
66
Pixel() = Pixel(Point3f(0.0f0), 0.0f0, Point3f(0.0f0))
7+
8+
9+
function filter_offset(x, discrete_point, inv_filter_radius, filter_table_width)
10+
fx = abs((x - discrete_point) * inv_filter_radius * filter_table_width)
11+
return clamp(u_int32(ceil(fx)), Int32(1), Int32(filter_table_width)) # TODO is clipping ok?
12+
end
13+
14+
15+
function generate_filter_table(filter)
16+
filter_table_width = 16
17+
filter_table = Matrix{Float32}(undef, filter_table_width, filter_table_width)
18+
r = filter.radius ./ filter_table_width
19+
for y in 0:filter_table_width-1, x in 0:filter_table_width-1
20+
p = Point2f((x + 0.5f0) * r[1], (y + 0.5f0) * r[2])
21+
filter_table[y+1, x+1] = filter(p)
22+
end
23+
24+
point = Point2f(filter_table_width)
25+
# Compute sample's raster bounds.
26+
discrete_point = point .- 0.5f0
27+
# Compute sample radius around point
28+
p0 = ceil.(Int, discrete_point .- filter.radius)
29+
p1 = floor.(Int, discrete_point .+ filter.radius) .+ 1
30+
# Make sure we're inbounds
31+
inv_radius = 1.0f0 ./ filter.radius
32+
# Precompute x & y filter offsets.
33+
offsets_x = filter_offsets(p0[1], p1[1], discrete_point[1], inv_radius[1], filter_table_width)
34+
offsets_y = filter_offsets(p0[2], p1[2], discrete_point[2], inv_radius[2], filter_table_width)
35+
# Loop over filter support & add sample to pixel array.
36+
xrange = p0[1]:p1[1]
37+
yrange = p0[2]:p1[2]
38+
weights = zeros(Float32, length(xrange), length(yrange))
39+
for i in 1:length(xrange), j in 1:length(yrange)
40+
w = filter_table[offsets_y[j], offsets_x[i]]
41+
weights[i, j] = w
42+
end
43+
return weights
44+
end
45+
746
struct Film{Pixels<:AbstractMatrix{Pixel}}
847
resolution::Point2f
948
"""
@@ -92,153 +131,68 @@ struct FilmTilePixel{S<:Spectrum}
92131
end
93132
FilmTilePixel() = FilmTilePixel(RGBSpectrum(), 0f0)
94133

95-
struct FilmTile{Pixels<:AbstractMatrix{<:FilmTilePixel}}
96-
"""
97-
Bounds should start from 1 not 0.
98-
"""
99-
bounds::Bounds2
100-
filter_radius::Point2f
101-
inv_filter_radius::Point2f
102-
filter_table_width::Int32
103-
pixels::Pixels
104-
end
105134

106-
function FilmTile(
107-
bounds::Bounds2, filter_radius::Point2f,
108-
filter_table_width::Int32,
109-
)
110-
# Include some padding for over rounding (since we re-use the tiles)
111-
tile_res = (Int32.(inclusive_sides(bounds))) .+ 2
112-
contrib_sum = fill(RGBSpectrum(), tile_res[2], tile_res[1])
113-
filter_weight_sum = fill(0.0f0, tile_res[2], tile_res[1])
114-
pixels = StructArray{FilmTilePixel{RGBSpectrum}}((contrib_sum, filter_weight_sum))
115-
FilmTile{typeof(pixels)}(
116-
bounds, filter_radius, 1.0f0 ./ filter_radius,
117-
filter_table_width,
118-
pixels,
119-
)
120-
end
121135

122136
"""
123-
Bounds should start from 1 not 0.
137+
Point in (x, y) format.
124138
"""
125-
function FilmTile(f::Film, sample_bounds::Bounds2, radius)
126-
p0 = ceil.(sample_bounds.p_min .- 0.5f0 .- radius)
127-
p1 = floor.(sample_bounds.p_max .- 0.5f0 .+ radius) .+ 1f0
128-
tile_bounds = Bounds2(p0, p1) f.crop_bounds
129-
FilmTile(tile_bounds, radius, f.filter_table_width)
130-
end
131-
132-
function reset!(tile::FilmTile)
133-
134-
tile.pixels.contrib_sum .= (RGBSpectrum(0f0),)
135-
tile.pixels.filter_weight_sum .= 0f0
136-
end
137-
138-
function update_bounds!(f::Film, tile::FilmTile, sample_bounds::Bounds2)
139-
reset!(tile)
140-
radius = tile.filter_radius
141-
p0 = ceil.(sample_bounds.p_min .- 0.5f0 .- radius)
142-
p1 = floor.(sample_bounds.p_max .- 0.5f0 .+ radius) .+ 1.0f0
143-
bounds = Bounds2(p0, p1) f.crop_bounds
144-
tile_res = (Int32.(inclusive_sides(bounds)))
145-
@assert all(reverse(tile_res) .<= size(tile.pixels)) "$(reverse(tile_res)) != $(size(tile.pixels)) $(sample_bounds)"
146-
FilmTile(bounds, radius, tile.inv_filter_radius, tile.filter_table_width, tile.pixels)
147-
end
148-
149-
function filter_offset!(offsets, start, stop, discrete_point, inv_filter_radius, filter_table_width)
150-
@inbounds for (i, x) in enumerate(Int(start):Int(stop))
151-
fx = abs((x - discrete_point) * inv_filter_radius * filter_table_width)
152-
offsets[i] = clamp(ceil(fx), 1, filter_table_width) # TODO is clipping ok?
139+
@inline function get_pixel_index(crop_bounds, p::Point2)
140+
i1, i2 = u_int32.((p .- crop_bounds.p_min .+ 1.0f0))
141+
return CartesianIndex(i1, i2)
142+
end
143+
144+
@inline function merge_film_tile!(f::AbstractMatrix{Pixel}, crop_bounds::Bounds2, ft::AbstractMatrix{FilmTilePixel}, tile::Bounds2, tile_col::Int32)
145+
ft_contrib_sum = ft.contrib_sum
146+
ft_filter_weight_sum = ft.filter_weight_sum
147+
f_xyz = f.xyz
148+
f_filter_weight_sum = f.filter_weight_sum
149+
linear = Int32(1)
150+
@inbounds for pixel in tile
151+
f_idx = get_pixel_index(crop_bounds, pixel)
152+
f_xyz[f_idx] += to_XYZ(ft_contrib_sum[linear, tile_col])
153+
f_filter_weight_sum[f_idx] += ft_filter_weight_sum[linear, tile_col]
154+
linear += Int32(1)
153155
end
156+
return
154157
end
155158

156-
@inline function filter_offset(x, discrete_point, inv_filter_radius, filter_table_width)
157-
fx = abs((x - discrete_point) * inv_filter_radius * filter_table_width)
158-
return clamp(u_int32(ceil(fx)), Int32(1), Int32(filter_table_width)) # TODO is clipping ok?
159-
end
160-
161-
function filter_offsets(start, stop, discrete_point, inv_filter_radius, filter_table_width)::NTuple{8, Int32}
162-
range = Int32(start):Int32(stop)
163-
return ntuple(8) do i
164-
if i <= length(range)
165-
filter_offset(range[i], discrete_point, inv_filter_radius, filter_table_width)::Int32
166-
else
167-
Int32(0)
168-
end
169-
end
159+
@inline function get_tile_index(bounds::Bounds2, p::Point2)
160+
j, i = u_int32.((p .- bounds.p_min .+ 1.0f0))
161+
ncols = u_int32(inclusive_sides(bounds)[1])
162+
return (i - Int32(1)) * ncols + j
170163
end
171164

172-
"""
173-
Add sample contribution to the film tile.
174-
175-
- `point::Point2f`:
176-
should start from 1 not 0.
177-
And is relative to the film, not the film tile.
178-
"""
179-
function add_sample!(
180-
film::Film, t::FilmTile, point::Point2f, spectrum::S,
181-
sample_weight::Float32 = 1f0,
182-
) where S<:Spectrum
183-
165+
@inline function add_sample!(
166+
tiles::AbstractMatrix{FilmTilePixel}, tile::Bounds2, tile_column::Int32, point::Point2f, spectrum::RGBSpectrum,
167+
filter_table, filter_radius::Point2f, sample_weight::Float32=1.0f0,
168+
)
184169
# Compute sample's raster bounds.
185170
discrete_point = point .- 0.5f0
186-
p0 = ceil.(Int32, discrete_point .- t.filter_radius)
187-
p1 = floor.(Int32, discrete_point .+ t.filter_radius) .+ Int32(1)
188-
p0 = Int32.(max.(p0, max.(t.bounds.p_min, Point2{Int32}(1))))
189-
p1 = Int32.(min.(p1, t.bounds.p_max))
190-
# Precompute x & y filter offsets.
191-
offsets_x = filter_offsets(p0[1], p1[1], discrete_point[1], t.inv_filter_radius[1], t.filter_table_width)
192-
offsets_y = filter_offsets(p0[2], p1[2], discrete_point[2], t.inv_filter_radius[2], t.filter_table_width)
171+
# Compute sample radius around point
172+
p0 = u_int32.(ceil.(discrete_point .- filter_radius))
173+
p1 = u_int32.(floor.(discrete_point .+ filter_radius)) .+ Int32(1)
174+
# Make sure we're inbounds
175+
pmin = u_int32.(tile.p_min)
176+
pmax = u_int32.(tile.p_max)
177+
p0 = max.(p0, max.(pmin, Point2{Int32}(1)))::Point2{Int32}
178+
p1 = min.(p1, pmax)::Point2{Int32}
193179
# Loop over filter support & add sample to pixel array.
194-
pixels = t.pixels
195-
contrib_sum = pixels.contrib_sum
196-
filter_weight_sum = pixels.filter_weight_sum
197-
filter_table = film.filter_table
198-
@inbounds for (j, y) in enumerate(Int(p0[2]):Int(p1[2])), (i, x) in enumerate(Int(p0[1]):Int(p1[1]))
199-
w = filter_table[offsets_y[j], offsets_x[i]]
200-
@real_assert sample_weight <= 1
201-
@real_assert w <= 1
202-
idx = get_pixel_index(t, Point2(x, y))
203-
contrib_sum[idx] += spectrum * sample_weight * w
204-
filter_weight_sum[idx] += w
180+
contrib_sum = tiles.contrib_sum
181+
filter_weight_sum = tiles.filter_weight_sum
182+
xrange = p0[1]:p1[1]
183+
yrange = p0[2]:p1[2]
184+
xn = length(xrange) % Int32
185+
yn = length(yrange) % Int32
186+
@inbounds for i in Int32(1):xn, j in Int32(1):yn
187+
x = xrange[i]
188+
y = yrange[j]
189+
w = filter_table[i, j]
190+
idx = get_tile_index(tile, Point2(x, y))
191+
contrib_sum[idx, tile_column] += spectrum * sample_weight * w
192+
filter_weight_sum[idx, tile_column] += w
205193
end
206194
end
207195

208-
"""
209-
Point in (x, y) format.
210-
"""
211-
@inline function get_pixel_index(t::FilmTile, p::Point2)
212-
i1, i2 = u_int32.((p .- t.bounds.p_min .+ 1f0))
213-
return CartesianIndex(i2, i1)
214-
end
215-
216-
"""
217-
Point in (x, y) format.
218-
"""
219-
@inline function get_pixel_index(f::Film, p::Point2)
220-
i1, i2 = u_int32.((p .- f.crop_bounds.p_min .+ 1f0))
221-
return CartesianIndex(i2, i1)
222-
end
223-
224-
function merge_film_tile!(f::Film, ft::FilmTile)
225-
x_range = Int(ft.bounds.p_min[1]):Int(ft.bounds.p_max[1])
226-
y_range = Int(ft.bounds.p_min[2]):Int(ft.bounds.p_max[2])
227-
ft_contrib_sum = ft.pixels.contrib_sum
228-
ft_filter_weight_sum = ft.pixels.filter_weight_sum
229-
f_xyz = f.pixels.xyz
230-
f_filter_weight_sum = f.pixels.filter_weight_sum
231-
232-
@inbounds for y in y_range, x in x_range
233-
pixel = Point2{Int32}(x, y)
234-
ft_idx = get_pixel_index(ft, pixel)
235-
f_idx = get_pixel_index(f, pixel)
236-
f_xyz[f_idx] += to_XYZ(ft_contrib_sum[ft_idx])
237-
f_filter_weight_sum[f_idx] += ft_filter_weight_sum[ft_idx]
238-
end
239-
end
240-
241-
242196
function set_image!(f::Film, spectrum::Matrix{S}) where {S<:Spectrum}
243197
@real_assert size(f.pixels) == size(spectrum)
244198
f.pixels.xyz .= to_XYZ.(spectrum)

src/integrators/sampler.jl

Lines changed: 10 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,9 @@ function li(
155155
end
156156

157157
@inline function specular_reflect(
158-
bsdf, sampler, max_depth, ray::RayDifferentials,
159-
si::SurfaceInteraction, scene::Scene, depth::Int32,
160-
)
158+
bsdf, sampler, max_depth, ray::RayDifferentials,
159+
si::SurfaceInteraction, scene::Scene, depth::Int32,
160+
)
161161

162162
# Compute specular reflection direction `wi` and BSDF value.
163163
wo = si.core.wo
@@ -408,29 +408,15 @@ end
408408
end
409409

410410

411-
struct Tile
412-
tile_indx::NTuple{2, Int32}
413-
width::Int32
411+
@inline function trace_pixel(camera, scene, pixel, sampler, max_depth)
412+
camera_sample = get_camera_sample(sampler, pixel)
413+
ray, ω = generate_ray_differential(camera, camera_sample)
414+
if ω > 0.0f0
415+
return li_iterative(sampler, max_depth, ray, scene)
416+
end
417+
return RGBSpectrum(0.0f0)
414418
end
415419

416-
# function sample_kernel_inner(i::A, scene::B, t_sampler::C, film::D, film_tile::E, camera::F, pixel::G, spp_sqr::H) where {A,B,C,D,E,F,G,H}
417-
# for _ in 1:t_sampler.samples_per_pixel
418-
# camera_sample = get_camera_sample(t_sampler, pixel)
419-
# ray, ω = generate_ray_differential(camera, camera_sample)
420-
# ray = scale_differentials(ray, spp_sqr)
421-
# l = RGBSpectrum(0.0f0)
422-
# if ω > 0.0f0
423-
# # l = li(t_sampler, Int32(i.max_depth), ray, scene, Int32(1))
424-
# l = li_iterative(t_sampler, Int32(i.max_depth), ray, scene)
425-
# end
426-
# # TODO check l for invalid values
427-
# if isnan(l)
428-
# l = RGBSpectrum(0.0f0)
429-
# end
430-
# add_sample!(film, film_tile, camera_sample.film, l, ω)
431-
# end
432-
# end
433-
434420
@noinline function sample_tile(sampler, camera, scene, film, film_tile, tile_bounds, max_depth)
435421
spp_sqr = 1.0f0 / Float32(sampler.samples_per_pixel)
436422
for pixel in tile_bounds

src/kernel-abstractions.jl

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -63,29 +63,15 @@ function to_gpu(ArrayType, scene::Trace.Scene; preserve=[])
6363
return Trace.Scene(scene.lights, bvh, scene.bound)
6464
end
6565

66-
67-
@inline function trace_pixel(camera, scene, pixel, samples_per_pixel, max_depth, l)
68-
s = UniformSampler(samples_per_pixel)
69-
camera_sample = get_camera_sample(s, pixel)
70-
ray, ω = generate_ray_differential(camera, camera_sample)
71-
if ω > 0.0f0
72-
l += li_iterative(s, max_depth, ray, scene)
73-
end
74-
return l
75-
end
76-
77-
@kernel function ka_trace_image!(img, camera, scene, samples_per_pixel, max_depth, niter)
66+
@kernel function ka_trace_image!(img, camera, scene, sampler, max_depth)
7867
_idx = @index(Global)
7968
idx = _idx % Int32
8069
@inbounds if checkbounds(Bool, img, idx)
8170
cols = size(img, 2) % Int32
8271
row = (idx - Int32(1)) ÷ cols + Int32(1)
8372
col = (idx - Int32(1)) % cols + Int32(1)
84-
rgb = img[idx]
85-
ninv = 0.5f0
86-
l = RGBSpectrum(ninv * rgb.r, ninv * rgb.g, ninv * rgb.b)
8773
pixel = Point2f((row, cols - col))
88-
l = trace_pixel(camera, scene, pixel, samples_per_pixel, max_depth, l)
74+
l = trace_pixel(camera, scene, pixel, sampler, max_depth)
8975
img[idx] = RGB{Float32}(( l.c)...)
9076
end
9177
nothing
@@ -94,7 +80,8 @@ end
9480
function launch_trace_image!(img, camera, scene, samples_per_pixel::Int32, max_depth::Int32, niter::Int32)
9581
backend = KA.get_backend(img)
9682
kernel! = ka_trace_image!(backend)
97-
kernel!(img, camera, scene, samples_per_pixel, max_depth, niter, ndrange=size(img), workgroupsize=(16, 16))
83+
sampler = UniformSampler(samples_per_pixel)
84+
kernel!(img, camera, scene, sampler, max_depth, niter, ndrange=size(img), workgroupsize=(16, 16))
9885
KA.synchronize(backend)
9986
return img
10087
end

0 commit comments

Comments
 (0)