AI: add plasma, voronoi and perlin noise
This commit is contained in:
@@ -235,7 +235,10 @@ funcNoise:
|
||||
| GUMBELDIST LPAREN expr COMMA expr COMMA expr (COMMA expr)? RPAREN # GumbelDistFunc
|
||||
| WEIBULLDIST LPAREN expr COMMA expr COMMA expr (COMMA expr)? RPAREN # WeibullDistFunc
|
||||
| CHI2DIST LPAREN expr COMMA expr (COMMA expr)? RPAREN # Chi2DistFunc
|
||||
| STUDENTTDIST LPAREN expr COMMA expr (COMMA expr)? RPAREN # StudentTDistFunc;
|
||||
| STUDENTTDIST LPAREN expr COMMA expr (COMMA expr)? RPAREN # StudentTDistFunc
|
||||
| PERLIN LPAREN expr COMMA expr (COMMA expr)? (COMMA expr)? (COMMA expr)? RPAREN # PerlinFunc
|
||||
| CELLULAR LPAREN expr COMMA expr (COMMA expr)? (COMMA expr)? (COMMA expr)? RPAREN # CellularFunc
|
||||
| PLASMA LPAREN expr COMMA expr (COMMA expr)? (COMMA expr)? (COMMA expr)? RPAREN # PlasmaFunc;
|
||||
|
||||
// LEXER RULES
|
||||
|
||||
@@ -380,6 +383,9 @@ GUMBELDIST: 'randgumbel' | 'random_gumbel';
|
||||
WEIBULLDIST: 'randw' | 'random_weibull';
|
||||
CHI2DIST: 'randchi2' | 'random_chi2';
|
||||
STUDENTTDIST: 'randt' | 'random_studentt';
|
||||
PERLIN: 'perlin' | 'perlin_noise';
|
||||
CELLULAR: 'cellular' | 'voronoi' | 'worley' | 'cellular_noise';
|
||||
PLASMA: 'plasma' | 'turbulence' | 'plasma_noise';
|
||||
|
||||
PLUS: '+';
|
||||
MINUS: '-';
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -137,39 +137,42 @@ GUMBELDIST=136
|
||||
WEIBULLDIST=137
|
||||
CHI2DIST=138
|
||||
STUDENTTDIST=139
|
||||
PLUS=140
|
||||
MINUS=141
|
||||
MULT=142
|
||||
DIV=143
|
||||
MOD=144
|
||||
POW=145
|
||||
LSHIFT=146
|
||||
RSHIFT=147
|
||||
GE=148
|
||||
GT=149
|
||||
LE=150
|
||||
LT=151
|
||||
EQ=152
|
||||
EQUEALS=153
|
||||
NE=154
|
||||
PIPE=155
|
||||
LPAREN=156
|
||||
RPAREN=157
|
||||
COMMA=158
|
||||
SEMICOLON=159
|
||||
ARROW=160
|
||||
LBRACKET=161
|
||||
RBRACKET=162
|
||||
QUESTION=163
|
||||
COLON=164
|
||||
LBRACE=165
|
||||
RBRACE=166
|
||||
NUMBER=167
|
||||
CONSTANT=168
|
||||
VARIABLE=169
|
||||
SL_COMMENT=170
|
||||
ML_COMMENT=171
|
||||
WS=172
|
||||
PERLIN=140
|
||||
CELLULAR=141
|
||||
PLASMA=142
|
||||
PLUS=143
|
||||
MINUS=144
|
||||
MULT=145
|
||||
DIV=146
|
||||
MOD=147
|
||||
POW=148
|
||||
LSHIFT=149
|
||||
RSHIFT=150
|
||||
GE=151
|
||||
GT=152
|
||||
LE=153
|
||||
LT=154
|
||||
EQ=155
|
||||
EQUEALS=156
|
||||
NE=157
|
||||
PIPE=158
|
||||
LPAREN=159
|
||||
RPAREN=160
|
||||
COMMA=161
|
||||
SEMICOLON=162
|
||||
ARROW=163
|
||||
LBRACKET=164
|
||||
RBRACKET=165
|
||||
QUESTION=166
|
||||
COLON=167
|
||||
LBRACE=168
|
||||
RBRACE=169
|
||||
NUMBER=170
|
||||
CONSTANT=171
|
||||
VARIABLE=172
|
||||
SL_COMMENT=173
|
||||
ML_COMMENT=174
|
||||
WS=175
|
||||
'sin'=1
|
||||
'cos'=2
|
||||
'tan'=3
|
||||
@@ -272,30 +275,30 @@ WS=172
|
||||
'cov'=123
|
||||
'crop'=124
|
||||
'none'=125
|
||||
'+'=140
|
||||
'-'=141
|
||||
'*'=142
|
||||
'/'=143
|
||||
'%'=144
|
||||
'^'=145
|
||||
'<<'=146
|
||||
'>>'=147
|
||||
'>='=148
|
||||
'>'=149
|
||||
'<='=150
|
||||
'<'=151
|
||||
'=='=152
|
||||
'='=153
|
||||
'!='=154
|
||||
'|'=155
|
||||
'('=156
|
||||
')'=157
|
||||
','=158
|
||||
';'=159
|
||||
'->'=160
|
||||
'['=161
|
||||
']'=162
|
||||
'?'=163
|
||||
':'=164
|
||||
'{'=165
|
||||
'}'=166
|
||||
'+'=143
|
||||
'-'=144
|
||||
'*'=145
|
||||
'/'=146
|
||||
'%'=147
|
||||
'^'=148
|
||||
'<<'=149
|
||||
'>>'=150
|
||||
'>='=151
|
||||
'>'=152
|
||||
'<='=153
|
||||
'<'=154
|
||||
'=='=155
|
||||
'='=156
|
||||
'!='=157
|
||||
'|'=158
|
||||
'('=159
|
||||
')'=160
|
||||
','=161
|
||||
';'=162
|
||||
'->'=163
|
||||
'['=164
|
||||
']'=165
|
||||
'?'=166
|
||||
':'=167
|
||||
'{'=168
|
||||
'}'=169
|
||||
|
||||
File diff suppressed because one or more lines are too long
+743
-701
File diff suppressed because it is too large
Load Diff
@@ -137,39 +137,42 @@ GUMBELDIST=136
|
||||
WEIBULLDIST=137
|
||||
CHI2DIST=138
|
||||
STUDENTTDIST=139
|
||||
PLUS=140
|
||||
MINUS=141
|
||||
MULT=142
|
||||
DIV=143
|
||||
MOD=144
|
||||
POW=145
|
||||
LSHIFT=146
|
||||
RSHIFT=147
|
||||
GE=148
|
||||
GT=149
|
||||
LE=150
|
||||
LT=151
|
||||
EQ=152
|
||||
EQUEALS=153
|
||||
NE=154
|
||||
PIPE=155
|
||||
LPAREN=156
|
||||
RPAREN=157
|
||||
COMMA=158
|
||||
SEMICOLON=159
|
||||
ARROW=160
|
||||
LBRACKET=161
|
||||
RBRACKET=162
|
||||
QUESTION=163
|
||||
COLON=164
|
||||
LBRACE=165
|
||||
RBRACE=166
|
||||
NUMBER=167
|
||||
CONSTANT=168
|
||||
VARIABLE=169
|
||||
SL_COMMENT=170
|
||||
ML_COMMENT=171
|
||||
WS=172
|
||||
PERLIN=140
|
||||
CELLULAR=141
|
||||
PLASMA=142
|
||||
PLUS=143
|
||||
MINUS=144
|
||||
MULT=145
|
||||
DIV=146
|
||||
MOD=147
|
||||
POW=148
|
||||
LSHIFT=149
|
||||
RSHIFT=150
|
||||
GE=151
|
||||
GT=152
|
||||
LE=153
|
||||
LT=154
|
||||
EQ=155
|
||||
EQUEALS=156
|
||||
NE=157
|
||||
PIPE=158
|
||||
LPAREN=159
|
||||
RPAREN=160
|
||||
COMMA=161
|
||||
SEMICOLON=162
|
||||
ARROW=163
|
||||
LBRACKET=164
|
||||
RBRACKET=165
|
||||
QUESTION=166
|
||||
COLON=167
|
||||
LBRACE=168
|
||||
RBRACE=169
|
||||
NUMBER=170
|
||||
CONSTANT=171
|
||||
VARIABLE=172
|
||||
SL_COMMENT=173
|
||||
ML_COMMENT=174
|
||||
WS=175
|
||||
'sin'=1
|
||||
'cos'=2
|
||||
'tan'=3
|
||||
@@ -272,30 +275,30 @@ WS=172
|
||||
'cov'=123
|
||||
'crop'=124
|
||||
'none'=125
|
||||
'+'=140
|
||||
'-'=141
|
||||
'*'=142
|
||||
'/'=143
|
||||
'%'=144
|
||||
'^'=145
|
||||
'<<'=146
|
||||
'>>'=147
|
||||
'>='=148
|
||||
'>'=149
|
||||
'<='=150
|
||||
'<'=151
|
||||
'=='=152
|
||||
'='=153
|
||||
'!='=154
|
||||
'|'=155
|
||||
'('=156
|
||||
')'=157
|
||||
','=158
|
||||
';'=159
|
||||
'->'=160
|
||||
'['=161
|
||||
']'=162
|
||||
'?'=163
|
||||
':'=164
|
||||
'{'=165
|
||||
'}'=166
|
||||
'+'=143
|
||||
'-'=144
|
||||
'*'=145
|
||||
'/'=146
|
||||
'%'=147
|
||||
'^'=148
|
||||
'<<'=149
|
||||
'>>'=150
|
||||
'>='=151
|
||||
'>'=152
|
||||
'<='=153
|
||||
'<'=154
|
||||
'=='=155
|
||||
'='=156
|
||||
'!='=157
|
||||
'|'=158
|
||||
'('=159
|
||||
')'=160
|
||||
','=161
|
||||
';'=162
|
||||
'->'=163
|
||||
'['=164
|
||||
']'=165
|
||||
'?'=166
|
||||
':'=167
|
||||
'{'=168
|
||||
'}'=169
|
||||
|
||||
+703
-442
File diff suppressed because it is too large
Load Diff
@@ -979,5 +979,20 @@ class MathExprVisitor(ParseTreeVisitor):
|
||||
return self.visitChildren(ctx)
|
||||
|
||||
|
||||
# Visit a parse tree produced by MathExprParser#PerlinFunc.
|
||||
def visitPerlinFunc(self, ctx:MathExprParser.PerlinFuncContext):
|
||||
return self.visitChildren(ctx)
|
||||
|
||||
|
||||
# Visit a parse tree produced by MathExprParser#CellularFunc.
|
||||
def visitCellularFunc(self, ctx:MathExprParser.CellularFuncContext):
|
||||
return self.visitChildren(ctx)
|
||||
|
||||
|
||||
# Visit a parse tree produced by MathExprParser#PlasmaFunc.
|
||||
def visitPlasmaFunc(self, ctx:MathExprParser.PlasmaFuncContext):
|
||||
return self.visitChildren(ctx)
|
||||
|
||||
|
||||
|
||||
del MathExprParser
|
||||
@@ -2565,3 +2565,207 @@ class UnifiedMathVisitor(MathExprVisitor):
|
||||
|
||||
# Fallback for other types
|
||||
return int(a) >> b_int
|
||||
|
||||
def visitPerlinFunc(self, ctx):
|
||||
"""perlin(seed, scale, [octaves], [offset], [shape])
|
||||
Perlin noise with smooth gradients - supports arbitrary dimensions.
|
||||
"""
|
||||
seed_val = yield ctx.expr(0)
|
||||
seed = int(seed_val.item()) if self._is_tensor(seed_val) else int(seed_val)
|
||||
|
||||
scale_val = yield ctx.expr(1)
|
||||
scale = float(scale_val.item()) if self._is_tensor(scale_val) else float(scale_val)
|
||||
|
||||
octaves = 1
|
||||
expr_idx = 2
|
||||
if len(ctx.expr()) > expr_idx:
|
||||
oct_val = yield ctx.expr(expr_idx)
|
||||
octaves = int(oct_val.item()) if self._is_tensor(oct_val) else int(oct_val)
|
||||
expr_idx += 1
|
||||
|
||||
offset = None
|
||||
if len(ctx.expr()) > expr_idx:
|
||||
offset_val = yield ctx.expr(expr_idx)
|
||||
offset = offset_val
|
||||
expr_idx += 1
|
||||
|
||||
# Optional shape parameter
|
||||
shape = self.shape
|
||||
if len(ctx.expr()) > expr_idx:
|
||||
shape_arg = (yield ctx.expr(expr_idx))
|
||||
if self._is_tensor(shape_arg):
|
||||
shape = tuple(shape_arg.long().flatten().tolist())
|
||||
elif self._is_list(shape_arg):
|
||||
shape = tuple(int(x) for x in shape_arg)
|
||||
else:
|
||||
shape = (int(shape_arg),)
|
||||
|
||||
if len(shape) == 0:
|
||||
return torch.tensor(0.0, device=self.device)
|
||||
|
||||
offset_list = None
|
||||
if offset is not None:
|
||||
if self._is_tensor(offset):
|
||||
offset_list = [float(x) for x in offset.flatten().tolist()]
|
||||
elif self._is_list(offset):
|
||||
offset_list = [float(x) for x in offset]
|
||||
else:
|
||||
offset_list = [float(offset)]
|
||||
|
||||
grids = torch.meshgrid(
|
||||
*[
|
||||
torch.arange(s, dtype=torch.float32, device=self.device)
|
||||
+ (offset_list[i] if offset_list is not None and i < len(offset_list) else 0.0)
|
||||
for i, s in enumerate(shape)
|
||||
],
|
||||
indexing='ij'
|
||||
)
|
||||
|
||||
noise = NoiseUtils.perlin_noise_nd(grids, scale, seed, self.device)
|
||||
|
||||
if octaves > 1:
|
||||
result = noise
|
||||
amplitude = 0.5
|
||||
frequency = 2.0
|
||||
for oct in range(octaves - 1):
|
||||
scaled_grids = tuple(g * frequency for g in grids)
|
||||
octave_noise = NoiseUtils.perlin_noise_nd(scaled_grids, scale / frequency, seed + oct, self.device)
|
||||
result = result + octave_noise * amplitude
|
||||
amplitude *= 0.5
|
||||
frequency *= 2.0
|
||||
noise = result / (2 - 2**(-octaves))
|
||||
|
||||
return noise
|
||||
|
||||
def visitCellularFunc(self, ctx):
|
||||
"""cellular(seed, scale, [jitter], [offset], [shape])
|
||||
Cellular/Voronoi noise - supports arbitrary dimensions.
|
||||
"""
|
||||
seed_val = yield ctx.expr(0)
|
||||
seed = int(seed_val.item()) if self._is_tensor(seed_val) else int(seed_val)
|
||||
|
||||
scale_val = yield ctx.expr(1)
|
||||
scale = float(scale_val.item()) if self._is_tensor(scale_val) else float(scale_val)
|
||||
|
||||
jitter = 0.5
|
||||
expr_idx = 2
|
||||
if len(ctx.expr()) > expr_idx:
|
||||
jitter_val = yield ctx.expr(expr_idx)
|
||||
jitter = float(jitter_val.item()) if self._is_tensor(jitter_val) else float(jitter_val)
|
||||
jitter = max(0.0, min(1.0, jitter))
|
||||
expr_idx += 1
|
||||
|
||||
offset = None
|
||||
if len(ctx.expr()) > expr_idx:
|
||||
offset_val = yield ctx.expr(expr_idx)
|
||||
offset = offset_val
|
||||
expr_idx += 1
|
||||
|
||||
# Optional shape parameter
|
||||
shape = self.shape
|
||||
if len(ctx.expr()) > expr_idx:
|
||||
shape_arg = (yield ctx.expr(expr_idx))
|
||||
if self._is_tensor(shape_arg):
|
||||
shape = tuple(shape_arg.long().flatten().tolist())
|
||||
elif self._is_list(shape_arg):
|
||||
shape = tuple(int(x) for x in shape_arg)
|
||||
else:
|
||||
shape = (int(shape_arg),)
|
||||
|
||||
if len(shape) == 0:
|
||||
return torch.tensor(0.0, device=self.device)
|
||||
|
||||
offset_list = None
|
||||
if offset is not None:
|
||||
if self._is_tensor(offset):
|
||||
offset_list = [float(x) for x in offset.flatten().tolist()]
|
||||
elif self._is_list(offset):
|
||||
offset_list = [float(x) for x in offset]
|
||||
else:
|
||||
offset_list = [float(offset)]
|
||||
|
||||
grids = torch.meshgrid(
|
||||
*[
|
||||
torch.arange(s, dtype=torch.float32, device=self.device)
|
||||
+ (offset_list[i] if offset_list is not None and i < len(offset_list) else 0.0)
|
||||
for i, s in enumerate(shape)
|
||||
],
|
||||
indexing='ij'
|
||||
)
|
||||
|
||||
noise = NoiseUtils.cellular_noise_nd(grids, scale, jitter, seed, self.device)
|
||||
return noise
|
||||
|
||||
def visitPlasmaFunc(self, ctx):
|
||||
"""plasma(seed, scale, [octaves], [offset], [shape])
|
||||
Plasma/Turbulence noise - chaotic high-frequency patterns.
|
||||
"""
|
||||
seed_val = yield ctx.expr(0)
|
||||
seed = int(seed_val.item()) if self._is_tensor(seed_val) else int(seed_val)
|
||||
|
||||
scale_val = yield ctx.expr(1)
|
||||
scale = float(scale_val.item()) if self._is_tensor(scale_val) else float(scale_val)
|
||||
|
||||
octaves = 1
|
||||
expr_idx = 2
|
||||
if len(ctx.expr()) > expr_idx:
|
||||
oct_val = yield ctx.expr(expr_idx)
|
||||
octaves = int(oct_val.item()) if self._is_tensor(oct_val) else int(oct_val)
|
||||
expr_idx += 1
|
||||
|
||||
offset = None
|
||||
if len(ctx.expr()) > expr_idx:
|
||||
offset_val = yield ctx.expr(expr_idx)
|
||||
offset = offset_val
|
||||
expr_idx += 1
|
||||
|
||||
# Optional shape parameter
|
||||
shape = self.shape
|
||||
if len(ctx.expr()) > expr_idx:
|
||||
shape_arg = (yield ctx.expr(expr_idx))
|
||||
if self._is_tensor(shape_arg):
|
||||
shape = tuple(shape_arg.long().flatten().tolist())
|
||||
elif self._is_list(shape_arg):
|
||||
shape = tuple(int(x) for x in shape_arg)
|
||||
else:
|
||||
shape = (int(shape_arg),)
|
||||
|
||||
if len(shape) == 0:
|
||||
return torch.tensor(0.0, device=self.device)
|
||||
|
||||
offset_list = None
|
||||
if offset is not None:
|
||||
if self._is_tensor(offset):
|
||||
offset_list = [float(x) for x in offset.flatten().tolist()]
|
||||
elif self._is_list(offset):
|
||||
offset_list = [float(x) for x in offset]
|
||||
else:
|
||||
offset_list = [float(offset)]
|
||||
|
||||
grids = torch.meshgrid(
|
||||
*[
|
||||
torch.arange(s, dtype=torch.float32, device=self.device)
|
||||
+ (offset_list[i] if offset_list is not None and i < len(offset_list) else 0.0)
|
||||
for i, s in enumerate(shape)
|
||||
],
|
||||
indexing='ij'
|
||||
)
|
||||
|
||||
# Call perlin_noise_nd with all coordinate grids
|
||||
noise = NoiseUtils.plasma_noise_nd(grids, scale, seed, self.device)
|
||||
|
||||
# Apply octaves (fBm-like composition)
|
||||
if octaves > 1:
|
||||
result = noise
|
||||
amplitude = 0.5
|
||||
frequency = 2.0
|
||||
for oct in range(octaves - 1):
|
||||
scaled_grids = tuple(g * frequency for g in grids)
|
||||
octave_noise = NoiseUtils.plasma_noise_nd(scaled_grids, scale / frequency, seed + oct, self.device)
|
||||
result = result + octave_noise * amplitude
|
||||
amplitude *= 0.5
|
||||
frequency *= 2.0
|
||||
noise = result / (2 - 2**(-octaves))
|
||||
|
||||
return noise
|
||||
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
"""
|
||||
GPU-accelerated noise generation utilities for arbitrary ND tensors.
|
||||
Pure PyTorch implementation with deterministic seeding.
|
||||
True Perlin noise with gradient interpolation - arbitrary dimensions.
|
||||
"""
|
||||
|
||||
import torch
|
||||
|
||||
|
||||
class NoiseUtils:
|
||||
"""GPU-accelerated Perlin noise generation with support for arbitrary dimensions."""
|
||||
|
||||
@staticmethod
|
||||
def _hash_nd(coords, seed=0):
|
||||
"""
|
||||
Hash function for N-dimensional coordinates.
|
||||
coords: tuple or list of coordinate tensors
|
||||
Returns: hash value in [0, 1)
|
||||
"""
|
||||
h = seed
|
||||
# Combine all coordinates into hash
|
||||
for i, coord in enumerate(coords):
|
||||
prime = [73856093, 19349663, 83492791, 39916801, 56962349, 76978801][i % 6]
|
||||
h ^= (torch.floor(coord).long() * prime)
|
||||
|
||||
h = h ^ (h >> 13)
|
||||
h = (h * 377119761) & 0x7fffffff
|
||||
return (h.float() / 0x7fffffff).clamp(0, 0.9999)
|
||||
|
||||
@staticmethod
|
||||
def _fade(t):
|
||||
"""Perlin fade curve: 6t^5 - 15t^4 + 10t^3"""
|
||||
return t**3 * (t * (t * 6 - 15) + 10)
|
||||
|
||||
@staticmethod
|
||||
def _build_grid_samples(coords_frac, coords_floor):
|
||||
"""
|
||||
Build all 2^n corner samples for n-dimensional Perlin noise.
|
||||
coords_frac: tuple of fractional parts (for fading)
|
||||
coords_floor: tuple of integer parts (for grid corners)
|
||||
Returns: list of (corner_coords, fade_values) tuples
|
||||
"""
|
||||
ndim = len(coords_floor)
|
||||
samples = []
|
||||
|
||||
# Generate all 2^ndim corners
|
||||
for i in range(1 << ndim):
|
||||
corner_coords = []
|
||||
for d in range(ndim):
|
||||
# Extract bit d from i to determine if we add 1 to this dimension
|
||||
if (i >> d) & 1:
|
||||
corner_coords.append(coords_floor[d] + 1)
|
||||
else:
|
||||
corner_coords.append(coords_floor[d])
|
||||
samples.append(tuple(corner_coords))
|
||||
|
||||
return samples
|
||||
|
||||
@staticmethod
|
||||
def perlin_noise_nd(coords_input, scale=1.0, seed=0, device=None):
|
||||
"""
|
||||
N-dimensional Perlin noise with proper gradient interpolation.
|
||||
coords_input: tuple of coordinate tensors with same shape
|
||||
scale: frequency scale
|
||||
seed: random seed
|
||||
Returns: noise tensor in same shape as input coordinates
|
||||
"""
|
||||
ndim = len(coords_input)
|
||||
|
||||
# Scale coordinates
|
||||
coords = tuple(c / scale for c in coords_input)
|
||||
|
||||
# Split into integer and fractional parts
|
||||
coords_floor = tuple(torch.floor(c).long() for c in coords)
|
||||
coords_frac = tuple(c - torch.floor(c) for c in coords)
|
||||
|
||||
# Fade curves for each dimension
|
||||
fades = tuple(NoiseUtils._fade(f) for f in coords_frac)
|
||||
|
||||
# Accumulate weighted corner contributions
|
||||
result = torch.zeros_like(coords_input[0])
|
||||
prime_seeds = [1013, 1619, 3137, 5021, 7919, 10427]
|
||||
for corner_idx in range(1 << ndim):
|
||||
corner_offsets = tuple((corner_idx >> d) & 1 for d in range(ndim))
|
||||
corner_coords = tuple(coords_floor[d] + corner_offsets[d] for d in range(ndim))
|
||||
|
||||
grad_components = []
|
||||
for d in range(ndim):
|
||||
h = NoiseUtils._hash_nd(corner_coords, seed + prime_seeds[d % len(prime_seeds)] * (d + 1))
|
||||
grad_components.append(h * 2.0 - 1.0)
|
||||
grad = torch.stack(grad_components, dim=0)
|
||||
grad_norm = torch.linalg.norm(grad, dim=0, keepdim=True)
|
||||
grad = grad / torch.where(grad_norm == 0, torch.ones_like(grad_norm), grad_norm)
|
||||
|
||||
dist_components = [coords_frac[d] - corner_offsets[d] for d in range(ndim)]
|
||||
dist = torch.stack(dist_components, dim=0)
|
||||
dot = torch.sum(grad * dist, dim=0)
|
||||
|
||||
weight = 1.0
|
||||
for d in range(ndim):
|
||||
if corner_offsets[d]:
|
||||
weight = weight * fades[d]
|
||||
else:
|
||||
weight = weight * (1 - fades[d])
|
||||
|
||||
result = result + dot * weight
|
||||
|
||||
return torch.clamp(result, -1, 1)
|
||||
|
||||
@staticmethod
|
||||
def cellular_noise_nd(coords_input, scale=1.0, jitter=0.5, seed=0, device=None):
|
||||
"""
|
||||
N-dimensional Cellular (Voronoi) noise.
|
||||
coords_input: tuple of coordinate tensors
|
||||
Returns: distance to nearest feature point in [0, 1]
|
||||
"""
|
||||
ndim = len(coords_input)
|
||||
|
||||
# Scale coordinates
|
||||
coords = tuple(c / scale for c in coords_input)
|
||||
|
||||
# Integer grid cell and position within cell
|
||||
coords_cell = tuple(torch.floor(c).long() for c in coords)
|
||||
coords_frac = tuple(c - torch.floor(c) for c in coords)
|
||||
|
||||
# Search neighborhood (3^ndim cells)
|
||||
min_dist = torch.full_like(coords[0], float('inf'))
|
||||
|
||||
def generate_offsets(d):
|
||||
"""Generate all offsets for neighborhood search"""
|
||||
if d == 0:
|
||||
return [[]]
|
||||
return [[i] + offset for i in [-1, 0, 1] for offset in generate_offsets(d - 1)]
|
||||
|
||||
offsets = generate_offsets(ndim)
|
||||
|
||||
for offset in offsets:
|
||||
# Neighbor cell coordinates
|
||||
neighbor_coords = tuple(coords_cell[i] + offset[i] for i in range(ndim))
|
||||
|
||||
# Hash-based feature point in neighbor cell
|
||||
hash_vals = []
|
||||
for seed_mult in range(ndim):
|
||||
h = NoiseUtils._hash_nd(neighbor_coords, seed * (seed_mult + 2))
|
||||
hash_vals.append(h * jitter)
|
||||
|
||||
# Feature point position (cell + jitter)
|
||||
feature_pos = tuple(
|
||||
offset[i] + hash_vals[i]
|
||||
for i in range(ndim)
|
||||
)
|
||||
|
||||
# Distance to feature point
|
||||
dist_sq = sum((coords_frac[i] - feature_pos[i])**2 for i in range(ndim))
|
||||
dist = torch.sqrt(dist_sq)
|
||||
|
||||
min_dist = torch.minimum(min_dist, dist)
|
||||
|
||||
return min_dist+0.5
|
||||
|
||||
@staticmethod
|
||||
def plasma_noise_nd(coords_input, scale=1.0, seed=0, device=None):
|
||||
"""
|
||||
N-dimensional Plasma/Turbulence noise - high-frequency chaotic patterns.
|
||||
Uses interpolated noise for smooth results.
|
||||
coords_input: tuple of coordinate tensors
|
||||
"""
|
||||
ndim = len(coords_input)
|
||||
result = torch.zeros_like(coords_input[0])
|
||||
amplitude = 1.0
|
||||
frequency = 1.0
|
||||
max_amplitude = 0.0
|
||||
|
||||
for octave in range(4):
|
||||
# Scale coordinates by frequency and base scale
|
||||
scaled_coords = tuple((c / scale) * frequency for c in coords_input)
|
||||
|
||||
# Use Perlin-like interpolation for smooth plasma
|
||||
noise_octave = NoiseUtils.perlin_noise_nd(
|
||||
tuple((c / scale) * frequency for c in coords_input),
|
||||
1.0, # scale already applied above
|
||||
seed + octave * 1000,
|
||||
device
|
||||
)
|
||||
|
||||
# Add this octave
|
||||
result = result + noise_octave * amplitude
|
||||
max_amplitude += amplitude
|
||||
|
||||
# Update for next octave
|
||||
amplitude *= 0.5
|
||||
frequency *= 2.0
|
||||
|
||||
result = result / max_amplitude
|
||||
return (result*2)+0.5
|
||||
Reference in New Issue
Block a user