https://github.com/jonasmb/curlnoisejittering
Raw File
Tip revision: 42ba8b5ee132682b7804c6f47a7e1b91d6d73c9d authored by Jonàs Martínez on 26 February 2024, 12:21:49 UTC
Add Worley noise image 250x250 pixels
Tip revision: 42ba8b5
scnoise.py
import numpy as np
import matplotlib.pyplot as plt
import math
import time
from numba import jit, njit, float64, uint64, prange
import numba as nb

vector = nb.types.Array(dtype=float64, ndim=1, layout="C")
image = nb.types.Array(dtype=float64, ndim=3, layout="C")

@jit(vector(float64, float64, float64))
def vec3(x,y,z):
  return np.array((x,y,z))

@jit(vector(vector))
def normalize(v):
  len = math.sqrt(np.dot(v,v))
  return v/len

@jit(float64(vector))
def cubic(v):
  x = 1.0 - np.dot(v, v)*4.0
  return x*x*x if x > 0.0 else 0.0

@jit(vector(vector))
def cubic_grad(v):
  x = 1.0 - np.dot(v, v)*4.0
  return v*(x*x*(-24.0) if x > 0.0 else 0.0)

#sources = 30;
#a = 0.8*pow(sources, -1.0/3.0);
# Use the following if using impulse magnitudes in [0.5,1] to manage with fewer impulses

@jit(float64(vector))
def scnoise(p):
  sources = 10
  a = 0.5*pow(sources, -1.0/3.0)
  pi0 = np.floor(p - 0.5)
  result = 0.0
  for i in range(8):
    corner = np.mod(vec3(i, np.floor(i/2), np.floor(i/4)), 2)
    pi = pi0 + corner
    t = np.uint32(int(4*sources*(pi[0] + pi[1]*1000 + pi[2]*576 + pi[0]*pi[1]*pi[2]*3)))
    np.random.seed(t)
    n = np.random.random(size=(sources,4))
    for j in range(sources):
      #c = a*(n[j,0] - 0.5)
      # Use the following for c to manage with fewer impulses (e.g. sources = 10)
      c = n[j,0] - 0.5
      c = a*(np.copysign(0.5, c) + c)*0.5
      xi = vec3(n[j,1], n[j,2], n[j,3])
      x = pi + xi
      result += c*cubic(x - p)
  return result + 0.5

@jit(vector(vector))
def scnoise_grad(p):
  sources = 10
  a = 0.5*pow(sources, -1.0/3.0)
  pi0 = np.floor(p - 0.5)
  result = vec3(0.0, 0.0, 0.0)
  for i in range(8):
    corner = np.mod(vec3(i, np.floor(i/2), np.floor(i/4)), 2)
    pi = pi0 + corner
    t = np.uint32(int(4*sources*(pi[0] + pi[1]*1000 + pi[2]*576 + pi[0]*pi[1]*pi[2]*3)))
    np.random.seed(t)
    n = np.random.random(size=(sources,4))
    for j in range(sources):
      #c = a*(n[j,0] - 0.5)
      # Use the following for c to manage with fewer impulses (e.g. sources = 10)
      c = n[j,0] - 0.5
      c = a*(np.copysign(0.5, c) + c)*0.5
      xi = vec3(n[j,1], n[j,2], n[j,3])
      x = pi + xi
      result += c*cubic_grad(x - p)
  return result

@jit(vector(vector))
def scnoise_curl(p):
  sources = 10
  a = 0.5*pow(sources, -1.0/3.0)
  pi0 = np.floor(p - 0.5)
  g1 = vec3(0.0, 0.0, 0.0)
  g2 = vec3(0.0, 0.0, 0.0)
  g3 = vec3(0.0, 0.0, 0.0)
  for i in range(8):
    corner = vec3(np.bitwise_and(i, 1), np.bitwise_and(np.right_shift(i, 1), 1), np.bitwise_and(np.right_shift(i, 2), 1))
    pi = pi0 + corner
    t = np.uint32(int(6*sources*(pi[0] + pi[1]*1000 + pi[2]*576 + pi[0]*pi[1]*pi[2]*3)))
    np.random.seed(t)
    n = np.random.random(size=(sources,6))
    for j in range(sources):
      c = a*(vec3(n[j,0], n[j,1], n[j,2]) - 0.5)
      # Use the following for c to manage with fewer impulses (e.g. sources = 10)
      #c = n[j,0] - 0.5
      #c = a*(np.copysign(0.5, c) + c)*0.5
      xi = vec3(n[j,3], n[j,4], n[j,5])
      x = pi + xi
      g1 += c[0]*cubic_grad(x - p)
      g2 += c[1]*cubic_grad(x - p)
      g3 += c[2]*cubic_grad(x - p)
  return vec3(g3[1] - g2[2], g1[2] - g3[0], g2[0] - g1[1])

@jit(image(), nopython=True, parallel=False)
def render():
  noise_scale = 20.0
  width = 512
  height = 384
  aspect = width/height
  im = np.zeros(shape=(height, width, 3))
  z = -2.0
  for row in prange(height):
    for column in range(width):
      u = (column + 0.5)/width
      v = (row + 0.5)/height
      ray_direction = vec3(aspect*(2.0*u-1.0), 2.0*v-1.0, z)
      #im[row,column,:] = scnoise(ray_direction*noise_scale)
      #im[row,column,:] = scnoise_grad(ray_direction*noise_scale)*0.5 + 0.5
      im[row,column,:] = scnoise_curl(ray_direction*noise_scale)*0.5 + 0.5
      im[row,column,2] = 0.0
  return im

if __name__ == "__main__":
  t0 = time.perf_counter()
  im = render()
  t1 = time.perf_counter()
  print(t1 - t0)

  plt.imshow(im)
  plt.show()
back to top