nortikin / sverchok

Sverchok
http://nortikin.github.io/sverchok/
GNU General Public License v3.0
2.24k stars 232 forks source link

Experiments with an alternative turbulence function (spectral syntesis) #1232

Closed kalwalt closed 7 years ago

kalwalt commented 7 years ago

Hi to all, in these days, after restoring my pc, because i had a serious problem with both linux and windows partition, i had to reformatting both, :cry: , i come back to study Python and sverchok, and tryed to implement the turbulence function that came from the book "Texturing and modeling" chapter 2 p.86: This is the function in C code:

float
turbulence( point Q)
{
       float   value = 0;
       for ( f  = MINFREQ; f < MAXFREQ; f *= 2 )
            value += abs(snoise( Q * f) / f
       return value
}

where snoise is a signed noise in the range -1 and 1. The turbulence function use the spectral synthesis approach.

This is my implementation:


'''
turbulence script from texturing and modeling book ch.2 p.86, spectral syntesys approach.
Used only for testing pourpouse, trying to implement a turbulence function with rseed, because blender turbulence lack in this sense.
See the opensimplex version for the same implementation but with seed.
made by @kalwalt.
'''

from mathutils import Vector, noise

def sv_main(vec=[],octaves=1,amplitude=1.0,frequency=0.5,rseed=1):

    data = [[]]

    in_sockets = [
        ['v', 'vec', vec],
        ['s', 'octaves', octaves],
        ['s', 'amplitude', amplitude],
        ['s', 'frequency', frequency],
        ['s', 'Random seed', rseed]]

    out_sockets = [
        ['s', 'Float Data', data]
    ]

    def turbulence(vec,oct,freq,rseed):
        #we choose a noise type
        noise_type = noise.types.STDPERLIN
        #set the seed but won't work with blender noise
        noise.seed_set(rseed)
        value = 0.0
        for o in range(oct):

            freq *= 2.0
            vVec = Vector(vec)
            multVec = vVec * freq
            #print(multVec)
            #print(f)
            value += abs(noise.noise(multVec,noise_type))/freq 
            #value += (noise.noise(multVec,noise_type))/f
            #value += amplitude*(noise.noise(multVec,noise_type))

        return value

    #print('turbulence: {0}'.format(turbulence(vec,octaves,frequency,rseed))) 

    out = []

    if vec and vec[0]:
        for v in vec[0]:
            out = turbulence(v,octaves,frequency,rseed)
            data[0].append(out)
            #print(out)
            #print(len(data[0]))

    #print('data: {0}'.format(data))

    return in_sockets, out_sockets

the reason also to do this is to use opensimplex https://github.com/lmas/opensimplex it support seed, but output only float, in the next hours i will post an opensimplex version of the same script. i came to this because my pull_request https://github.com/nortikin/sverchok/pull/1188 but I don't know if this has sense it's most my own exercise...

Screenshot of the script: scrren-turbulence-spectral-synt

Will be fine for test purposes to have a viz for scalar values in a texture format, i think it's easier to render a texture than to render a geometry. I'll look into this when I will find a solution for it.

zeffii commented 7 years ago

sorry to hear about machine, good to have you back.

kalwalt commented 7 years ago

sorry to hear about machine, good to have you back.

Thank you @zeffii! A lot of lost time....

here some screenshots with opensimplex, with 1 octave: screen-turb-opensimplex-1oct

2 octaves: screen-turb-opensimplex-2oct

3 octaves: screen-turb-opensimplex-3oct

4 octaves: screen-turb-opensimplex-4oct

5 octaves: screen-turb-opensimplex-5oct

and seed is working! and very easy to setup see the code:


'''
turbulence script from texturing and modeling book ch.2 p.86, spectral syntesys approach.
Used only for testing pourpouse, trying to implement a turbulence function with rseed, because blender turbulence lack in this sense.
See the opensimplex version for the same implementation but with seed.
made by @kalwalt.
'''
import sys #change the path to your folder
sys.path.append('/home/walter/blender-2.78a-linux-glibc211-x86_64/2.78/scripts/addons/sverchok-master/node_scripts/templates')

from opensimplex import OpenSimplex
from mathutils import Vector

def sv_main(vec=[],octaves=1,amplitude=1.0,frequency=0.5,rseed=1):

    data = [[]]

    in_sockets = [
        ['v', 'vec', vec],
        ['s', 'octaves', octaves],
        ['s', 'amplitude', amplitude],
        ['s', 'frequency', frequency],
        ['s', 'Random seed', rseed]]

    out_sockets = [
        ['s', 'Float Data', data]
    ]

    osx= OpenSimplex(rseed)

    def turbulence(vec,oct,freq):

        value = 0.0

        for o in range(oct):

            freq *= 2.0
            vVec = Vector(vec)
            multVec = vVec * freq
            #print(multVec)
            #print(f)
            value += abs(osx.noise3d(multVec.x,multVec.y,multVec.z))/freq 
            #value += (noise.noise(multVec,noise_type))/f
            #value += amplitude*(noise.noise(multVec,noise_type))

        return value

    out = []

    if vec and vec[0]:
        for v in vec[0]:
            out = turbulence(v,octaves,frequency)
            data[0].append(out)
            #print(out)
            #print(len(data[0]))

    #print('data: {0}'.format(data))

    return in_sockets, out_sockets

Anyway OpenSimplex has only a 'flavour' implememtation of noise (simplex noise) instead the mathutils.noise has more flavours...

zeffii commented 7 years ago

a tip for you to speed up the appending to data[0] is to do

append = data[0].append

    if vec and vec[0]:
        for v in vec[0]:
            append(turbulence(v,octaves,frequency))

for this scenario.

kalwalt commented 7 years ago

@zeffii i will try it!

zeffii commented 7 years ago

or even...

extend = data[0].extend

    if vec and vec[0]:
        extend([turbulence(v,octaves,frequency) for v in vec[0]])
kalwalt commented 7 years ago

@zeffii thank's for the help!

kalwalt commented 7 years ago

@zeffii let open this issue i will post something more!

kalwalt commented 7 years ago

Tomorrow i will post the other (second) function on turbulence, haven't had the time these days, busy with texture viewer...

kalwalt commented 7 years ago

This is the second turbulence spectral sintesys function described in texturing and modeling:

//original function by texturing and modeling p. 87
float turbulence(point Q)
{      
        float value = 0;
        float cutoff = clamp(0.5/Qwidth, 0, MAXFREQ);
        float fade;

        for (f = MINFREQ; f > 0.5*cutoff; f*=2)
            value += abs(snoise(Q * f)/f;
        fade = clamp(2*(cutoff-f)/cutoff, 0, 1);
        value += fade * abs(snoise(Q * f)/f;
        return value;
}

this is the scripted version:


# Turbulence script from texturing and modeling book ch.2 p.86
# spectral syntesys approach. This the second function described.
# Used only for testing pourpouse, trying to implement a turbulence function
# with rseed, because blender turbulence lack in this sense.
# See the opensimplex version for the same implementation but with seed.
# made by @kalwalt

from mathutils import Vector, noise
# import numpy as np

# max(min(my_value, max_value), min_value)
def clamp(value_in, max_value, min_value):
    return max(min(value_in, max_value), min_value)

def frange(start, to, step):
    while start < to:
        yield start
        start += step

# frange2 should be more accurate
def frange2(x, y, jump=1.0):
    '''Range for floats.'''
    i = 0.0
    x = float(x)  # Prevent yielding integers.
    x0 = x
    epsilon = jump / 2.0
    yield x  # yield always first value
    while x + epsilon < y:
        i += 1.0
        x = x0 + i * jump
        yield x

def sv_main(vec=[], octaves=1, amplitude=0.8,
            frequency=0.8, Qwidth=0.1, rseed=2):

    data = [[]]

    in_sockets = [
        ['v', 'vec', vec],
        ['s', 'octaves', octaves],
        ['s', 'amplitude', amplitude],
        ['s', 'frequency', frequency],
        ['s', 'Random seed', rseed],
        ['s', 'Qwidth', Qwidth]

    ]

    out_sockets = [
        ['s', 'Float Data', data]
    ]

    def turbulence(vec, oct, freq, Qwidth):

        value = 0.0
        noise_type = noise.types.STDPERLIN
        fade = 0.0
        # tihs the vesion without numpy
        cutoff = clamp(0.5 / Qwidth, float(octaves), 0.0)
        # numpy.clip(a, a_min, a_max, out=None)[source]¶
        # cutoff = np.clip(0.5 / Qwidth, 0.0, float(octaves))
        for o in frange2(0.0, 0.5 * cutoff, 0.1):
            freq *= 2.0
            vVec = Vector(vec)
            multVec = vVec * freq
            value += abs(noise.noise(multVec, noise_type)) / freq
            fade = clamp(2.0 * (cutoff - freq) / cutoff, 0.0, 1.0)
            #fade = np.clip(2.0*(cutoff - freq)/cutoff, 1.0, 0.0)
            value += fade * abs(noise.noise(multVec, noise_type)) / freq
        return value

    append = data[0].append

    if vec and vec[0]:
        for v in vec[0]:
            append(turbulence(v, octaves, frequency, Qwidth))

    return in_sockets, out_sockets

turbulence_spectral_syntesis_clamped_original the frange and frange2 function are from: http://stackoverflow.com/questions/7267226/range-for-floats ( i would also trry with numpy.linspace, if i have time...) if you want try np.clip instead the clamp function just uncomment the few lines.

i have also an openSimplex version almost ready...

kalwalt commented 7 years ago

i have some doubt with my implementation, i mean i'm not 100% sure if the C++ function is correctly translated to python. In this sense a feedback will be really appreciated.

kalwalt commented 7 years ago

OpenSimplex version with rseed:

'''
turbulence script from texturing and modeling book ch.2 p.86
spectral syntesys approach.
This the second function described with OpenSimplex
Used only for testing pourpouse, trying to implement a turbulence function
with rseed, because blender turbulence lack in this sense.
made by @kalwalt.
'''
# adjiust the path to OpneSimplex folder and uncomment the lines
import sys
sys.path.append('/home/walter/blender-2.78a-linux-glibc211-x86_64/2.78/scripts/addons/sverchok-master/node_scripts/templates')

from opensimplex import OpenSimplex
from mathutils import Vector
import numpy as np

# max(min(my_value, max_value), min_value)
def clamp(value_in, max_value, min_value):
    return max(min(value_in, max_value), min_value)

def frange(start, to, step):
    while start < to:
        yield start
        start += step

# frange2 should be more accurate
def frange2(x, y, jump=1.0):
    '''Range for floats.'''
    i = 0.0
    x = float(x)  # Prevent yielding integers.
    x0 = x
    epsilon = jump / 2.0
    yield x  # yield always first value
    while x + epsilon < y:
        i += 1.0
        x = x0 + i * jump
        yield x

def sv_main(vec=[], octaves=1, amplitude=0.8,
            frequency=0.8, Qwidth=0.1, rseed=2):

    data = [[]]

    in_sockets = [
        ['v', 'vec', vec],
        ['s', 'octaves', octaves],
        ['s', 'amplitude', amplitude],
        ['s', 'frequency', frequency],
        ['s', 'Random seed', rseed],
        ['s', 'Qwidth', Qwidth]

    ]

    out_sockets = [
        ['s', 'Float Data', data]
    ]

    osx = OpenSimplex(int(rseed))

    def turbulence(vec, oct, freq, Qwidth):

        value = 0.0
        # cutoff = clamp(0.5/Qwidth, float(octaves),0.0)
        # numpy.clip(a, a_min, a_max, out=None)[source]¶
        cutoff = np.clip(0.5/Qwidth, 0.0, float(octaves))

        for o in frange(0.0, 0.5*cutoff, 0.1):

            freq *= 2.0
            vVec = Vector(vec)
            multVec = vVec * freq
            value += abs(osx.noise3d(multVec.x, multVec.y, multVec.z))/freq
            # fade = clamp(2.0*(cutoff-freq)/cutoff, 0.0, 1.0)
            fade = np.clip(2.0 * (cutoff - freq) / cutoff, 1.0, 0.0)
            value += fade * abs(osx.noise3d(multVec.x, multVec.y, multVec.z)) / freq

        return value

    append = data[0].append

    if vec and vec[0]:
        for v in vec[0]:
            append(turbulence(v, octaves, frequency, Qwidth))

    return in_sockets, out_sockets
zeffii commented 7 years ago

closed due to inactivity, feel free to reopen when appropriate