Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Noise ?

in Codea Craft Posts: 616

Hi All,

I have dabbled with noise - Perlin, simplex etc and used a few methods to design heightmap. With the arrival of Craft the noise functions have been significantly improved with lots of options to play with. Another star for Craft. But, unfortunately I’m not a mathematician and my early attempts are very crude. What I’m looking for is how to use coherent noise - example calls and the noisegradients you can generate.

Anyone got any ideas ?

Comments

  • dave1707dave1707 Mod
    Posts: 6,325

    @Bri_G The different noise functions just return different values for the same input values. The example below shows the same input x,y,z values for 3 different noise functions. The output results from them are different. To see a better representation, a 3 dimensional grid should be used.

    function setup()
        x,y,z=.1,.2,.3
    
        n = craft.noise.perlin()
        value = n:getValue(x,y,z)
        print(value)
    
        n = craft.noise.rigidMulti()
        value = n:getValue(x,y,z)
        print(value)
    
        n = craft.noise.billow()
        value = n:getValue(x,y,z)
        print(value)
    end
    
  • dave1707dave1707 Mod
    edited November 13 Posts: 6,325

    @Bri_G I thru together this 3D graph of some noise functions. When you run this, it takes a few seconds before the graph displays. To see the different noise results, uncomment each noise function in the for loops. The grid calculations are from x -1 To 1, y -1 To 1, and z -1 to 1 in steps of .2 . The y value is replaced with the calculation from the noise function to show the different noise values.

    I deleted the code because of some mistakes I made.
    An updated version is shown below.
    
  • Posts: 616

    @dave1707 - thanks for that. Tried those out but ran into a problem when I tried to use craft.noise.constant() which threw up an error even with a value in the parentheses a la reference. Tried n:getValue(x,y,z) as against n(x,y,z) and the output noise.constant printed.

    Being naive I thought noise was just another random function and expected to get random output. But that’s not the case - you get the same output every time from the same input. They are just mathematical functions that give different output from different input, but reproducible. Just need to bend them to our needs.

    Trying to apply the features of lib.noise a la their website

    Libnoise

    What I’m trying to get my head round is coherent-noise.

  • dave1707dave1707 Mod
    edited November 12 Posts: 6,325

    @Bri_G From what I know about coherent noise is:

    1. It returns the same output for the same input.
    2. A small input change results in a small output change
    3. A large input change results in a different output change.

    It’s like playing a CD. If you play the same CD, you get the same songs. If you turn up the volume a little, all the songs are the same but a little louder. If you turn up the volume a lot, it’s the same songs but distorted. Might not be a good example.

  • dave1707dave1707 Mod
    edited November 13 Posts: 6,325

    Here’s an updated 3D graphic of some Craft noise functions. Change the noise slider, then press calc to show the noise results.

    Removed the code.
    See the updated version below.
    
  • edited November 13 Posts: 616
    @dave1707 - pretty awesome and prime candidate for a Craft example. Could improve slightly if you can switch off the ground or make it transparent then viewed from above you can see the 2 dimensional spread of the data ie a rectangle.
  • dave1707dave1707 Mod
    Posts: 6,325

    @Bri_G I have other changes I’m going to make to the code, so I can add a switch that shows/hides the ground.

  • dave1707dave1707 Mod
    Posts: 6,325

    @Bri_G I added your suggestion in the code below. There’s a slider to show/hide the ground. I also added a slider to allow the noise function to use a value other than 0 for the y axis. That way you can see the differences with different starting y values.

    supportedOrientations(LANDSCAPE_ANY)
    
    function setup()
        assert(craft, "Please include Craft as a dependency")
        assert(OrbitViewer, "Please include Cameras (not Camera) as a dependency")
        name={"Gradient","Perlin","Billow","Const","RigidMulti"}
        nTab={craft.noise.gradient(),craft.noise.perlin(),
            craft.noise.billow(),craft.noise.const(),craft.noise.rigidMulti()}
        tab={}
    
        fill(255, 0, 0, 255)
        scene = craft.scene()
        skyMaterial=scene.sky.material
        skyMaterial.sky=color(0, 62, 255, 255)
        skyMaterial.horizon=color(99, 255, 0, 255)
        scene.sun.rotation=quat.eulerAngles(20,45,-30)
        v=scene.camera:add(OrbitViewer, vec3(0,0,0), 30, 0, 200)
        v.rx=10
        v.ry=20
        parameter.integer("noise",1,#nTab,1)
        parameter.number("yVal",-1,2,0)
        parameter.action("calc",calc)
        parameter.boolean("hide ground",createGround)
        calc()
    end
    
    function createSphere(x,y,z)
        size=.12
        sphere1=scene:entity()
        s1=sphere1:add(craft.rigidbody,STATIC)
        sphere1.position=vec3(x,y,z)
        sphere1:add(craft.shape.sphere,size)
        sphere1.model = craft.model.icosphere(size,1)
        sphere1.material = craft.material("Materials:Specular")
        sphere1.material.diffuse=color(255,0,0)
        table.insert(tab,sphere1)
    end
    
    function draw()
        update(DeltaTime)
        scene:draw()    
        text("Slide your finger to rotate image",WIDTH/2,HEIGHT-25)
        text("Values from x =-1.4 to 1.4  and  z =-1.4 to 1.4    step size = .05",WIDTH/2,HEIGHT-50)
        text("y = "..yVal,WIDTH/2,HEIGHT-75)
        text("Noise function   =  "..name[noise],WIDTH/2,HEIGHT-100)
    end
    
    function update(dt)
        scene:update(dt)
    end
    
    function createGround()
        if hide_ground and ground then
            ground:destroy()
            ground=nil
        elseif not ground then
            ground = scene:entity()
            ground.model = craft.model.cube(vec3(15,.05,15))
            ground.material = craft.material("Materials:Specular")
            ground.material.map = readImage("Surfaces:Desert Cliff Roughness") 
        end
    end
    
    function calc()
        output.clear()
        print("Calculating... please wait.")
        for z=1,#tab do
            tab[z]:destroy()
        end
        collectgarbage()
        tab={}
        step=.05
        n=nTab[noise]
        for x=-1.4,1.4, step do
            for z=-1.4,1.4,step do
                value = n:getValue(x,yVal,z)
                createSphere(x*5,value*5,z*5)
            end
        end
        output.clear()
        print("Change noise for another noise function then press calc.")
    end
    
  • Posts: 616
    @dave1707 - that's neat, gives the user the chance to modify the data and see it from all angles. Nice coding.
  • Posts: 616

    @John

    You may be able to answer this - regarding libnoise. In the Tutorial series, tutorial 3 shows the generation of a greyscale tile. It also shows a graph of the 'bounding rectangle' used for the generation. I can generate grey scale tiles without problem but and, as in a later tutorial, can produce tiles which extend the first tile matched at the interface.

    However the tutorial series suggested that the tiles will wrap so I was hoping the far right face on the second tile would wrap to match the first tile left face and bottom wraps with top faces. But that is not the case.

    Could you explain what the dimensions used in the graphs are - they seem out of proportion to the scale of the images (ie 2 to 6 versus 1 to 256).

    Also I am assuming I will have to write some blending routines too get the desired face overlaps - is that the case. You have already written some in the Planet generator example for Craft so I will base my routines on yours.

    Could you also advise on the x,y,z parameter ranges for the calls to the noiselib. The tutorial looks like the x and z parameters are used - if so what is the y parameter. I've just fiddled with them to generate my maps and they seem to need low (<0.01) numbers.

    Finally, some of the images I have generated seem to have colours in strange places. I have only used positive numbers in my coding - it looks like the craft.noise.perlin(x,y,z) call returns numbers between -1 and +1, is that the case. If so my code will need some adjustment.

    By the way figured out the skybox now I can use it with obj models - looks awesome. Will post a vid later.

  • dave1707dave1707 Mod
    edited November 15 Posts: 6,325

    @Bri_G I went to the libnoise page and thought I’d write this. Here’s the prelim noise from -1 To 1 in steps of .0075. I’m changing the color based on a range of the results from the perlin noise function. Slide your finger up or down to change the z value into the perlin noise. When you lift your finger, it takes a few seconds to recalculate a new image.

    Code removed.
    An updated version is below.
    
  • edited November 15 Posts: 616
    @dave1707 - thanks for the code my pad battery flat and watching Peaky Blinders so will check it out tomorrow. Found some of other code in John's Voxel Terrain example. That will take a long time to digest but it answered my some of my needs with the frequency setting for the noise. Thanks again - respond again after a little rest and play.
  • dave1707dave1707 Mod
    edited November 16 Posts: 6,325

    @Bri_G If you’re wondering about frequency, I modified my program to allow you to change the settings for the perlin noise function. Just make a slider change, tap the screen to re-calc. @John The lacunarity value doesn’t work, is it the correct spelling in the documentation.

    EDIT: Changed noise.perlin() to noise.billow(). Lacunarity works for the noise function billow but not for perlin. The parameter slider for lacunarity was uncommented.

    supportedOrientations(LANDSCAPE_ANY)
    
    function setup()
        parameter.integer("z_level",-50,50,0)
        parameter.integer("octaves",1,12,6)
        parameter.number("frequency",0,20,1)
        parameter.number("persistence",0,2,.5)
        parameter.number("lacunarity",0,10,2)
        parameter.integer("seed",0,10,0)
        rectMode(CENTER)
        backingMode(RETAINED)
        ee=true
        calc()
    end
    
    function calc()
        output.clear()
        print("Calculating")
        ee=true
        tab={}
        local v=craft.noise.billow() 
        v.octaves=octaves
        v.frequency=frequency
        v.persistence=persistence
        v.lacunarity=lacunarity
        v.seed=seed
        local step=.0075
        for x=-1,1,step do
            for y=-1,1,step do
                local z=v:getValue(x,y,z_level)    
                table.insert(tab,vec3(x,y,z))      
            end
        end
        output.clear()
        print("Creating image")
    end
    
    function draw()
        if ee then
            fill(255,0,0)
            text("Change a slider value then tap the screen to re-calc",WIDTH/2,HEIGHT-15)
            for a,b in pairs(tab) do
                setColor(b.z)
                ellipse(WIDTH/2+b.x*350,HEIGHT/2+b.y*350,6)
            end
            ee=false
            output.clear()                
        end
    end
    
    function touched(t)
        if t.state==BEGAN then
            calc()
        end
    end
    
    function setColor(c)
       if c<-.25 then
           fill(0,0,128+c*50)
       elseif c<0 then
           fill(0,0,255-c*300)
       elseif c<.0625 then
           fill(0,128+c*500,255-c*500)
       elseif c<.23 then
           fill(250-c*400, 193-c*400, 64-c*400, 255)
       elseif c<.4then
           fill(32+c*250,160+c*250,0)
       elseif c<.75 then
           fill(224-c*250,244-c*150,0)
       elseif c<.95 then
           fill(128+c*100,128+c*100,128+c*100,128+c*100)
       else
           fill(255,255,255,c*300)
       end
    end
    
  • dave1707dave1707 Mod
    Posts: 6,325

    @John The lacunarity works for noise.billow() and noise.rigidMulti() but not for noise.perlin() even though it’s listed for noise.perlin(). I don’t know if the documentation is wrong or perlin is wrong.

  • Posts: 616
    @dave1707- thanks for the update, interesting way of generating map what are the -1 to +1 scales? Is it the ouput range from the perlin noise generator.bswitched it to greyscale and saw some straight line features. Intrigued by the z_level and its range is that a feature of perlin noise?

    Thanks again slowly making progress here. Do you know if you can enhance the tiles like the tutorials for lib noise by brightening and changing the contrast - thought there may be features within Craft to do it or some shaders.
  • Posts: 616

    Oops, meant say programmatically how do you adjust brightness and contrast in an image.

  • Posts: 616

    Looks like brightening is just multiplying each colour variable by a percentage. Contrast looks very similar. I thought you would graduate a change over selected ranges in the image for contrast. Think I’ll use Gimp or Affinity!!!

  • dave1707dave1707 Mod
    Posts: 6,325

    @Bri_G The -1 to +1 is just the x and y ranges. Think of graph paper where the x and y axis goes from -1 to +1. Then I just increment through all the x and y axis values by .0075 . Then think of the z level as some amount above or below the graph paper. Every point at an x,y,z value will get a number back from the noise.perlin function. So every point in a cube will get some value from noise.perlin.

  • dave1707dave1707 Mod
    edited November 18 Posts: 6,325

    @Bri_G Here’s a cube where I calculate the perlin value for each value from -1.2 to 1.2 for x,y,z at increments of .2 . I then take the perlin value at those points and assign it a color value for each sphere. Use 2 fingers for pinch, expand, or move to zoom in, zoom out, or move the cube. It looks interesting to zoom in and rotate the cube. I also show color text that gives the color range of the spheres and the values. It takes several seconds for the color spheres to show.

    supportedOrientations(LANDSCAPE_ANY)
    displayMode(FULLSCREEN)
    
    function setup()
        assert(craft, "Please include Craft as a dependency")
        assert(OrbitViewer, "Please include Cameras (not Camera) as a dependency")
        tab={}
        scene = craft.scene()
        skyMaterial=scene.sky.material
        skyMaterial.sky=color(0)
        skyMaterial.horizon=color(0)
        scene.sun.rotation=quat.eulerAngles(20,45,-30)
        v=scene.camera:add(OrbitViewer, vec3(0,0,0), 50, 0, 300)
        v.rx=10
        v.ry=20
        calc()
    end
    
    function createSphere(x,y,z,val)
        size=.2
        sphere1=scene:entity()
        s1=sphere1:add(craft.rigidbody,STATIC)
        sphere1.position=vec3(x*2,y*2,z*2)
        sphere1:add(craft.shape.sphere,size)
        sphere1.model = craft.model.icosphere(size,3)
        sphere1.material = craft.material("Materials:Specular")
        setColor(val)
        sphere1.material.diffuse=color(r,g,b)
        table.insert(tab,sphere1)
    end
    
    function setColor(v)
        if v<-.9 then
            r,g,b=0,0,0
        elseif v<-.6 then
            r,g,b=0,0,255
        elseif v<-.3 then
            r,g,b=0,255,0
        elseif v<0 then
            r,g,b=0,255,255
        elseif v>.9 then
            r,g,b=255,255,255
        elseif v>.6 then
            r,g,b=255,255,0
        elseif v>.3 then
            r,g,b=255,0,255
        elseif v>=0 then
            r,g,b=255,0,0
        end
    end
    
    function draw()
        update(DeltaTime)
        scene:draw()    
        text("Slide your finger to rotate image",WIDTH/2,HEIGHT-25)
        text("Two finger pinch or expand or move to zoom in or out or move.",WIDTH/2,HEIGHT-50)
        fill(94, 182, 229, 255)
        rect(0,425,80,200)
        fill(0,0,0)
        text("-1 to -.9",40,600)
        fill(0,0,255)
        text("-.9 to -.6",40,580)
        fill(0,255,0)
        text("-.6 to -.3",40,560)
        fill(0,255,255)
        text("-.3 to   0",40,540)
        fill(255,0,0)
        text("  0 to  .3",40,520)
        fill(255,0,255)
        text(" .3 to  .6",40,500)
        fill(255,255,0)
        text(" .6 to  .9",40,480)
        fill(255,255,255)
        text(" .9 to   1",40,460)
    end
    
    function update(dt)
        scene:update(dt)
    end
    
    function calc()
        tab={}
        step=.2
        n=craft.noise.perlin()
        --n=craft.noise.billow()
        for x=-1.2,1.2, step do
            for y=-1.2,1.2,step do
                for z=-1.2,1.2,step do
                    value = n:getValue(x,y,z)
                    createSphere(x*5,y*5,z*5,value)
                end
            end
        end
    end
    
  • Posts: 616

    @dave1707 - aha so the value range of the noise.perlin output is used (your first/second examples) as the axes for x and y and your using the single output of the as the z value. My problem - not thinking in maths mode just thinking of a semi random value for the height in a heightmap for each x and y point.

    Your third example is quite spectacular especially when you consider the array can be moved and expanded in real time and space - when you zoom into the array and move it around. Thanks again, time for me to try to use this.

  • dave1707dave1707 Mod
    Posts: 6,325

    @Bri_G All of the noise functions use x,y,z values as input and output a single value for that position. So any point in 3D space (x,y,z) has a noise value. By plotting the x,y values as a flat surface and the value returned by noise as the height, you can create unlimited landscapes.

Sign In or Register to comment.