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.
Files changed (122) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +72 -0
  4. data/Rakefile +8 -0
  5. data/Steepfile +15 -0
  6. data/exe/raysetta +3 -0
  7. data/lib/raysetta/aabb.rb +116 -0
  8. data/lib/raysetta/background/base.rb +19 -0
  9. data/lib/raysetta/background/cube_map.rb +44 -0
  10. data/lib/raysetta/background/gradient.rb +28 -0
  11. data/lib/raysetta/background/solid.rb +24 -0
  12. data/lib/raysetta/background/sphere_map.rb +30 -0
  13. data/lib/raysetta/camera.rb +109 -0
  14. data/lib/raysetta/entity_base.rb +28 -0
  15. data/lib/raysetta/exe.rb +75 -0
  16. data/lib/raysetta/hit.rb +38 -0
  17. data/lib/raysetta/image.rb +46 -0
  18. data/lib/raysetta/interval.rb +63 -0
  19. data/lib/raysetta/material/base.rb +19 -0
  20. data/lib/raysetta/material/dielectric.rb +60 -0
  21. data/lib/raysetta/material/diffuse_light.rb +42 -0
  22. data/lib/raysetta/material/lambertian.rb +48 -0
  23. data/lib/raysetta/material/metal.rb +49 -0
  24. data/lib/raysetta/object/base.rb +23 -0
  25. data/lib/raysetta/object/box.rb +50 -0
  26. data/lib/raysetta/object/bvh.rb +81 -0
  27. data/lib/raysetta/object/group.rb +59 -0
  28. data/lib/raysetta/object/moving_sphere.rb +41 -0
  29. data/lib/raysetta/object/quad.rb +88 -0
  30. data/lib/raysetta/object/sphere.rb +68 -0
  31. data/lib/raysetta/object/tri.rb +46 -0
  32. data/lib/raysetta/output/base.rb +23 -0
  33. data/lib/raysetta/output/png.rb +26 -0
  34. data/lib/raysetta/output/ppm.rb +19 -0
  35. data/lib/raysetta/perlin.rb +91 -0
  36. data/lib/raysetta/ray.rb +28 -0
  37. data/lib/raysetta/runner/base.rb +28 -0
  38. data/lib/raysetta/runner/concurrent.rb +17 -0
  39. data/lib/raysetta/runner/processes.rb +26 -0
  40. data/lib/raysetta/runner/ractors.rb +32 -0
  41. data/lib/raysetta/runner/sync.rb +28 -0
  42. data/lib/raysetta/runner/threads.rb +26 -0
  43. data/lib/raysetta/runner.rb +45 -0
  44. data/lib/raysetta/scatter.rb +12 -0
  45. data/lib/raysetta/scene.rb +205 -0
  46. data/lib/raysetta/texture/base.rb +19 -0
  47. data/lib/raysetta/texture/checker.rb +55 -0
  48. data/lib/raysetta/texture/image.rb +57 -0
  49. data/lib/raysetta/texture/noise.rb +46 -0
  50. data/lib/raysetta/texture/solid_color.rb +36 -0
  51. data/lib/raysetta/tracer.rb +43 -0
  52. data/lib/raysetta/util.rb +38 -0
  53. data/lib/raysetta/vec2.rb +125 -0
  54. data/lib/raysetta/vec3.rb +214 -0
  55. data/lib/raysetta/version.rb +5 -0
  56. data/lib/raysetta.rb +44 -0
  57. data/mise.toml +2 -0
  58. data/scenes/box.json +26 -0
  59. data/scenes/cornell.json +108 -0
  60. data/scenes/cubemap.json +109 -0
  61. data/scenes/dumb.json +58 -0
  62. data/scenes/earth.json +79 -0
  63. data/scenes/example.json +11712 -0
  64. data/scenes/perlin.json +2126 -0
  65. data/scenes/quads.json +38 -0
  66. data/scenes/simple_light.json +2126 -0
  67. data/scenes/testcube.json +113 -0
  68. data/sig/raysetta/aabb.rbs +39 -0
  69. data/sig/raysetta/background/base.rbs +11 -0
  70. data/sig/raysetta/background/cube_map.rbs +9 -0
  71. data/sig/raysetta/background/gradient.rbs +10 -0
  72. data/sig/raysetta/background/solid.rbs +9 -0
  73. data/sig/raysetta/background/sphere_map.rbs +9 -0
  74. data/sig/raysetta/camera.rbs +41 -0
  75. data/sig/raysetta/entity_base.rbs +12 -0
  76. data/sig/raysetta/hit.rbs +21 -0
  77. data/sig/raysetta/image.rbs +13 -0
  78. data/sig/raysetta/interval.rbs +36 -0
  79. data/sig/raysetta/material/base.rbs +11 -0
  80. data/sig/raysetta/material/dielectric.rbs +15 -0
  81. data/sig/raysetta/material/diffuse_light.rbs +12 -0
  82. data/sig/raysetta/material/lambertian.rbs +10 -0
  83. data/sig/raysetta/material/metal.rbs +11 -0
  84. data/sig/raysetta/object/base.rbs +15 -0
  85. data/sig/raysetta/object/box.rbs +13 -0
  86. data/sig/raysetta/object/bvh.rbs +23 -0
  87. data/sig/raysetta/object/group.rbs +16 -0
  88. data/sig/raysetta/object/moving_sphere.rbs +15 -0
  89. data/sig/raysetta/object/quad.rbs +26 -0
  90. data/sig/raysetta/object/sphere.rbs +14 -0
  91. data/sig/raysetta/object/tri.rbs +15 -0
  92. data/sig/raysetta/output/base.rbs +15 -0
  93. data/sig/raysetta/output/png.rbs +8 -0
  94. data/sig/raysetta/output/ppm.rbs +7 -0
  95. data/sig/raysetta/perlin.rbs +19 -0
  96. data/sig/raysetta/ray.rbs +18 -0
  97. data/sig/raysetta/runner/base.rbs +15 -0
  98. data/sig/raysetta/runner/concurrent.rbs +10 -0
  99. data/sig/raysetta/runner/processes.rbs +9 -0
  100. data/sig/raysetta/runner/ractors.rbs +11 -0
  101. data/sig/raysetta/runner/sync.rbs +11 -0
  102. data/sig/raysetta/runner/threads.rbs +9 -0
  103. data/sig/raysetta/runner.rbs +5 -0
  104. data/sig/raysetta/scatter.rbs +8 -0
  105. data/sig/raysetta/scene.rbs +69 -0
  106. data/sig/raysetta/texture/base.rbs +11 -0
  107. data/sig/raysetta/texture/checker.rbs +15 -0
  108. data/sig/raysetta/texture/image.rbs +11 -0
  109. data/sig/raysetta/texture/noise.rbs +13 -0
  110. data/sig/raysetta/texture/solid_color.rbs +10 -0
  111. data/sig/raysetta/tracer.rbs +16 -0
  112. data/sig/raysetta/util.rbs +15 -0
  113. data/sig/raysetta/vec2.rbs +48 -0
  114. data/sig/raysetta/vec3.rbs +67 -0
  115. data/sig/raysetta.rbs +6 -0
  116. data/sig/stdlib/chunky_png.rbs +24 -0
  117. data/sig/stdlib/etc.rbs +3 -0
  118. data/sig/stdlib/json.rbs +3 -0
  119. data/sig/stdlib/optparse.rbs +4 -0
  120. data/sig/stdlib/parallel.rbs +5 -0
  121. data/sig/stdlib/progress.rbs +5 -0
  122. 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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Raysetta
4
+ VERSION = "0.1.0"
5
+ 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
@@ -0,0 +1,2 @@
1
+ [tools]
2
+ ruby = "4.0.2"
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
+ }