@@ -228,10 +228,8 @@ struct CellNoise3D:Noise
228
228
public
229
229
func evaluate( _ x: Double , _ y: Double , _ z: Double ) -> Double
230
230
{
231
- let sample : DoubleV3 = ( x * self . frequency, y * self . frequency, z * self . frequency)
232
-
233
- let bin : IntV3 = ( floor ( sample. x) , floor ( sample. y) , floor ( sample. z) ) ,
234
- offset : DoubleV3 = ( sample. x - Double( bin. a) , sample. y - Double( bin. b) , sample. z - Double( bin. c) )
231
+ let sample : DoubleV3 = ( x * self . frequency, y * self . frequency, z * self . frequency) ,
232
+ bin : IntV3 = ( floor ( sample. x) , floor ( sample. y) , floor ( sample. z) )
235
233
236
234
// determine kernel
237
235
@@ -244,165 +242,205 @@ struct CellNoise3D:Noise
244
242
// near - quadrant.x ————— near quadrant →
245
243
// ↓
246
244
247
- let quadrant : IntV3 = ( offset. x > 0.5 ? 1 : - 1 , offset. y > 0.5 ? 1 : - 1 , offset. z > 0.5 ? 1 : - 1 ) ,
248
- near : IntV3 = ( bin. a + ( quadrant. a + 1 ) >> 1 , bin. b + ( quadrant. b + 1 ) >> 1 , bin. c + ( quadrant. c + 1 ) >> 1 )
245
+ let quadrant : IntV3 = ( sample. x - Double( bin. a) > 0.5 ? 1 : - 1 ,
246
+ sample. y - Double( bin. b) > 0.5 ? 1 : - 1 ,
247
+ sample. z - Double( bin. c) > 0.5 ? 1 : - 1 )
248
+ let near : IntV3 = ( bin. a + ( quadrant. a + 1 ) >> 1 , bin. b + ( quadrant. b + 1 ) >> 1 , bin. c + ( quadrant. c + 1 ) >> 1 )
249
249
250
250
let nearpoint_disp : DoubleV3 = ( abs ( offset. x - Double( ( quadrant. a + 1 ) >> 1 ) ) ,
251
251
abs ( offset. y - Double( ( quadrant. b + 1 ) >> 1 ) ) ,
252
252
abs ( offset. z - Double( ( quadrant. c + 1 ) >> 1 ) ) )
253
253
254
254
var r2 : Double = self . distance ( from: sample, generating_point: near)
255
255
256
- // the following unrolled code is not actually necessary — the loop at the
257
- // bottom of the function is capable of handling all cases, but unrolling
258
- // it partially results in an enormous performance gain, about a factor
259
- // of 7 over the pure loop version.
260
256
@inline ( __always)
261
- func test ( generating_point : IntV3 , dx : Double = 0 , dy : Double = 0 , dz : Double = 0 )
257
+ func _inspect_cell ( offset : IntV3 )
262
258
{
263
- if dx*dx + dy*dy + dz*dz < r2
259
+ // calculate distance from quadrant volume to kernel cell
260
+ var cell_distance2 : Double
261
+ if cell_offset. a != 0
262
+ { // move by 0.5 towards zero
263
+ let dx : Double = nearpoint_disp. x + Double( cell_offset. a) + ( cell_offset. a > 0 ? - 0.5 : 0.5 )
264
+ cell_distance2 = dx*dx
265
+ }
266
+ else
264
267
{
265
- r2 = min ( r2 , self . distance ( from : sample , generating_point : generating_point ) )
268
+ cell_distance2 = 0
266
269
}
270
+
271
+ if cell_offset. b != 0
272
+ { // move by 0.5 towards zero
273
+ let dy : Double = nearpoint_disp. y + Double( cell_offset. b) + ( cell_offset. b > 0 ? - 0.5 : 0.5 )
274
+ cell_distance2 += dy*dy
275
+ }
276
+
277
+ if cell_offset. c != 0
278
+ { // move by 0.5 towards zero
279
+ let dz : Double = nearpoint_disp. z + Double( cell_offset. c) + ( cell_offset. c > 0 ? - 0.5 : 0.5 )
280
+ cell_distance2 += dz*dz
281
+ }
282
+
283
+ guard cell_distance2 < r2
284
+ else
285
+ {
286
+ return
287
+ }
288
+
289
+ let generating_point : IntV3 = ( near. a + quadrant. a*cell_offset. a,
290
+ near. b + quadrant. b*cell_offset. b,
291
+ near. c + quadrant. c*cell_offset. c)
292
+ r2 = min ( r2, self . distance ( from: sample, generating_point: generating_point) )
267
293
}
268
294
269
- // (0.0 , [(-1, 0, 0), (0, -1, 0), (0, 0, -1), (0, -1, -1), (-1, 0, -1), (-1, -1, 0), (-1, -1, -1)])
270
- let far : IntV3 = ( near. a - quadrant. a, near. b - quadrant. b, near. c - quadrant. c)
271
- test ( generating_point: ( far. a, near. b, near. c) , dx: nearpoint_disp. x - 0.5 )
272
- test ( generating_point: ( near. a, far. b, near. c) , dy: nearpoint_disp. y - 0.5 )
273
- test ( generating_point: ( near. a, near. b, far. c) , dz: nearpoint_disp. z - 0.5 )
295
+ // check each cell group, exiting early if we are guaranteed to have found
296
+ // the closest point
274
297
275
- test ( generating_point: ( near. a, far. b, far. c) , dy: nearpoint_disp. y - 0.5 , dz: nearpoint_disp. z - 0.5 )
276
- test ( generating_point: ( far. a, near. b, far. c) , dx: nearpoint_disp. x - 0.5 , dz: nearpoint_disp. z - 0.5 )
277
- test ( generating_point: ( far. a, far. b, near. c) , dx: nearpoint_disp. x - 0.5 , dy: nearpoint_disp. y - 0.5 )
298
+ // Cell group:
299
+ // within r^2 = 0.25
300
+ // cumulative sample coverage = 47.85%
301
+ _inspect_cell ( offset: ( - 1 , 0 , 0 ) )
302
+ _inspect_cell ( offset: ( 0 , - 1 , 0 ) )
303
+ _inspect_cell ( offset: ( 0 , 0 , - 1 ) )
278
304
279
- test ( generating_point: far, dx: nearpoint_disp. x - 0.5 , dy: nearpoint_disp. y - 0.5 , dz: nearpoint_disp. z - 0.5 )
305
+ _inspect_cell ( offset: ( 0 , - 1 , - 1 ) )
306
+ _inspect_cell ( offset: ( - 1 , 0 , - 1 ) )
307
+ _inspect_cell ( offset: ( - 1 , - 1 , 0 ) )
280
308
281
- // EARLY EXIT: Testing shows about 47.85% of samples are eliminated by here
282
- // (0.25, [(1, 0, 0), ( 0, 1, 0), ( 0, 0, 1),
283
- // (0, -1, 1), ( 0, 1, -1), ( 1, 0, -1), (-1, 0, 1), (-1, 1, 0), (1, -1, 0),
284
- // (1, -1, -1), (-1, 1, -1), (-1, -1, 1)])
309
+ _inspect_cell ( offset: ( - 1 , - 1 , - 1 ) )
285
310
guard r2 > 0.25
286
311
else
287
312
{
288
313
return self . amplitude * r2
289
314
}
290
315
291
- let inner : IntV3 = ( near. a + quadrant. a, near. b + quadrant. b, near. c + quadrant. c)
292
- test ( generating_point: ( inner. a, near. b, near. c) , dx: nearpoint_disp. x + 0.5 )
293
- test ( generating_point: ( near. a, inner. b, near. c) , dy: nearpoint_disp. y + 0.5 )
294
- test ( generating_point: ( near. a, near. b, inner. c) , dz: nearpoint_disp. z + 0.5 )
316
+ // Cell group:
317
+ // within r^2 = 0.5
318
+ // cumulative sample coverage = 88.60%
319
+ for cell_offset in [ ( 1 , 0 , 0 ) , ( 0 , 1 , 0 ) , ( 0 , 0 , 1 ) ,
320
+ ( 0 , - 1 , 1 ) , ( 0 , 1 , - 1 ) , ( 1 , 0 , - 1 ) , ( - 1 , 0 , 1 ) , ( - 1 , 1 , 0 ) , ( 1 , - 1 , 0 ) ,
321
+ ( 1 , - 1 , - 1 ) , ( - 1 , 1 , - 1 ) , ( - 1 , - 1 , 1 ) ]
322
+ {
323
+ _inspect_cell ( offset: cell_offset)
324
+ }
325
+ guard r2 > 0.5
326
+ else
327
+ {
328
+ return self . amplitude * r2
329
+ }
295
330
296
- test ( generating_point: ( near. a, far. b, inner. c) , dy: nearpoint_disp. y - 0.5 , dz: nearpoint_disp. z + 0.5 )
297
- test ( generating_point: ( near. a, inner. b, far. c) , dy: nearpoint_disp. y + 0.5 , dz: nearpoint_disp. z - 0.5 )
298
- test ( generating_point: ( inner. a, near. b, far. c) , dx: nearpoint_disp. x + 0.5 , dz: nearpoint_disp. z - 0.5 )
299
- test ( generating_point: ( far. a, near. b, inner. c) , dx: nearpoint_disp. x - 0.5 , dz: nearpoint_disp. z + 0.5 )
300
- test ( generating_point: ( far. a, inner. b, near. c) , dx: nearpoint_disp. x - 0.5 , dy: nearpoint_disp. y + 0.5 )
301
- test ( generating_point: ( inner. a, far. b, near. c) , dx: nearpoint_disp. x + 0.5 , dy: nearpoint_disp. y - 0.5 )
331
+ // Cell group:
332
+ // within r^2 = 0.75
333
+ // cumulative sample coverage = 98.26%
334
+ for cell_offset in [ ( 0 , 1 , 1 ) , ( 1 , 0 , 1 ) , ( 1 , 1 , 0 ) , ( - 1 , 1 , 1 ) , ( 1 , - 1 , 1 ) , ( 1 , 1 , - 1 ) ]
335
+ {
336
+ _inspect_cell ( offset: cell_offset)
337
+ }
338
+ guard r2 > 0.75
339
+ else
340
+ {
341
+ return self . amplitude * r2
342
+ }
302
343
303
- test ( generating_point: ( inner. a, far. b, far. c) , dx: nearpoint_disp. x + 0.5 , dy: nearpoint_disp. y - 0.5 , dz: nearpoint_disp. z - 0.5 )
304
- test ( generating_point: ( far. a, inner. b, far. c) , dx: nearpoint_disp. x - 0.5 , dy: nearpoint_disp. y + 0.5 , dz: nearpoint_disp. z - 0.5 )
305
- test ( generating_point: ( far. a, far. b, inner. c) , dx: nearpoint_disp. x - 0.5 , dy: nearpoint_disp. y - 0.5 , dz: nearpoint_disp. z + 0.5 )
344
+ // Cell group:
345
+ // within r^2 = 1.0
346
+ // cumulative sample coverage = 99.94%
347
+ _inspect_cell ( offset: ( 1 , 1 , 1 ) )
348
+ guard r2 > 1.0
349
+ else
350
+ {
351
+ return self . amplitude * r2
352
+ }
306
353
307
- // EARLY EXIT: Testing shows about 88.60% of samples are eliminated by here
308
- // (0.5 , [(0, 1, 1), (1, 0, 1), (1, 1, 0), (-1, 1, 1), (1, -1, 1), (1, 1, -1)])
309
- guard r2 > 0.5
354
+ // Cell group:
355
+ // within r^2 = 1.25
356
+ // cumulative sample coverage > 99.99%
357
+ for cell_offset in [ ( - 2 , 0 , 0 ) , ( 0 , - 2 , 0 ) , ( 0 , 0 , - 2 ) ,
358
+ ( 0 , - 2 , - 1 ) , ( 0 , - 1 , - 2 ) , ( - 2 , 0 , - 1 ) , ( - 1 , 0 , - 2 ) , ( - 2 , - 1 , 0 ) , ( - 1 , - 2 , 0 ) ,
359
+ ( - 2 , - 1 , - 1 ) , ( - 1 , - 2 , - 1 ) , ( - 1 , - 1 , - 2 ) ]
360
+ {
361
+ _inspect_cell ( offset: cell_offset)
362
+ }
363
+ guard r2 > 1.25
310
364
else
311
365
{
312
366
return self . amplitude * r2
313
367
}
314
368
315
- test ( generating_point: ( near. a, inner. b, inner. c) , dy: nearpoint_disp. y + 0.5 , dz: nearpoint_disp. z + 0.5 )
316
- test ( generating_point: ( inner. a, near. b, inner. c) , dx: nearpoint_disp. x + 0.5 , dz: nearpoint_disp. z + 0.5 )
317
- test ( generating_point: ( inner. a, inner. b, near. c) , dx: nearpoint_disp. x + 0.5 , dy: nearpoint_disp. y + 0.5 )
369
+ // Cell group:
370
+ // within r^2 = 1.5
371
+ // cumulative sample coverage > 99.99%
372
+ for cell_offset in [ ( 0 , 1 , - 2 ) , ( 0 , - 2 , 1 ) , ( 1 , 0 , - 2 ) , ( - 2 , 0 , 1 ) , ( 1 , - 2 , 0 ) , ( - 2 , 1 , 0 ) ,
373
+ ( - 2 , 1 , - 1 ) , ( - 2 , - 1 , 1 ) , ( 1 , - 2 , - 1 ) , ( - 1 , - 2 , 1 ) , ( 1 , - 1 , - 2 ) , ( - 1 , 1 , - 2 ) ]
374
+ {
375
+ _inspect_cell ( offset: cell_offset)
376
+ }
377
+ guard r2 > 1.5
378
+ else
379
+ {
380
+ return self . amplitude * r2
381
+ }
318
382
319
- test ( generating_point: ( far. a, inner. b, inner. c) , dx: nearpoint_disp. x - 0.5 , dy: nearpoint_disp. y + 0.5 , dz: nearpoint_disp. z + 0.5 )
320
- test ( generating_point: ( inner. a, far. b, inner. c) , dx: nearpoint_disp. x + 0.5 , dy: nearpoint_disp. y - 0.5 , dz: nearpoint_disp. z + 0.5 )
321
- test ( generating_point: ( inner. a, inner. b, far. c) , dx: nearpoint_disp. x + 0.5 , dy: nearpoint_disp. y + 0.5 , dz: nearpoint_disp. z - 0.5 )
383
+ // Cell group:
384
+ // within r^2 = 2.0
385
+ // cumulative sample coverage > 99.99%
386
+ _inspect_cell ( offset: ( - 2 , 1 , 1 ) )
387
+ _inspect_cell ( offset: ( 1 , - 2 , 1 ) )
388
+ _inspect_cell ( offset: ( 1 , 1 , - 2 ) )
389
+ guard r2 > 2.0
390
+ else
391
+ {
392
+ return self . amplitude * r2
393
+ }
322
394
323
- // EARLY EXIT: Testing shows about 98.26% of samples are eliminated by here
324
- // (0.75, [(1, 1, 1)])
325
- guard r2 > 0.75
395
+ // Cell group:
396
+ // within r^2 = 2.25
397
+ // cumulative sample coverage > 99.99%
398
+ for cell_offset in [ ( 0 , - 2 , - 2 ) , ( - 2 , 0 , - 2 ) , ( - 2 , - 2 , 0 ) , ( - 1 , - 2 , - 2 ) , ( - 2 , - 1 , - 2 ) , ( - 2 , - 2 , - 1 ) ]
399
+ {
400
+ _inspect_cell ( offset: cell_offset)
401
+ }
402
+ guard r2 > 2.25
326
403
else
327
404
{
328
405
return self . amplitude * r2
329
406
}
330
407
331
- test ( generating_point: inner, dx: nearpoint_disp. x + 0.5 , dy: nearpoint_disp. y + 0.5 , dz: nearpoint_disp. z + 0.5 )
332
-
333
- // Testing shows about 99.94% of samples are eliminated by here
334
-
335
- // The following loop is responsible for about 25% of the noise generator’s
336
- // runtime. While it is possible to unroll the rest of it, we run up against
337
- // diminishing returns.
338
- let kernel : [ ( r2: Double , cell_offsets: [ ( Int , Int , Int ) ] ) ] =
339
- [
340
- // (0.0 , [(-1, 0, 0), (0, -1, 0), (0, 0, -1), (-1, -1, 0), (-1, 0, -1), (0, -1, -1), (-1, -1, -1)]),
341
- // (0.25, [(1, 0, 0), (0, 1, 0), (0, 0, 1), (-1, 0, 1), (0, -1, 1), (-1, -1, 1), (-1, 1, 0), (1, -1, 0),
342
- // (0, 1, -1), (-1, 1, -1), (1, 0, -1), (1, -1, -1)]),
343
- // (0.5 , [(0, 1, 1), (1, 0, 1), (1, 1, 0), (-1, 1, 1), (1, -1, 1), (1, 1, -1)]),
344
- // (0.75, [(1, 1, 1)]),
345
- ( 1.0 , [ ( - 2 , 0 , 0 ) , ( - 2 , - 1 , 0 ) , ( 0 , - 2 , 0 ) , ( - 1 , - 2 , 0 ) , ( - 2 , 0 , - 1 ) , ( - 2 , - 1 , - 1 ) , ( 0 , - 2 , - 1 ) , ( - 1 , - 2 , - 1 ) ,
346
- ( 0 , 0 , - 2 ) , ( - 1 , 0 , - 2 ) , ( 0 , - 1 , - 2 ) , ( - 1 , - 1 , - 2 ) ] ) ,
347
- ( 1.25 , [ ( - 2 , 0 , 1 ) , ( - 2 , - 1 , 1 ) , ( 0 , - 2 , 1 ) , ( - 1 , - 2 , 1 ) , ( - 2 , 1 , 0 ) , ( 1 , - 2 , 0 ) , ( - 2 , 1 , - 1 ) , ( 1 , - 2 , - 1 ) ,
348
- ( 0 , 1 , - 2 ) , ( - 1 , 1 , - 2 ) , ( 1 , 0 , - 2 ) , ( 1 , - 1 , - 2 ) ] ) ,
349
- ( 1.5 , [ ( - 2 , 1 , 1 ) , ( 1 , - 2 , 1 ) , ( 1 , 1 , - 2 ) ] ) ,
350
- ( 2.0 , [ ( - 2 , - 2 , 0 ) , ( - 2 , - 2 , - 1 ) , ( - 2 , 0 , - 2 ) , ( - 2 , - 1 , - 2 ) , ( 0 , - 2 , - 2 ) , ( - 1 , - 2 , - 2 ) ] ) ,
351
- ( 2.25 , [ ( 0 , 0 , 2 ) , ( - 1 , 0 , 2 ) , ( 0 , - 1 , 2 ) , ( - 1 , - 1 , 2 ) , ( - 2 , - 2 , 1 ) , ( 0 , 2 , 0 ) , ( - 1 , 2 , 0 ) , ( 2 , 0 , 0 ) ,
352
- ( 2 , - 1 , 0 ) , ( 0 , 2 , - 1 ) , ( - 1 , 2 , - 1 ) , ( 2 , 0 , - 1 ) , ( 2 , - 1 , - 1 ) , ( - 2 , 1 , - 2 ) , ( 1 , - 2 , - 2 ) ] ) ,
353
- ( 2.5 , [ ( 0 , 1 , 2 ) , ( - 1 , 1 , 2 ) , ( 1 , 0 , 2 ) , ( 1 , - 1 , 2 ) , ( 0 , 2 , 1 ) , ( - 1 , 2 , 1 ) , ( 2 , 0 , 1 ) , ( 2 , - 1 , 1 ) ,
354
- ( 1 , 2 , 0 ) , ( 2 , 1 , 0 ) , ( 1 , 2 , - 1 ) , ( 2 , 1 , - 1 ) ] ) ,
355
- ( 2.75 , [ ( 1 , 1 , 2 ) , ( 1 , 2 , 1 ) , ( 2 , 1 , 1 ) ] )
356
- ]
357
-
358
- for (kernel_radius2, cell_offsets) : ( r2: Double , cell_offsets: [ ( Int , Int , Int ) ] ) in kernel
408
+ // Cell group:
409
+ // within r^2 = 2.5
410
+ // cumulative sample coverage > 99.99%
411
+ for cell_offset in [ ( 2 , 0 , 0 ) , ( 0 , 2 , 0 ) , ( 0 , 0 , 2 ) ,
412
+ ( 0 , - 1 , 2 ) , ( 0 , 2 , - 1 ) , ( - 1 , 0 , 2 ) , ( 2 , 0 , - 1 ) , ( - 1 , 2 , 0 ) , ( 2 , - 1 , 0 ) ,
413
+ ( 1 , - 2 , - 2 ) , ( 2 , - 1 , - 1 ) , ( - 2 , - 2 , 1 ) , ( - 1 , - 1 , 2 ) , ( - 2 , 1 , - 2 ) , ( - 1 , 2 , - 1 ) ]
359
414
{
360
- // EARLY EXIT
361
- guard kernel_radius2 < r2
362
- else
363
- {
364
- break
365
- }
415
+ _inspect_cell ( offset: cell_offset)
416
+ }
417
+ guard r2 > 2.5
418
+ else
419
+ {
420
+ return self . amplitude * r2
421
+ }
366
422
367
- for cell_offset : IntV3 in cell_offsets
368
- {
369
- // calculate distance from quadrant volume to kernel cell
370
- var cell_distance2 : Double
371
- if cell_offset. a == 0
372
- {
373
- cell_distance2 = 0
374
- }
375
- else
376
- { // move by 0.5 towards zero
377
- let dx : Double = nearpoint_disp. x + Double( cell_offset. a) + ( cell_offset. a > 0 ? - 0.5 : 0.5 )
378
- cell_distance2 = dx*dx
379
- }
380
-
381
- if cell_offset. b != 0
382
- { // move by 0.5 towards zero
383
- let dy : Double = nearpoint_disp. y + Double( cell_offset. b) + ( cell_offset. b > 0 ? - 0.5 : 0.5 )
384
- cell_distance2 += dy*dy
385
- }
386
-
387
- if cell_offset. c != 0
388
- { // move by 0.5 towards zero
389
- let dz : Double = nearpoint_disp. z + Double( cell_offset. c) + ( cell_offset. c > 0 ? - 0.5 : 0.5 )
390
- cell_distance2 += dz*dz
391
- }
392
-
393
- guard cell_distance2 < r2
394
- else
395
- {
396
- continue
397
- }
398
-
399
- let generating_point : IntV3 = ( near. a + quadrant. a*cell_offset. a,
400
- near. b + quadrant. b*cell_offset. b,
401
- near. c + quadrant. c*cell_offset. c)
402
- r2 = min ( r2, self . distance ( from: sample, generating_point: generating_point) )
403
- }
423
+ // Cell group:
424
+ // within r^2 = 2.75
425
+ // cumulative sample coverage > 99.99%
426
+ for cell_offset in [ ( 0 , 1 , 2 ) , ( 0 , 2 , 1 ) , ( 1 , 0 , 2 ) , ( 2 , 0 , 1 ) , ( 1 , 2 , 0 ) , ( 2 , 1 , 0 ) ,
427
+ ( 2 , 1 , - 1 ) , ( 2 , - 1 , 1 ) , ( 1 , 2 , - 1 ) , ( - 1 , 2 , 1 ) , ( 1 , - 1 , 2 ) , ( - 1 , 1 , 2 ) ]
428
+ {
429
+ _inspect_cell ( offset: cell_offset)
430
+ }
431
+ guard r2 > 2.75
432
+ else
433
+ {
434
+ return self . amplitude * r2
404
435
}
405
436
437
+ // Cell group:
438
+ // within r^2 = 3.0
439
+ // cumulative sample coverage = 100%
440
+ for cell_offset in [ ( 2 , 1 , 1 ) , ( 1 , 2 , 1 ) , ( 1 , 1 , 2 ) ]
441
+ {
442
+ _inspect_cell ( offset: cell_offset)
443
+ }
406
444
return self . amplitude * r2
407
445
}
408
446
0 commit comments