pbrt 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/README.md +267 -0
- data/lib/pbrt.rb +26 -0
- data/lib/pbrt/builder.rb +184 -0
- data/lib/pbrt/builder/accelerator.rb +36 -0
- data/lib/pbrt/builder/area_light_source.rb +25 -0
- data/lib/pbrt/builder/camera.rb +67 -0
- data/lib/pbrt/builder/film.rb +29 -0
- data/lib/pbrt/builder/integrator.rb +82 -0
- data/lib/pbrt/builder/light_source.rb +81 -0
- data/lib/pbrt/builder/material.rb +266 -0
- data/lib/pbrt/builder/named_material.rb +24 -0
- data/lib/pbrt/builder/named_medium.rb +46 -0
- data/lib/pbrt/builder/pixel_filter.rb +64 -0
- data/lib/pbrt/builder/sampler.rb +60 -0
- data/lib/pbrt/builder/shape.rb +171 -0
- data/lib/pbrt/builder/texture.rb +167 -0
- data/lib/pbrt/parameter.rb +12 -0
- data/lib/pbrt/parameter_list.rb +45 -0
- data/lib/pbrt/signature.rb +39 -0
- data/lib/pbrt/spectrum.rb +29 -0
- data/lib/pbrt/statement.rb +11 -0
- data/lib/pbrt/statement/fixed_size.rb +28 -0
- data/lib/pbrt/statement/variadic.rb +15 -0
- data/lib/pbrt/texture.rb +63 -0
- data/lib/pbrt/values.rb +25 -0
- metadata +82 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2c1d00561cc281ba73d41b7eec84aa1b810ab11faa5f9b81e0b327f70cfb766b
|
4
|
+
data.tar.gz: 03c1f5222c7920c6d55629ba9ca36190f1a3b9c41548ad2ba37f570e4bef7f30
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7e971e81039dae5d17d12e051cdff6d3c276c17c5c7364ca2f9cb0f78466f326de9ef86359c74b721da285e34506bb3ea3da1ec631064b67455d867ff99c2816
|
7
|
+
data.tar.gz: f2818a5c28c5c158e17a56856fd643dd0df422770e8e5c646e78948c60ea0dd76a40c23af465e9503e3b8c7b004df3578891558218751e78b31de2688829b6cb
|
data/README.md
ADDED
@@ -0,0 +1,267 @@
|
|
1
|
+
## PBRT
|
2
|
+
|
3
|
+
A Ruby gem to generate scene description files for the third edition of [Physically Based Rendering](http://www.pbr-book.org/).
|
4
|
+
|
5
|
+
This gem implements its [file format specification](https://pbrt.org/fileformat-v3.html) and wraps it in a friendly DSL.
|
6
|
+
|
7
|
+
## Overview
|
8
|
+
|
9
|
+
This gem makes it easier to generate scene description files, because:
|
10
|
+
- It provides methods that only accept valid parameter names
|
11
|
+
- It knows the types of parameters and automatically adds them for you
|
12
|
+
- It closely resembles the structure in the documentation
|
13
|
+
- It's easy to script with bits of Ruby
|
14
|
+
- It has no dependencies and streams to a plain IO object
|
15
|
+
|
16
|
+
## Example
|
17
|
+
|
18
|
+
This generates [the example](https://pbrt.org/fileformat-v3.html#example) from the documentation:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
require "pbrt"
|
22
|
+
|
23
|
+
builder = PBRT::Builder.new do
|
24
|
+
look_at(3, 4, 1.5, 0.5, 0.5, 0, 0, 0, 1)
|
25
|
+
camera.perspective(fov: 45)
|
26
|
+
|
27
|
+
sampler.halton(pixelsamples: 128)
|
28
|
+
integrator.path
|
29
|
+
film.image(filename: "simple.png", xresolution: 400, yresolution: 400)
|
30
|
+
|
31
|
+
world_begin do
|
32
|
+
comment "uniform blue-ish illumination from all directions"
|
33
|
+
light_source.infinite(L: rgb(0.4, 0.45, 0.5))
|
34
|
+
|
35
|
+
comment "approximate the sun"
|
36
|
+
light_source.distant(from: [-30, 40, 100], L: blackbody(3000, 1.5))
|
37
|
+
|
38
|
+
attribute_begin do
|
39
|
+
material.glass
|
40
|
+
shape.sphere(radius: 1)
|
41
|
+
end
|
42
|
+
|
43
|
+
attribute_begin do
|
44
|
+
texture("checks").spectrum.checkerboard(
|
45
|
+
uscale: [8],
|
46
|
+
vscale: [8],
|
47
|
+
tex1: rgb(0.1, 0.1, 0.1),
|
48
|
+
tex2: rgb(0.8, 0.8, 0.8),
|
49
|
+
)
|
50
|
+
|
51
|
+
material.matte(Kd: texture("checks"))
|
52
|
+
translate(0, 0, -1)
|
53
|
+
|
54
|
+
shape.trianglemesh(
|
55
|
+
indices: [0, 1, 2, 0, 2, 3],
|
56
|
+
P: [-20, -20, 0, 20, -20, 0, 20, 20, 0, -20, 20, 0],
|
57
|
+
st: [0, 0, 1, 0, 1, 1, 0, 1],
|
58
|
+
)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
puts builder.to_s
|
64
|
+
```
|
65
|
+
|
66
|
+
[This](https://pbrt.org/simple.png) is what it looks like when rendered.
|
67
|
+
|
68
|
+
## How do I use the gem?
|
69
|
+
|
70
|
+
As you can see in the example above, the gem has a `PBRT::Builder` that takes a block
|
71
|
+
where you can call methods to generate 'directives'. The methods you can call directly
|
72
|
+
correspond to the documentation.
|
73
|
+
|
74
|
+
For example, in the [Transformations section](https://pbrt.org/fileformat-v3.html#transformations)
|
75
|
+
there is an 'Identity' directive which you can generate with the `identity` method.
|
76
|
+
|
77
|
+
There are two kinds of directives:
|
78
|
+
- Those that take plain arguments
|
79
|
+
- Those that take arguments as named parameter lists
|
80
|
+
|
81
|
+
For example:
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
# plain arguments
|
85
|
+
translate(1, 2, 3)
|
86
|
+
|
87
|
+
# parameter list
|
88
|
+
shape.sphere(radius: 2, zmin: 0.2, zmax: 0.7)
|
89
|
+
```
|
90
|
+
|
91
|
+
The majority of directives have 'implementations' such as the 'perspective' implementation
|
92
|
+
for the 'Camera' directive or the 'sphere' implementation for the 'Shape' directive. To
|
93
|
+
specify this, call the method on the directive:
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
camera.perspective
|
97
|
+
sampler.halton(pixelsamples: 16)
|
98
|
+
shape.sphere(radius: 1)
|
99
|
+
light_source.spotlight(from: [0, 1, 0], to: [0, 0, 0])
|
100
|
+
```
|
101
|
+
|
102
|
+
All directives and implementations are specified in the documentation as well as the names
|
103
|
+
of parameters and their types. For parameters that have type `point2`, `point3`, `vector2` etc,
|
104
|
+
you can pass an array and if the parameter takes several points e.g. `point[4]` you can either
|
105
|
+
pass a flat array or group the individual points into sub-arrays:
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
# flat array
|
109
|
+
shape.curve(P: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
|
110
|
+
|
111
|
+
# sub-arrays
|
112
|
+
shape.curve(P: [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]])
|
113
|
+
```
|
114
|
+
|
115
|
+
There is no difference (other than readability). They will generate the same code. In fact, the
|
116
|
+
same is true for any parameter that takes multiple values. For example, the
|
117
|
+
[`LookAt` directive](https://pbrt.org/fileformat-v3.html#transformations) takes 9 floats that may
|
118
|
+
be grouped into sub-arrays for clarity:
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
look_at(
|
122
|
+
[3, 4, 1.5], # eye
|
123
|
+
[0.5, 0.5, 0], # look at point
|
124
|
+
[0, 0, 1], # up vector
|
125
|
+
)
|
126
|
+
```
|
127
|
+
|
128
|
+
Some directives come in pairs like `WorldBegin` / `WorldEnd` and `AttributeBegin` / `AttributeEnd`.
|
129
|
+
The gem only provides a method for 'begin' and lets you pass a block to specify what goes inside it:
|
130
|
+
|
131
|
+
```ruby
|
132
|
+
world_begin do
|
133
|
+
transform(1, 2, 3)
|
134
|
+
sphere.shape
|
135
|
+
|
136
|
+
attribute_begin do
|
137
|
+
# ...
|
138
|
+
end
|
139
|
+
end
|
140
|
+
```
|
141
|
+
|
142
|
+
Finally, there are some directives that take a name such as [`Texture`](https://pbrt.org/fileformat-v3.html#textures)
|
143
|
+
and [`MakeNamedMaterial`](https://pbrt.org/fileformat-v3.html#materials). In these cases, pass the name in to the
|
144
|
+
top-level method before calling others on it.
|
145
|
+
|
146
|
+
Textures also take a 'class' which is either 'spectrum' or 'float' which can be specified with a chained method call:
|
147
|
+
|
148
|
+
```ruby
|
149
|
+
texture("mytexture").spectrum.checkerboard(dimension: 2)
|
150
|
+
make_named_material("mymaterial").plastic(roughness: 0.1)
|
151
|
+
```
|
152
|
+
|
153
|
+
If you can't figure out how to call the directive / implementation you want, this gem has an example of every single
|
154
|
+
one being called in it's [`builder_spec.rb`](https://github.com/tuzz/pbrt/blob/master/spec/pbrt/builder_spec.rb).
|
155
|
+
|
156
|
+
## Spectrums and Textures
|
157
|
+
|
158
|
+
Some parameters take types that are `spectrum`, `spectrum texture`, `float texture`, `spectrum / float texture`.
|
159
|
+
|
160
|
+
A spectrum type is used to specify a color spectrum which can be represented in different ways.
|
161
|
+
[Parameter Lists](https://pbrt.org/fileformat-v3.html#parameter-lists) section for more details. When a parameter
|
162
|
+
has the spectrum type, wrap its arguments with one of the representations:
|
163
|
+
|
164
|
+
```ruby
|
165
|
+
# rgb
|
166
|
+
light_source.point(scale: rgb(0.8, 0.1, 0.1))
|
167
|
+
|
168
|
+
# xyz
|
169
|
+
light_source.point(scale: xyz(0.8, 0.1, 0.1))
|
170
|
+
|
171
|
+
# sampled
|
172
|
+
light_source.point(scale: sampled([300, 0.3, 400, 0.6])
|
173
|
+
light_source.point(scale: sampled("filename"))
|
174
|
+
|
175
|
+
# blackbody
|
176
|
+
light_source.point(scale: blackbody(6500, 1))
|
177
|
+
```
|
178
|
+
|
179
|
+
Occasionally this can be omitted if it can be inferred:
|
180
|
+
|
181
|
+
```ruby
|
182
|
+
light_source.point(scale: "filename")
|
183
|
+
```
|
184
|
+
|
185
|
+
But it's usually better to include it to make your intent clearer.
|
186
|
+
|
187
|
+
A type like `spectrum texture` actually means the parameter accepts either a spectrum or the name of a texture
|
188
|
+
that you have created with the 'Texture' directive. In this case, the string "filename" is ambiguous because it could
|
189
|
+
be a file containing spectrum sample data, or it could be the name of one of your textures.
|
190
|
+
|
191
|
+
If things are ambiguous, PBRT will raise an error:
|
192
|
+
|
193
|
+
```ruby
|
194
|
+
AmbiguousArgumentError:
|
195
|
+
Please specify whether "filename" is a spectrum or texture.
|
196
|
+
If it's a texture, wrap it with: texture("filename")
|
197
|
+
If it's a spectrum, wrap it with its representation: sampled("filename")
|
198
|
+
Valid representations are: rgb, xyz, sampled and blackbody
|
199
|
+
```
|
200
|
+
|
201
|
+
For the `float texture` type, it can always be decided what you meant because a `float` is always a number and a
|
202
|
+
`texture` is always a string.
|
203
|
+
|
204
|
+
There's only one case where the type can be ambiguous and PBRT will not raise an error. If you enter a number for the
|
205
|
+
`spectrum / float texture`, PBRT will assume you meant a float rather than a spectrum as floats are much more common, but
|
206
|
+
if you really want a spectrum then wrap the argument in one of its representations (e.g. rgb).
|
207
|
+
|
208
|
+
## IO / Builder pattern
|
209
|
+
|
210
|
+
In the example above, a block is being passed to the `PBRT::Builder`, but you can also use this as a more traditional
|
211
|
+
builder by calling methods on it:
|
212
|
+
|
213
|
+
```ruby
|
214
|
+
builder = PBRT::Builder.new
|
215
|
+
builder.world_begin do
|
216
|
+
builder.translate(1, 2, 3)
|
217
|
+
builder.shape.sphere
|
218
|
+
end
|
219
|
+
```
|
220
|
+
|
221
|
+
Builder methods return `self` so you can chain methods if you'd like:
|
222
|
+
|
223
|
+
```ruby
|
224
|
+
translate(1, 2, 3).shape.sphere(radius: 1)
|
225
|
+
```
|
226
|
+
|
227
|
+
When a builder is constructed, it takes an IO object that it streams its directives to. You can pass one of your own,
|
228
|
+
for example, if you want to stream your directives straight into a file to save on memory usage:
|
229
|
+
|
230
|
+
```ruby
|
231
|
+
File.open("myscene.pbrt", "w") do |file|
|
232
|
+
PBRT::Builder.new(io: file) do
|
233
|
+
translate(1, 2, 3)
|
234
|
+
shape.sphere(radius: 1)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
```
|
238
|
+
|
239
|
+
## Things this gem won't do
|
240
|
+
|
241
|
+
This gem does some basic checking of parameter and can infer types for you, but it won't do much beyond that. Specifically:
|
242
|
+
|
243
|
+
- It won't error if you pass values of the wrong type
|
244
|
+
- It won't error if you pass arrays with too many / few values
|
245
|
+
- It won't error if you use directives inappropriately, e.g. specifying `LookAt` inside the `WorldBegin` section
|
246
|
+
- It won't explain what any of the directives mean or how to use them
|
247
|
+
|
248
|
+
It would be great if it did some of those things, and I'd happily welcome pull requests to add them.
|
249
|
+
|
250
|
+
## Quirks
|
251
|
+
|
252
|
+
- The 02sequence sampler is called o2sequence because Ruby methods can't begin with a number
|
253
|
+
- The texture method can be used for adding a directive as well as disambiguating values
|
254
|
+
- Every single material parameter can be used in shape directives because the specification allows it
|
255
|
+
- You can actually generate multiple renders for the same scene by adding more `WorldBegin` directives
|
256
|
+
|
257
|
+
## Contribution
|
258
|
+
|
259
|
+
[MIT License](LICENSE)
|
260
|
+
|
261
|
+
I am in no way affiliated with PBRT or any of its authors.
|
262
|
+
|
263
|
+
If you find this gem useful, I'd love to hear how you're using it on [Twitter](https://twitter.com/chrispatuzzo) and will happily
|
264
|
+
welcome pull requests or suggestions for improvement.
|
265
|
+
|
266
|
+
I intend to use this on some other projects of my own, so [a search of my GitHub repositories](https://github.com/search?q=user%3Atuzz+pbrt)
|
267
|
+
might be useful if you're planning to use it. Good luck.
|
data/lib/pbrt.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
module PBRT
|
2
|
+
end
|
3
|
+
|
4
|
+
require "pbrt/parameter"
|
5
|
+
require "pbrt/values"
|
6
|
+
require "pbrt/parameter_list"
|
7
|
+
require "pbrt/statement"
|
8
|
+
require "pbrt/statement/fixed_size"
|
9
|
+
require "pbrt/statement/variadic"
|
10
|
+
require "pbrt/signature"
|
11
|
+
require "pbrt/spectrum"
|
12
|
+
require "pbrt/texture"
|
13
|
+
require "pbrt/builder"
|
14
|
+
require "pbrt/builder/camera"
|
15
|
+
require "pbrt/builder/sampler"
|
16
|
+
require "pbrt/builder/film"
|
17
|
+
require "pbrt/builder/pixel_filter"
|
18
|
+
require "pbrt/builder/integrator"
|
19
|
+
require "pbrt/builder/accelerator"
|
20
|
+
require "pbrt/builder/shape"
|
21
|
+
require "pbrt/builder/light_source"
|
22
|
+
require "pbrt/builder/area_light_source"
|
23
|
+
require "pbrt/builder/material"
|
24
|
+
require "pbrt/builder/named_material"
|
25
|
+
require "pbrt/builder/texture"
|
26
|
+
require "pbrt/builder/named_medium"
|
data/lib/pbrt/builder.rb
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
module PBRT
|
2
|
+
class Builder
|
3
|
+
attr_accessor :io
|
4
|
+
|
5
|
+
def initialize(io: StringIO.new, &block)
|
6
|
+
self.io = io
|
7
|
+
|
8
|
+
instance_eval &block if block_given?
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
io.string
|
13
|
+
end
|
14
|
+
|
15
|
+
def camera
|
16
|
+
Camera.new(self)
|
17
|
+
end
|
18
|
+
|
19
|
+
def sampler
|
20
|
+
Sampler.new(self)
|
21
|
+
end
|
22
|
+
|
23
|
+
def film
|
24
|
+
Film.new(self)
|
25
|
+
end
|
26
|
+
|
27
|
+
def pixel_filter
|
28
|
+
PixelFilter.new(self)
|
29
|
+
end
|
30
|
+
|
31
|
+
def integrator
|
32
|
+
Integrator.new(self)
|
33
|
+
end
|
34
|
+
|
35
|
+
def accelerator
|
36
|
+
Accelerator.new(self)
|
37
|
+
end
|
38
|
+
|
39
|
+
def shape
|
40
|
+
Shape.new(self)
|
41
|
+
end
|
42
|
+
|
43
|
+
def light_source
|
44
|
+
LightSource.new(self)
|
45
|
+
end
|
46
|
+
|
47
|
+
def area_light_source
|
48
|
+
AreaLightSource.new(self)
|
49
|
+
end
|
50
|
+
|
51
|
+
def material
|
52
|
+
Material.new(self)
|
53
|
+
end
|
54
|
+
|
55
|
+
def make_named_material(name)
|
56
|
+
NamedMaterial.new(self, name)
|
57
|
+
end
|
58
|
+
|
59
|
+
def make_named_medium(name)
|
60
|
+
NamedMedium.new(self, name)
|
61
|
+
end
|
62
|
+
|
63
|
+
def medium_interface(*args)
|
64
|
+
write Statement.fixed_size("MediumInterface", 2, args)
|
65
|
+
end
|
66
|
+
|
67
|
+
def identity
|
68
|
+
write Statement.fixed_size("Identity", 0)
|
69
|
+
end
|
70
|
+
|
71
|
+
def translate(*args)
|
72
|
+
write Statement.fixed_size("Translate", 3, args)
|
73
|
+
end
|
74
|
+
|
75
|
+
def scale(*args)
|
76
|
+
write Statement.fixed_size("Scale", 3, args)
|
77
|
+
end
|
78
|
+
|
79
|
+
def rotate(*args)
|
80
|
+
write Statement.fixed_size("Rotate", 4, args)
|
81
|
+
end
|
82
|
+
|
83
|
+
def look_at(*args)
|
84
|
+
write Statement.fixed_size("LookAt", 9, args)
|
85
|
+
end
|
86
|
+
|
87
|
+
def coordinate_system(*args)
|
88
|
+
write Statement.fixed_size("CoordinateSystem", 1, args)
|
89
|
+
end
|
90
|
+
|
91
|
+
def coord_sys_transform(*args)
|
92
|
+
write Statement.fixed_size("CoordSysTransform", 1, args)
|
93
|
+
end
|
94
|
+
|
95
|
+
def transform(*args)
|
96
|
+
write Statement.fixed_size("Transform", 16, args)
|
97
|
+
end
|
98
|
+
|
99
|
+
def concat_transform(*args)
|
100
|
+
write Statement.fixed_size("ConcatTransform", 16, args)
|
101
|
+
end
|
102
|
+
|
103
|
+
def transform_times(*args)
|
104
|
+
write Statement.fixed_size("TransformTimes", 2, args)
|
105
|
+
end
|
106
|
+
|
107
|
+
def active_transform(*args)
|
108
|
+
write Statement.fixed_size("ActiveTransform", 1, args)
|
109
|
+
end
|
110
|
+
|
111
|
+
def reverse_orientation(*args)
|
112
|
+
write Statement.fixed_size("ReverseOrientation", 0)
|
113
|
+
end
|
114
|
+
|
115
|
+
def world_begin(&block)
|
116
|
+
write Statement.fixed_size("WorldBegin", 0)
|
117
|
+
instance_eval &block
|
118
|
+
write Statement.fixed_size("WorldEnd", 0)
|
119
|
+
end
|
120
|
+
|
121
|
+
def attribute_begin(&block)
|
122
|
+
write Statement.fixed_size("AttributeBegin", 0)
|
123
|
+
instance_eval &block
|
124
|
+
write Statement.fixed_size("AttributeEnd", 0)
|
125
|
+
end
|
126
|
+
|
127
|
+
def transform_begin(&block)
|
128
|
+
write Statement.fixed_size("TransformBegin", 0)
|
129
|
+
instance_eval &block
|
130
|
+
write Statement.fixed_size("TransformEnd", 0)
|
131
|
+
end
|
132
|
+
|
133
|
+
def object_begin(*args, &block)
|
134
|
+
write Statement.fixed_size("ObjectBegin", 1, *args)
|
135
|
+
instance_eval &block
|
136
|
+
write Statement.fixed_size("ObjectEnd", 0)
|
137
|
+
end
|
138
|
+
|
139
|
+
def object_instance(*args)
|
140
|
+
write Statement.fixed_size("ObjectInstance", 1, *args)
|
141
|
+
end
|
142
|
+
|
143
|
+
def comment(string)
|
144
|
+
write string.split("\n").map { |s| "# #{s}\n" }.join
|
145
|
+
end
|
146
|
+
|
147
|
+
def include(args)
|
148
|
+
write Statement.fixed_size("Include", 1, args)
|
149
|
+
end
|
150
|
+
|
151
|
+
def named_material(args)
|
152
|
+
write Statement.fixed_size("NamedMaterial", 1, args)
|
153
|
+
end
|
154
|
+
|
155
|
+
def rgb(*args)
|
156
|
+
Spectrum.new(:rgb, *args)
|
157
|
+
end
|
158
|
+
|
159
|
+
def color(*args)
|
160
|
+
Spectrum.new(:color, *args)
|
161
|
+
end
|
162
|
+
|
163
|
+
def xyz(*args)
|
164
|
+
Spectrum.new(:xyz, *args)
|
165
|
+
end
|
166
|
+
|
167
|
+
def sampled(*args)
|
168
|
+
Spectrum.new(:spectrum, *args)
|
169
|
+
end
|
170
|
+
|
171
|
+
def blackbody(*args)
|
172
|
+
Spectrum.new(:blackbody, *args)
|
173
|
+
end
|
174
|
+
|
175
|
+
def texture(*args)
|
176
|
+
PBRT::Texture.new(self, *args)
|
177
|
+
end
|
178
|
+
|
179
|
+
def write(statement)
|
180
|
+
io.puts statement
|
181
|
+
self
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|