Skip to content

Commit 545569f

Browse files
committed
switch to per-sample FBM composition and displacement calculations which simplifies the code a lot (at the expense of a negligible cost in performance)
1 parent d67fd56 commit 545569f

File tree

1 file changed

+65
-29
lines changed

1 file changed

+65
-29
lines changed

sources/noise/compounds.swift

Lines changed: 65 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ public
22
struct FBM<Source>:Noise where Source:Noise
33
{
44
private
5-
let generators:[Source]
5+
let generators:[Source],
6+
octaves:Int,
7+
persistence:Double,
8+
lacunarity:Double,
9+
amplitude:Double
610

711
// UNDOCUMENTED
812
public
@@ -25,31 +29,45 @@ struct FBM<Source>:Noise where Source:Noise
2529
init(generators:[Source])
2630
{
2731
self.generators = generators
32+
33+
self.octaves = 1
34+
self.persistence = 0.5
35+
self.lacunarity = 2
36+
self.amplitude = 1
2837
}
2938

3039
@available(*, unavailable, message: "init(amplitude:frequency:seed:) defaults to octaves = 1, which does not make sense for FBM modules")
3140
public
3241
init(amplitude:Double, frequency:Double, seed:Int)
3342
{
3443
self.generators = []
44+
45+
self.octaves = 1
46+
self.persistence = 0.5
47+
self.lacunarity = 2
48+
self.amplitude = 1
3549
}
3650

3751
@available(*, unavailable, message: "use init(_:octaves:persistence:lacunarity:) instead")
3852
public
3953
init(amplitude:Double, frequency:Double, octaves:Int, persistence:Double = 0.75, lacunarity:Double = 2, seed:Int = 0)
4054
{
4155
self.generators = []
56+
57+
self.octaves = 1
58+
self.persistence = 0.5
59+
self.lacunarity = 2
60+
self.amplitude = 1
4261
}
4362

4463
// UNDOCUMENTED, default was changed from 0.75 to 0.5
4564
public
4665
init(_ source:Source, octaves:Int, persistence:Double = 0.5, lacunarity:Double = 2)
4766
{
4867
// calculate maximum range
49-
let range_inverse:Double
5068
if persistence == 0.5
5169
{
52-
range_inverse = Double(1 << (octaves - 1)) / Double(1 << octaves - 1)
70+
self.amplitude = Double(1 << (octaves - 1)) / Double(1 << octaves - 1)
5371
}
5472
else
5573
{
@@ -61,50 +79,65 @@ struct FBM<Source>:Noise where Source:Noise
6179
contribution *= persistence
6280
}
6381

64-
range_inverse = 1 / accumulation
82+
self.amplitude = 1 / accumulation
6583
}
6684

67-
var generators:[Source] = [source.amplitude_scaled(by: range_inverse)]
85+
var generators:[Source] = [source]
6886
generators.reserveCapacity(octaves)
6987
for i in (0 ..< octaves - 1)
7088
{
71-
generators.append(generators[i].amplitude_scaled(by: persistence).frequency_scaled(by: lacunarity).reseeded())
89+
generators.append(generators[i].reseeded())
7290
}
7391

7492
self.generators = generators
93+
self.octaves = octaves
94+
self.persistence = persistence
95+
self.lacunarity = lacunarity
7596
}
7697

7798
public
7899
func evaluate(_ x:Double, _ y:Double) -> Double
79100
{
80-
var z:Double = 0
101+
var Σ:Double = 0,
102+
a:Double = self.amplitude,
103+
f:Double = 1
81104
for generator in self.generators
82105
{
83-
z += generator.evaluate(x, y) // a .reduce(:{}) is much slower than a simple loop
106+
Σ += a * generator.evaluate(f * x, f * y)
107+
a *= self.persistence
108+
f *= self.lacunarity
84109
}
85-
return z
110+
return Σ
86111
}
87112

88113
public
89114
func evaluate(_ x:Double, _ y:Double, _ z:Double) -> Double
90115
{
91-
var w:Double = 0
116+
var Σ:Double = 0,
117+
a:Double = self.amplitude,
118+
f:Double = 1
92119
for generator in self.generators
93120
{
94-
w += generator.evaluate(x, y, z)
121+
Σ += a * generator.evaluate(f * x, f * y, f * z)
122+
a *= self.persistence
123+
f *= self.lacunarity
95124
}
96-
return w
125+
return Σ
97126
}
98127

99128
public
100129
func evaluate(_ x:Double, _ y:Double, _ z:Double, _ w:Double) -> Double
101130
{
102-
var u:Double = 0
131+
var Σ:Double = 0,
132+
a:Double = self.amplitude,
133+
f:Double = 1
103134
for generator in self.generators
104135
{
105-
u += generator.evaluate(x, y, z, w)
136+
Σ += a * generator.evaluate(f * x, f * y, f * z, f * w)
137+
a *= self.persistence
138+
f *= self.lacunarity
106139
}
107-
return u
140+
return Σ
108141
}
109142
}
110143

@@ -114,7 +147,8 @@ struct DistortedNoise<Source, Displacement>:Noise where Source:Noise, Displaceme
114147
{
115148
private
116149
let source:Source,
117-
displacement:Displacement
150+
displacement:Displacement,
151+
strength:Double
118152

119153
public
120154
func amplitude_scaled(by factor:Double) -> DistortedNoise<Source, Displacement>
@@ -136,47 +170,49 @@ struct DistortedNoise<Source, Displacement>:Noise where Source:Noise, Displaceme
136170
with: self.displacement.reseeded())
137171
}
138172

139-
public
140-
init(displacing source:Source, with displacement:Displacement)
173+
public
174+
init(displacing source:Source, with displacement:Displacement, strength:Double = 1)
141175
{
142176
self.source = source
143177
self.displacement = displacement
178+
self.strength = strength
144179
}
145180

146181
public
147182
func evaluate(_ x: Double, _ y: Double) -> Double
148183
{
149-
let dx:Double = self.displacement.evaluate(x, y),
150-
dy:Double = self.displacement.evaluate(y, x)
184+
let dx:Double = self.strength * self.displacement.evaluate(x, y),
185+
dy:Double = self.strength * self.displacement.evaluate(y, x)
151186
return self.source.evaluate(x + dx, y + dy)
152187
}
153188

154189
public
155190
func evaluate(_ x: Double, _ y: Double, _ z: Double) -> Double
156191
{
157-
let dx:Double = 1.0 + self.displacement.evaluate(x, y, z),
158-
dy:Double = 1.0 + self.displacement.evaluate(y, z, x),
159-
dz:Double = 1.0 + self.displacement.evaluate(z, x, y)
192+
let dx:Double = self.strength * self.displacement.evaluate(x, y, z),
193+
dy:Double = self.strength * self.displacement.evaluate(y, z, x),
194+
dz:Double = self.strength * self.displacement.evaluate(z, x, y)
160195
return self.source.evaluate(x + dx, y + dy, z + dz)
161196
}
162197

163198
public
164199
func evaluate(_ x: Double, _ y: Double, _ z: Double, _ w:Double) -> Double
165200
{
166-
let dx:Double = self.displacement.evaluate(x, y, z, w),
167-
dy:Double = self.displacement.evaluate(y, z, w, x),
168-
dz:Double = self.displacement.evaluate(z, w, x, y),
169-
dw:Double = self.displacement.evaluate(w, x, y, z)
201+
let dx:Double = self.strength * self.displacement.evaluate(x, y, z, w),
202+
dy:Double = self.strength * self.displacement.evaluate(y, z, w, x),
203+
dz:Double = self.strength * self.displacement.evaluate(z, w, x, y),
204+
dw:Double = self.strength * self.displacement.evaluate(w, x, y, z)
170205
return self.source.evaluate(x + dx, y + dy, z + dz, w + dw)
171206
}
172207
}
173208

174209
extension DistortedNoise where Source == Displacement
175210
{
176211
public
177-
init(_ source:Source, strength:Double)
212+
init(_ source:Source, strength:Double = 1)
178213
{
179214
self.source = source
180-
self.displacement = source.amplitude_scaled(by: strength)
215+
self.displacement = source
216+
self.strength = strength
181217
}
182218
}

0 commit comments

Comments
 (0)