We are about to switch to a new forum software. Until then we have removed the registration on this forum.
Hi,
I'm trying to add Perlin noise to an icoSphere (icosahedron / geodesic sphere) but for some reason the terrain (noise) generated is too "homogeneous": "peaks" are allocated almost evenly and heights (vertices heights) don't vary much.
Ideally I'd like to be able to adjust the noise so as to get real random values (heterogeneous distribution and heights) like in the following pictures.
The vertices positions are stored in array list (here named "knots") and all I do is adding a variable (named "r") to the x, y, z position of each vertex:
for e in knots:
r = noise(random(1))
for i, f in enumerate(e):
particles[e[i]] = Particle(particles[e[i]].x() + particles[e[i]].x() * r , particles[e[i]].y() + particles[e[i]].y() * r, particles[e[i]].z() + particles[e[i]].z() * r)
I guess this is not an appropriate way to apply Perlin noise and would love some help to tweak the snippet above. Any tip, idea, code (both Java or Python) is welcomed !
EDIT: This question is a "programming question". Even though the code is in Python, the question is about Perlin noise programmation. Answers or suggestions in Java are totally fine !
full code:
add_library('peasycam')
from particle import Particle
def setup():
global ICOSUBDIVISION, vdata, tindices, particles
size(800, 600, OPENGL)
smooth(10)
cam = PeasyCam(this, 450)
particles = []
ICOSUBDIVISION = 0
X, Z = 0.525731112119133606, 0.850650808352039932
vdata = [[-X, 0, Z], [X, 0, Z], [-X, 0, -Z],
[X, 0, -Z], [0, Z, X], [0, Z, -X],
[0, -Z, X], [0, -Z, -X], [Z, X, 0],
[-Z, X, 0], [Z, -X, 0], [-Z, -X, 0]]
tindices = [[0, 4, 1], [0, 9, 4], [9, 5, 4], [4, 5, 8],
[4, 8, 1], [8, 10, 1], [8, 3, 10], [5, 3, 8],
[5, 2, 3], [2, 7, 3], [7, 10, 3], [7, 6, 10],
[7, 11, 6], [11, 0, 6], [ 0, 1, 6], [ 6, 1, 10],
[ 9, 0, 11], [ 9, 11, 2], [ 9, 2, 5], [ 7, 2, 11 ]]
Icosahedron()
def draw():
background(360)
lights()
f1, f2, f3 = PVector(), PVector(), PVector()
for e in range(0, len(particles), 3):
f1.x, f1.y, f1.z = particles[e].x(), particles[e].y(), particles[e].z()
f2.x, f2.y, f2.z = particles[e+1].x(), particles[e+1].y(), particles[e+1].z()
f3.x, f3.y, f3.z = particles[e+2].x(), particles[e+2].y(), particles[e+2].z()
noStroke()
colorMode(HSB, 360, 105, 100)
fill(abs(f1.x), abs(f1.y), abs(f1.z))
beginShape()
vertex( f1.x, f1.y, f1.z )
vertex( f2.x, f2.y, f2.z )
vertex( f3.x, f3.y, f3.z )
endShape( CLOSE )
def keyPressed():
global ICOSUBDIVISION, particles
if keyCode == UP:
if ICOSUBDIVISION == 5:
return
ICOSUBDIVISION += 1
particles = []
Icosahedron()
elif keyCode == DOWN:
if ICOSUBDIVISION == 0:
return
ICOSUBDIVISION -= 1
particles = []
Icosahedron()
def Icosahedron():
global vertexList
vertexList = []
[subdivide(vdata[tindices[e][0]], vdata[tindices[e][1]], vdata[tindices[e][2]], ICOSUBDIVISION) for e in range(20)]
for e in range(0, len(vertexList), 3):
x, y, z = 0, 1, 2
p = Particle(vertexList[e+x]* 200, vertexList[e+y]* 200, vertexList[e+z]* 200)
particles.append(p)
VertPos = set(particles)
knots = [[] for e in VertPos]
for i, v in enumerate(VertPos):
for j, p in enumerate(particles):
if p == v:
knots[i].append(j)
for e in knots:
r = noise(random(1))
for i, f in enumerate(e):
particles[e[i]] = Particle(particles[e[i]].x() + particles[e[i]].x() * r , particles[e[i]].y() + particles[e[i]].y() * r, particles[e[i]].z() + particles[e[i]].z() * r)
def norme(v):
ln = 0
for e in range(3):
ln += (v[e] * v[e])
ln = sqrt(ln)
for e in range(3):
v[e] /= ln
def addi(v):
for e in range(3):
vertexList.append(v[e])
def subdivide(v1, v2, v3, depth):
if depth == 0:
addi(v1)
addi(v2)
addi(v3)
return
v12, v23, v31 = [0,0,0], [0,0,0], [0,0,0]
for e in range(3):
v12[e] = (v1[e] + v2[e]) / 2
v23[e] = (v2[e] + v3[e]) / 2
v31[e] = (v3[e] + v1[e]) / 2
norme(v12)
norme(v23)
norme(v31)
subdivide(v1, v12, v31, depth - 1)
subdivide(v2, v23, v12, depth - 1)
subdivide(v3, v31, v23, depth - 1)
subdivide(v12, v23, v31, depth - 1)
Answers
just came up with this:
The final result is somewhat better but still no perfect.
@GoToLoop: Do you actually think it's a correct way to apply Perlin noise ?
Sorry. Dunno much about perlin or noise().
Most I've got close were these 2 online sketches I've refactored a long time ago:
@solub what is p in the above line? If you draw p without the noise, would you get a perfect sphere?
Related to your first post:
Where are you applying perlin noise? From what I know in 1D, perlin noise variation depends on the sample difference between calls to perlin noise. If you want to see more variation, you just need to sample at larger intervals aka. perlin_noise_function_generator(t) where t is your sampling interval. However, perlinNoise and noise is not the same function.
Kf
@kfrajer: Each p is a vertex, p.x, p.y, p.z are the vertices positions. So if I draw p without the noise I would indeed get a perfect sphere.
In the snippet I normalize each vertex, compute noise and multiply them by the noise value:
And this gives me someting like this:
(I can indeed map the noise between lower than 40 (say 30 or 20) and 100 to get more variations. )
I guess it's ok but am still not sure if it's an appropriate way to apply Perlin noise.
Take this picture for instance (found on google images after searching "icosphere" + "perlin noise") How Perlin noise can give you such variation in heights ? There must be somehtin I'm missing.
@solub --
If the example you found on the internet is indeed using perlin noise then the two issues are:
I would demonstrate on your provided sketch, but it fails on
(also, for some reason the forum inserted emoticon HTML into your line 15).
At first glance it looks like you are approaching things in the right way but your base radius is much larger than the reference example, and your output amplitude is way smaller, and you need the highest facet resolution shown in your demo (high resolution is required for sharp peaks). The regularity / irregularity on the surface is extremely sensitive to the scale of input.
To read more about the scale of input coordinates, see past discussions of "perlin" on this forum:
@jeremydouglass My apologies, I forgot to copy/paste the "particle" Class. After a google search I found that the shape in the example was actually created by accident.
(original article here).
I tried to reproduce the same kind of shape following the explanation:
But it didn't work. Dividing "each radius by a noise value" make the sphere shrink so much that it almost disapear.
Anyway, thanks for the links.
Full (working) code:
I‘d use noStroke(); and lights();
EDIT: It actually works if you divide the radii by a very small amount of the noise.
@Chrisir: If I use noStroke() and lights() nothing would be drawn on the screen since I only display strokes.
You only need to do that because you've mapped the noise to a large range in the line above. Because of line 4 you have a range of 10 to 120 which you divide by, giving 1/10 to 1/120, both tiny.
If you didn't do the map you'd have a range of 1/1 (original size) to 1/0 (huge!)
@koogs: indeed !
maybe you can connect the vertices to triangles...?
Hello, I'm really impressed and interested in the above code, but I don't know anything about Python :(
I'm trying to translate to Java (I don't know if it's even possible), but I' struggling with the various for() loop since I don't know methods like enumerate() or others..
The biggest problems are:
I don't know hot to translate this: line 78:
norme(), addi(), subdivide() methods
and more and more..
I can leave here what I've tried to do until now, with no results :(
Hope you can help me :) Thank you in advance
line 78 is just a for loop, the loop counter is at the end (because python!)
That is a python list comprehension: http://www.pythonforbeginners.com/basics/list-comprehensions-in-python
range(20)
is a built-in -- it returns [0, 1, 2, 3 ... 19]for e in range(20)
is like a classic loop -- and that loop drives the initial statement. the term from a list in the back part of the statement goes repeatedly into the first part of the statement.So the Java equivalent is:
...and these results appear to be returned inside a list of 20 things [] although I haven't studied the original code you are translating.
v1, v2, v3 aren't floats, they are PVectors, or float[3]
Thank you everybody, your suggestions have really helped me out of this :)
so here is where I am if anyone will need the code in JAVA:
Apparently the Python written code works better and runs fluidly then the JAVA one, in fact I can make one more ICOSUBDIVISION without lowering the frame rate.
Can this be optimized in some way :D ?
plus: I'm finding difference also in how the terrain move:
in python m += 0.04; r += 0.04; are working really good make a nice smooth movement.. in JAVA is just a mess, everything moves frenetically without order. anyone with some suggestions? :)
yes. use pshapes. rather than defining the shape every frame, sending it 3x vertices every time, calculate a pshape once, when it changes, and just draw that.
you mix up pvector and float[3] more than i like. if it was all pvector then you can probably use pvector's own normalise() method rather than norme(). subdivide becomes neater too.
you don't seem to be removing the old shape when adding the new subdivided one. that may be deliberate but it makes the new shape look less tidy.
the 20 on line 89, what is it? is it's tindices.size() then use that.
Thank you koogs, your help is really appreciated :)
So.. I managed somehow to use PShape and I can see an enormous difference!! Much much better in runtime :)
there's something more that I have to ask, I can't find a way to make an appropriate fill() to te shape.. as you can see it creates line inside the volume, not only in surface, and maybe that is where the error starts.. but I really can't find a way to correct it :(
I tried to avoided float[] and PVector mix up, but if I convert every float[] in PVector and even eliminate the norme() method it runs slower than ever!
I also re-initialize ArrayList when adding the new subdivided shape, this way I remove old ones or not?
here is the code until now:
I leave here also a frame to show the various errors
@BrokenCode --
Hmm. Maybe z-fighting? Possibly play around with depth test hints? (untested)
Yes, re-initializing removes everything -- the variable name is now pointed at a new ArrayList object, as the initialization line says.
Thank you for suggestions and clarification :)
Ok, so, I have to read how hint(DISABLE_DEPTH_TEST); hint(ENABLE_DEPTH_TEST); works, but I tried to put them somewhere in the code and both seems to do nothing :(
Hi @BrokenCode. You can find a somewhat similar Java version of my code here: https://www.openprocessing.org/sketch/92464
I hope it can help!
PS: If you decide for some reason to run the python version instead I'd recommend avoiding list comprehensions as it significantly slow down the sketch.
JAVA version (from the link)
Thank you, I will dig it! :D
Following this thread (where I was asking for help to make an icosphere), I would like to share a simpler approach to compute a sphere and apply Perlin noise to it.
The following example displays a Fibonacci sphere but could certainly work with a simple point sphere.
(Don't mind the yellow, that's a glitch from the GIF conversion)
Thanks so much for sharing this, @solub -- using hemesh triangulation on the Fibonacci sphere is an elegant approach, and noising the points looks great.