raysetta 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +72 -0
- data/Rakefile +8 -0
- data/Steepfile +15 -0
- data/exe/raysetta +3 -0
- data/lib/raysetta/aabb.rb +116 -0
- data/lib/raysetta/background/base.rb +19 -0
- data/lib/raysetta/background/cube_map.rb +44 -0
- data/lib/raysetta/background/gradient.rb +28 -0
- data/lib/raysetta/background/solid.rb +24 -0
- data/lib/raysetta/background/sphere_map.rb +30 -0
- data/lib/raysetta/camera.rb +109 -0
- data/lib/raysetta/entity_base.rb +28 -0
- data/lib/raysetta/exe.rb +75 -0
- data/lib/raysetta/hit.rb +38 -0
- data/lib/raysetta/image.rb +46 -0
- data/lib/raysetta/interval.rb +63 -0
- data/lib/raysetta/material/base.rb +19 -0
- data/lib/raysetta/material/dielectric.rb +60 -0
- data/lib/raysetta/material/diffuse_light.rb +42 -0
- data/lib/raysetta/material/lambertian.rb +48 -0
- data/lib/raysetta/material/metal.rb +49 -0
- data/lib/raysetta/object/base.rb +23 -0
- data/lib/raysetta/object/box.rb +50 -0
- data/lib/raysetta/object/bvh.rb +81 -0
- data/lib/raysetta/object/group.rb +59 -0
- data/lib/raysetta/object/moving_sphere.rb +41 -0
- data/lib/raysetta/object/quad.rb +88 -0
- data/lib/raysetta/object/sphere.rb +68 -0
- data/lib/raysetta/object/tri.rb +46 -0
- data/lib/raysetta/output/base.rb +23 -0
- data/lib/raysetta/output/png.rb +26 -0
- data/lib/raysetta/output/ppm.rb +19 -0
- data/lib/raysetta/perlin.rb +91 -0
- data/lib/raysetta/ray.rb +28 -0
- data/lib/raysetta/runner/base.rb +28 -0
- data/lib/raysetta/runner/concurrent.rb +17 -0
- data/lib/raysetta/runner/processes.rb +26 -0
- data/lib/raysetta/runner/ractors.rb +32 -0
- data/lib/raysetta/runner/sync.rb +28 -0
- data/lib/raysetta/runner/threads.rb +26 -0
- data/lib/raysetta/runner.rb +45 -0
- data/lib/raysetta/scatter.rb +12 -0
- data/lib/raysetta/scene.rb +205 -0
- data/lib/raysetta/texture/base.rb +19 -0
- data/lib/raysetta/texture/checker.rb +55 -0
- data/lib/raysetta/texture/image.rb +57 -0
- data/lib/raysetta/texture/noise.rb +46 -0
- data/lib/raysetta/texture/solid_color.rb +36 -0
- data/lib/raysetta/tracer.rb +43 -0
- data/lib/raysetta/util.rb +38 -0
- data/lib/raysetta/vec2.rb +125 -0
- data/lib/raysetta/vec3.rb +214 -0
- data/lib/raysetta/version.rb +5 -0
- data/lib/raysetta.rb +44 -0
- data/mise.toml +2 -0
- data/scenes/box.json +26 -0
- data/scenes/cornell.json +108 -0
- data/scenes/cubemap.json +109 -0
- data/scenes/dumb.json +58 -0
- data/scenes/earth.json +79 -0
- data/scenes/example.json +11712 -0
- data/scenes/perlin.json +2126 -0
- data/scenes/quads.json +38 -0
- data/scenes/simple_light.json +2126 -0
- data/scenes/testcube.json +113 -0
- data/sig/raysetta/aabb.rbs +39 -0
- data/sig/raysetta/background/base.rbs +11 -0
- data/sig/raysetta/background/cube_map.rbs +9 -0
- data/sig/raysetta/background/gradient.rbs +10 -0
- data/sig/raysetta/background/solid.rbs +9 -0
- data/sig/raysetta/background/sphere_map.rbs +9 -0
- data/sig/raysetta/camera.rbs +41 -0
- data/sig/raysetta/entity_base.rbs +12 -0
- data/sig/raysetta/hit.rbs +21 -0
- data/sig/raysetta/image.rbs +13 -0
- data/sig/raysetta/interval.rbs +36 -0
- data/sig/raysetta/material/base.rbs +11 -0
- data/sig/raysetta/material/dielectric.rbs +15 -0
- data/sig/raysetta/material/diffuse_light.rbs +12 -0
- data/sig/raysetta/material/lambertian.rbs +10 -0
- data/sig/raysetta/material/metal.rbs +11 -0
- data/sig/raysetta/object/base.rbs +15 -0
- data/sig/raysetta/object/box.rbs +13 -0
- data/sig/raysetta/object/bvh.rbs +23 -0
- data/sig/raysetta/object/group.rbs +16 -0
- data/sig/raysetta/object/moving_sphere.rbs +15 -0
- data/sig/raysetta/object/quad.rbs +26 -0
- data/sig/raysetta/object/sphere.rbs +14 -0
- data/sig/raysetta/object/tri.rbs +15 -0
- data/sig/raysetta/output/base.rbs +15 -0
- data/sig/raysetta/output/png.rbs +8 -0
- data/sig/raysetta/output/ppm.rbs +7 -0
- data/sig/raysetta/perlin.rbs +19 -0
- data/sig/raysetta/ray.rbs +18 -0
- data/sig/raysetta/runner/base.rbs +15 -0
- data/sig/raysetta/runner/concurrent.rbs +10 -0
- data/sig/raysetta/runner/processes.rbs +9 -0
- data/sig/raysetta/runner/ractors.rbs +11 -0
- data/sig/raysetta/runner/sync.rbs +11 -0
- data/sig/raysetta/runner/threads.rbs +9 -0
- data/sig/raysetta/runner.rbs +5 -0
- data/sig/raysetta/scatter.rbs +8 -0
- data/sig/raysetta/scene.rbs +69 -0
- data/sig/raysetta/texture/base.rbs +11 -0
- data/sig/raysetta/texture/checker.rbs +15 -0
- data/sig/raysetta/texture/image.rbs +11 -0
- data/sig/raysetta/texture/noise.rbs +13 -0
- data/sig/raysetta/texture/solid_color.rbs +10 -0
- data/sig/raysetta/tracer.rbs +16 -0
- data/sig/raysetta/util.rbs +15 -0
- data/sig/raysetta/vec2.rbs +48 -0
- data/sig/raysetta/vec3.rbs +67 -0
- data/sig/raysetta.rbs +6 -0
- data/sig/stdlib/chunky_png.rbs +24 -0
- data/sig/stdlib/etc.rbs +3 -0
- data/sig/stdlib/json.rbs +3 -0
- data/sig/stdlib/optparse.rbs +4 -0
- data/sig/stdlib/parallel.rbs +5 -0
- data/sig/stdlib/progress.rbs +5 -0
- metadata +206 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Raysetta
|
|
4
|
+
module Texture
|
|
5
|
+
class Checker < Base
|
|
6
|
+
attr_accessor :inv_scale, :even, :odd
|
|
7
|
+
|
|
8
|
+
def initialize(scale, even, odd)
|
|
9
|
+
self.scale = scale
|
|
10
|
+
@even = even
|
|
11
|
+
@odd = odd
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.solid(scale, even, odd)
|
|
15
|
+
new(scale, SolidColor.new(even), SolidColor.new(odd))
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def scale=(scale)
|
|
19
|
+
@inv_scale = 1.0 / scale
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def scale
|
|
23
|
+
1.0 / @inv_scale
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def sample(uv, point)
|
|
27
|
+
x = (inv_scale * point.x).floor
|
|
28
|
+
y = (inv_scale * point.y).floor
|
|
29
|
+
z = (inv_scale * point.z).floor
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
(x + y + z).even? ? even.sample(uv, point) : odd.sample(uv, point)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def ==(tex)
|
|
36
|
+
inv_scale == tex.inv_scale &&
|
|
37
|
+
even == tex.even &&
|
|
38
|
+
odd == tex.odd
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def hash
|
|
42
|
+
[inv_scale, even, odd].hash
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def export
|
|
46
|
+
{
|
|
47
|
+
**super,
|
|
48
|
+
scale: scale,
|
|
49
|
+
even: even.export,
|
|
50
|
+
odd: odd.export
|
|
51
|
+
}
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Raysetta
|
|
4
|
+
module Texture
|
|
5
|
+
class Image < Base
|
|
6
|
+
attr_accessor :image
|
|
7
|
+
|
|
8
|
+
def initialize(image)
|
|
9
|
+
@image = image
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def sample(uv, point)
|
|
13
|
+
# Clamp input texture coordinates to [0,1] x [1,0]
|
|
14
|
+
u = uv.u.clamp(0.0, 1.0)
|
|
15
|
+
v = 1.0 - uv.v.clamp(0.0, 1.0)
|
|
16
|
+
|
|
17
|
+
x = u*image.width + 0.5
|
|
18
|
+
y = v*image.height + 0.5
|
|
19
|
+
|
|
20
|
+
x1 = x.floor
|
|
21
|
+
y1 = y.floor
|
|
22
|
+
|
|
23
|
+
x2 = x.ceil
|
|
24
|
+
y2 = y.ceil
|
|
25
|
+
|
|
26
|
+
x2 = x1+1 if x2 == x1
|
|
27
|
+
y2 = y1+1 if y2 == y1
|
|
28
|
+
|
|
29
|
+
q11 = image[x1.to_i, y1.to_i]
|
|
30
|
+
q12 = image[x1.to_i, y2.to_i]
|
|
31
|
+
q21 = image[x2.to_i, y1.to_i]
|
|
32
|
+
q22 = image[x2.to_i, y2.to_i]
|
|
33
|
+
|
|
34
|
+
(q11*(x2-x)*(y2-y)+q21*(x-x1)*(y2-y)+q12*(x2-x)*(y-y1)+q22*(x-x1)*(y-y1)) / ((x2-x1)*(y2-y1)).to_f
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def ==(tex)
|
|
38
|
+
image == tex.image
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def hash
|
|
42
|
+
[type, image].hash
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def export
|
|
46
|
+
{
|
|
47
|
+
**super,
|
|
48
|
+
image: image.id
|
|
49
|
+
}
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def images
|
|
53
|
+
[image]
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Raysetta
|
|
4
|
+
module Texture
|
|
5
|
+
class Noise < Base
|
|
6
|
+
attr_accessor :scale, :depth, :marble_axis
|
|
7
|
+
|
|
8
|
+
def initialize(scale, depth, marble_axis, noise=Perlin.new)
|
|
9
|
+
@scale = scale
|
|
10
|
+
@depth = depth
|
|
11
|
+
@marble_axis = marble_axis
|
|
12
|
+
@noise = noise
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def sample(uv, point)
|
|
16
|
+
if marble_axis
|
|
17
|
+
Vec3.new(0.5) * (1.0 + Math.sin(scale * point.send(marble_axis) + 10.0 * @noise.turb(point, depth)))
|
|
18
|
+
else
|
|
19
|
+
Vec3.new(1.0) * @noise.turb(point, depth)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def ==(tex)
|
|
24
|
+
scale == tex.scale &&
|
|
25
|
+
depth == tex.depth &&
|
|
26
|
+
marble_axis == tex.marble_axis &&
|
|
27
|
+
noise == tex.noise
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def hash
|
|
31
|
+
[scale, depth, marble_axis, noise].hash
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def export
|
|
35
|
+
{ **super, scale:, depth:, marble_axis: marble_axis.to_s, noise: @noise.id }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def noises
|
|
39
|
+
[@noise]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
protected
|
|
43
|
+
attr_reader :noise
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Raysetta
|
|
4
|
+
module Texture
|
|
5
|
+
class SolidColor < Base
|
|
6
|
+
attr_accessor :albedo
|
|
7
|
+
|
|
8
|
+
def initialize(albedo)
|
|
9
|
+
@albedo = albedo
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.rgb(r, g, b)
|
|
13
|
+
new(Vec3.new(r, g, b))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def sample(uv, point)
|
|
17
|
+
albedo
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def ==(tex)
|
|
21
|
+
albedo == tex.albedo
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def hash
|
|
25
|
+
[type, albedo].hash
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def export
|
|
29
|
+
{
|
|
30
|
+
**super,
|
|
31
|
+
albedo: albedo.to_a
|
|
32
|
+
}
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Raysetta
|
|
4
|
+
class Tracer
|
|
5
|
+
attr_reader :scene, :width, :height, :samples_per_pixel, :max_depth
|
|
6
|
+
|
|
7
|
+
def initialize(scene, width: 256, height: 256, samples_per_pixel: 10, max_depth: 10)
|
|
8
|
+
@scene = scene
|
|
9
|
+
@width = width
|
|
10
|
+
@height = height
|
|
11
|
+
@samples_per_pixel = samples_per_pixel
|
|
12
|
+
@max_depth = max_depth
|
|
13
|
+
@pixel_sample_scale = 1.0 / samples_per_pixel
|
|
14
|
+
|
|
15
|
+
scene.camera.viewport(width, height)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def call(x, y)
|
|
19
|
+
pixel_color = Vec3.new
|
|
20
|
+
samples_per_pixel.times do
|
|
21
|
+
pixel_color.add(ray_color(scene.camera.ray(x, y)))
|
|
22
|
+
end
|
|
23
|
+
pixel_color.mul(@pixel_sample_scale).to_pixel
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def ray_color(r, depth = max_depth)
|
|
27
|
+
# If we've exceeded the ray bounce limit, no more light is gathered.
|
|
28
|
+
return Vec3.new unless depth > 0
|
|
29
|
+
|
|
30
|
+
rec = scene.world.hit(r, 0.001..Float::INFINITY)
|
|
31
|
+
# If the ray hits nothing, return the background color.
|
|
32
|
+
return scene.background.sample(r) unless rec
|
|
33
|
+
|
|
34
|
+
emitted = rec.material.emitted(rec.uv, rec.point)
|
|
35
|
+
scatter = rec.material.scatter(r, rec)
|
|
36
|
+
return emitted unless scatter
|
|
37
|
+
|
|
38
|
+
scattered = ray_color(scatter.ray, depth - 1).times(scatter.attenuation)
|
|
39
|
+
|
|
40
|
+
emitted + scattered
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Raysetta
|
|
4
|
+
module Util
|
|
5
|
+
EPSILON = 1e-8
|
|
6
|
+
|
|
7
|
+
def self.degrees_to_radians(degrees) = degrees * Math::PI / 180.0
|
|
8
|
+
|
|
9
|
+
# Returns a random real in [min,max).
|
|
10
|
+
def self.random(min, max) = min + (max-min)*rand
|
|
11
|
+
|
|
12
|
+
def self.linear_to_gamma(linear_component)
|
|
13
|
+
return 0.0 unless linear_component > 0.0
|
|
14
|
+
|
|
15
|
+
Math.sqrt(linear_component)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.gamma_to_linear(gamma_component, gamma = 2.2)
|
|
19
|
+
return 0.0 unless gamma_component > 0.0
|
|
20
|
+
|
|
21
|
+
gamma_component ** gamma
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.sphere_uv(point)
|
|
25
|
+
# point: a given point on the sphere of radius one, centered at the origin.
|
|
26
|
+
# u: returned value [0,1] of angle around the Y axis from X=-1.
|
|
27
|
+
# v: returned value [0,1] of angle from Y=-1 to Y=+1.
|
|
28
|
+
# <1 0 0> yields <0.50 0.50> <-1 0 0> yields <0.00 0.50>
|
|
29
|
+
# <0 1 0> yields <0.50 1.00> < 0 -1 0> yields <0.50 0.00>
|
|
30
|
+
# <0 0 1> yields <0.25 0.50> < 0 0 -1> yields <0.75 0.50>
|
|
31
|
+
|
|
32
|
+
theta = Math.acos(-point.y);
|
|
33
|
+
phi = Math.atan2(-point.z, point.x) + Math::PI;
|
|
34
|
+
|
|
35
|
+
Vec2.new(phi / (2.0*Math::PI), theta / Math::PI)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Raysetta
|
|
4
|
+
class Vec2
|
|
5
|
+
attr_accessor :x, :y
|
|
6
|
+
|
|
7
|
+
alias :u :x
|
|
8
|
+
alias :v :y
|
|
9
|
+
|
|
10
|
+
alias :u= :x=
|
|
11
|
+
alias :v= :y=
|
|
12
|
+
|
|
13
|
+
def initialize(x = 0.0, y = x)
|
|
14
|
+
@x = x
|
|
15
|
+
@y = y
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.random(min = 0.0, max = 1.0)
|
|
19
|
+
Vec2.new(Util.random(min, max), Util.random(min, max))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.sample_square = new(rand - 0.5, rand - 0.5)
|
|
23
|
+
|
|
24
|
+
def to_a = [x, y]
|
|
25
|
+
alias :to_ary :to_a
|
|
26
|
+
|
|
27
|
+
def [](i)
|
|
28
|
+
return x if i == 0
|
|
29
|
+
return y if i == 1
|
|
30
|
+
0.0
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def []=(i, v)
|
|
34
|
+
self.x = v if i == 0
|
|
35
|
+
self.y = v if i == 1
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def -@
|
|
39
|
+
Vec2.new(-x, -y)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def +(v) = Vec2.new(x + v.x, y + v.y)
|
|
43
|
+
def -(v) = Vec2.new(x - v.x, y - v.y)
|
|
44
|
+
def *(t) = Vec2.new(x * t, y * t)
|
|
45
|
+
def /(t) = self * (1.0/t)
|
|
46
|
+
|
|
47
|
+
def add(v)
|
|
48
|
+
self.x += v.x
|
|
49
|
+
self.y += v.y
|
|
50
|
+
self
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def sub(v)
|
|
54
|
+
self.x -= v.x
|
|
55
|
+
self.y -= v.y
|
|
56
|
+
self
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def mul(t)
|
|
60
|
+
self.x *= t
|
|
61
|
+
self.y *= t
|
|
62
|
+
self
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def div(t)
|
|
66
|
+
mul(1.0/t)
|
|
67
|
+
self
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def neg
|
|
71
|
+
self.x = -x
|
|
72
|
+
self.y = -y
|
|
73
|
+
self
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def length = Math.sqrt(length_squared)
|
|
77
|
+
def length_squared = dot(self)
|
|
78
|
+
|
|
79
|
+
def dot(v)
|
|
80
|
+
x * v.x + y * v.y
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def unit = self / length
|
|
84
|
+
|
|
85
|
+
def normalize
|
|
86
|
+
div(length)
|
|
87
|
+
self
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def dup = Vec2.new(x, y)
|
|
91
|
+
|
|
92
|
+
def abs!
|
|
93
|
+
self.x = x.abs
|
|
94
|
+
self.y = y.abs
|
|
95
|
+
self
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def abs
|
|
99
|
+
Vec2.new(x.abs, y.abs)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Return true if the vector is close to zero in all dimensions.
|
|
103
|
+
def zero?
|
|
104
|
+
x.abs < Util::EPSILON &&
|
|
105
|
+
y.abs < Util::EPSILON
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def to_s
|
|
109
|
+
"(#{x}, #{y})"
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def ==(v)
|
|
113
|
+
(x - v.x).abs < Util::EPSILON && (y - v.y).abs < Util::EPSILON
|
|
114
|
+
end
|
|
115
|
+
alias :eql? :==
|
|
116
|
+
|
|
117
|
+
def eql?(v)
|
|
118
|
+
x == v.x && y == v.y
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def hash
|
|
122
|
+
[x, y].hash
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Raysetta
|
|
4
|
+
class Vec3
|
|
5
|
+
attr_accessor :x, :y, :z
|
|
6
|
+
|
|
7
|
+
alias :r :x
|
|
8
|
+
alias :g :y
|
|
9
|
+
alias :b :z
|
|
10
|
+
|
|
11
|
+
alias :r= :x=
|
|
12
|
+
alias :g= :y=
|
|
13
|
+
alias :b= :z=
|
|
14
|
+
|
|
15
|
+
def initialize(x = 0.0, y = x, z = y)
|
|
16
|
+
@x = x
|
|
17
|
+
@y = y
|
|
18
|
+
@z = z
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.random(min = 0.0, max = 1.0)
|
|
22
|
+
Vec3.new(Util.random(min, max), Util.random(min, max), Util.random(min, max))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.random_in_unit_sphere
|
|
26
|
+
loop do
|
|
27
|
+
v = random(-1.0, 1.0)
|
|
28
|
+
return v if v.length_squared < 1
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.random_unit = random_in_unit_sphere.normalize
|
|
33
|
+
|
|
34
|
+
def self.random_on_hemisphere(normal)
|
|
35
|
+
on_unit_sphere = random_unit
|
|
36
|
+
if on_unit_sphere.dot(normal) > 0.0 # In the same hemisphere as the normal
|
|
37
|
+
on_unit_sphere
|
|
38
|
+
else
|
|
39
|
+
-on_unit_sphere
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.random_in_unit_disk
|
|
44
|
+
loop do
|
|
45
|
+
v = new(Util.random(-1.0, 1.0), Util.random(-1.0, 1.0), 0.0)
|
|
46
|
+
return v if v.length_squared < 1
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def to_a = [x, y, z]
|
|
51
|
+
alias :to_ary :to_a
|
|
52
|
+
|
|
53
|
+
def [](i)
|
|
54
|
+
return x if i == 0
|
|
55
|
+
return y if i == 1
|
|
56
|
+
return z if i == 2
|
|
57
|
+
0.0
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def []=(i, v)
|
|
61
|
+
self.x = v if i == 0
|
|
62
|
+
self.y = v if i == 1
|
|
63
|
+
self.z = v if i == 2
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def -@
|
|
67
|
+
Vec3.new(-x, -y, -z)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def +(v) = Vec3.new(x + v.x, y + v.y, z + v.z)
|
|
71
|
+
def -(v) = Vec3.new(x - v.x, y - v.y, z - v.z)
|
|
72
|
+
def *(t) = Vec3.new(x * t, y * t, z * t)
|
|
73
|
+
def /(t) = self * (1.0/t)
|
|
74
|
+
|
|
75
|
+
def add(v)
|
|
76
|
+
self.x += v.x
|
|
77
|
+
self.y += v.y
|
|
78
|
+
self.z += v.z
|
|
79
|
+
self
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def sub(v)
|
|
83
|
+
self.x -= v.x
|
|
84
|
+
self.y -= v.y
|
|
85
|
+
self.z -= v.z
|
|
86
|
+
self
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def mul(t)
|
|
90
|
+
self.x *= t
|
|
91
|
+
self.y *= t
|
|
92
|
+
self.z *= t
|
|
93
|
+
self
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def multiply(v)
|
|
97
|
+
self.x *= v.x
|
|
98
|
+
self.y *= v.y
|
|
99
|
+
self.z *= v.z
|
|
100
|
+
self
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def times(v)
|
|
104
|
+
dup.multiply(v)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def div(t)
|
|
108
|
+
mul(1.0/t)
|
|
109
|
+
self
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def neg
|
|
113
|
+
self.x = -x
|
|
114
|
+
self.y = -y
|
|
115
|
+
self.z = -z
|
|
116
|
+
self
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def length = Math.sqrt(length_squared)
|
|
120
|
+
def length_squared = dot(self)
|
|
121
|
+
|
|
122
|
+
def dot(v)
|
|
123
|
+
x * v.x + y * v.y + z * v.z
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def unit = self / length
|
|
127
|
+
|
|
128
|
+
def normalize
|
|
129
|
+
div(length)
|
|
130
|
+
self
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def dup = Vec3.new(x, y, z)
|
|
134
|
+
|
|
135
|
+
def abs!
|
|
136
|
+
self.x = x.abs
|
|
137
|
+
self.y = y.abs
|
|
138
|
+
self.z = z.abs
|
|
139
|
+
self
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def abs
|
|
143
|
+
Vec3.new(x.abs, y.abs, z.abs)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def smoothstep!
|
|
147
|
+
self.x = x**2 * (3.0 - 2.0 * x)
|
|
148
|
+
self.y = y**2 * (3.0 - 2.0 * y)
|
|
149
|
+
self.z = z**2 * (3.0 - 2.0 * z)
|
|
150
|
+
self
|
|
151
|
+
end
|
|
152
|
+
def smoothstep = dup.smoothstep!
|
|
153
|
+
|
|
154
|
+
def cross(v)
|
|
155
|
+
Vec3.new(
|
|
156
|
+
y * v.z - z * v.y,
|
|
157
|
+
z * v.x - x * v.z,
|
|
158
|
+
x * v.y - y * v.x
|
|
159
|
+
)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def reflect(n)
|
|
163
|
+
self - n * (dot(n) * 2.0)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def refract(n, etai_over_etat)
|
|
167
|
+
cos_theta = [(-self).dot(n), 1.0].min
|
|
168
|
+
r_out_perp = (self + n * cos_theta).mul(etai_over_etat)
|
|
169
|
+
r_out_parallel = n * -Math.sqrt((1.0 - r_out_perp.length_squared).abs)
|
|
170
|
+
r_out_perp.add(r_out_parallel)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def to_pixel
|
|
174
|
+
# Apply a linear to gamma transform for gamma 2
|
|
175
|
+
rg = Util.linear_to_gamma(r)
|
|
176
|
+
gg = Util.linear_to_gamma(g)
|
|
177
|
+
bg = Util.linear_to_gamma(b)
|
|
178
|
+
|
|
179
|
+
# Translate the [0,1] component values to the byte range [0,255].
|
|
180
|
+
intensity = Interval.new(0.000, 0.999)
|
|
181
|
+
rbyte = (256 * intensity.clamp(rg)).to_i
|
|
182
|
+
gbyte = (256 * intensity.clamp(gg)).to_i
|
|
183
|
+
bbyte = (256 * intensity.clamp(bg)).to_i
|
|
184
|
+
|
|
185
|
+
# Return the pixel color components.
|
|
186
|
+
[rbyte, gbyte, bbyte]
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def zero?
|
|
190
|
+
x.abs < Util::EPSILON &&
|
|
191
|
+
y.abs < Util::EPSILON &&
|
|
192
|
+
z.abs < Util::EPSILON
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def to_s
|
|
196
|
+
"(#{x}, #{y}, #{z})"
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def ==(v)
|
|
200
|
+
(x - v.x).abs < Util::EPSILON &&
|
|
201
|
+
(y - v.y).abs < Util::EPSILON &&
|
|
202
|
+
(z - v.z).abs < Util::EPSILON
|
|
203
|
+
end
|
|
204
|
+
alias :eql? :==
|
|
205
|
+
|
|
206
|
+
def eql?(v)
|
|
207
|
+
x == v.x && y == v.y && z == v.z
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def hash
|
|
211
|
+
[x, y, z].hash
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|
data/lib/raysetta.rb
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Raysetta
|
|
4
|
+
class Error < StandardError; end
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
require_relative "raysetta/version"
|
|
8
|
+
require_relative "raysetta/util"
|
|
9
|
+
require_relative "raysetta/vec2"
|
|
10
|
+
require_relative "raysetta/vec3"
|
|
11
|
+
require_relative "raysetta/ray"
|
|
12
|
+
require_relative "raysetta/interval"
|
|
13
|
+
require_relative "raysetta/aabb"
|
|
14
|
+
require_relative "raysetta/hit"
|
|
15
|
+
require_relative "raysetta/scatter"
|
|
16
|
+
require_relative "raysetta/entity_base"
|
|
17
|
+
require_relative "raysetta/image"
|
|
18
|
+
require_relative "raysetta/perlin"
|
|
19
|
+
require_relative "raysetta/object/base"
|
|
20
|
+
require_relative "raysetta/object/bvh"
|
|
21
|
+
require_relative "raysetta/object/group"
|
|
22
|
+
require_relative "raysetta/object/sphere"
|
|
23
|
+
require_relative "raysetta/object/moving_sphere"
|
|
24
|
+
require_relative "raysetta/object/quad"
|
|
25
|
+
require_relative "raysetta/object/tri"
|
|
26
|
+
require_relative "raysetta/object/box"
|
|
27
|
+
require_relative "raysetta/material/base"
|
|
28
|
+
require_relative "raysetta/material/lambertian"
|
|
29
|
+
require_relative "raysetta/material/metal"
|
|
30
|
+
require_relative "raysetta/material/dielectric"
|
|
31
|
+
require_relative "raysetta/material/diffuse_light"
|
|
32
|
+
require_relative "raysetta/texture/base"
|
|
33
|
+
require_relative "raysetta/texture/solid_color"
|
|
34
|
+
require_relative "raysetta/texture/checker"
|
|
35
|
+
require_relative "raysetta/texture/image"
|
|
36
|
+
require_relative "raysetta/texture/noise"
|
|
37
|
+
require_relative "raysetta/background/base"
|
|
38
|
+
require_relative "raysetta/background/solid"
|
|
39
|
+
require_relative "raysetta/background/gradient"
|
|
40
|
+
require_relative "raysetta/background/sphere_map"
|
|
41
|
+
require_relative "raysetta/background/cube_map"
|
|
42
|
+
require_relative "raysetta/camera"
|
|
43
|
+
require_relative "raysetta/scene"
|
|
44
|
+
require_relative "raysetta/tracer"
|
data/mise.toml
ADDED
data/scenes/box.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"world": {
|
|
3
|
+
"box": { "type": "Box", "a": [-1.0, -2.0, -1.0], "b": [1.0, 2.0, 1.0], "material": "mat" }
|
|
4
|
+
},
|
|
5
|
+
"materials": {
|
|
6
|
+
"mat": { "type": "Lambertian", "texture": "col" }
|
|
7
|
+
},
|
|
8
|
+
"textures": {
|
|
9
|
+
"col": { "type": "SolidColor", "albedo": [1.0, 0.2, 1.0] }
|
|
10
|
+
},
|
|
11
|
+
"images": {},
|
|
12
|
+
"noises": {},
|
|
13
|
+
"camera": {
|
|
14
|
+
"vfov": 80.0,
|
|
15
|
+
"lookfrom": [4.5, 4.5, 4.5],
|
|
16
|
+
"lookat": [0.0, 0.0, 0.0],
|
|
17
|
+
"vup": [0.0, 1.0, 0.0],
|
|
18
|
+
"defocus_angle": 0.0,
|
|
19
|
+
"focus_dist": 10.0
|
|
20
|
+
},
|
|
21
|
+
"background": {
|
|
22
|
+
"type": "Gradient",
|
|
23
|
+
"top": [0.5, 0.7, 1.0],
|
|
24
|
+
"bottom": [1.0, 1.0, 1.0]
|
|
25
|
+
}
|
|
26
|
+
}
|