# Midpoint displacement generating unsatisfactory terrain

Following this lesson for implementing mid-point displacement algorithm for terrain generation. I aim at generating a wavefront(obj) file of the terrain

I have an array of dimension (2^n + 1) x (2^n + 1). The corners of the 2d array is initialized with random numbers.

n = plug_in_n
arr = Array.new(2**n + 1) { Array.new(2**n +1) { 0 } }
n_augmented = 2**n

arr[0][0] = rand(10 + 1)
arr[0][n_augmented] = rand(10 + 1)
arr[n_augmented][0] = rand(10 + 1)
arr[n_augmented][n_augmented] = rand(10 + 1)


This is the script I have written for generating a terrain.

a = arr
n_augmented_copy = n_augmented
while n_augmented > 1

puts "n_augmented = " + n_augmented.to_s
i = 0
while(i < n_augmented_copy)
j = 0
while(j < n_augmented_copy)
a[i][j+(n_augmented)/2] = (a[i][j] + a[i][j+n_augmented]) / 2.0  + (rand 0.0..1.0)
a[i + (n_augmented) / 2][j + n_augmented] = (a[i][j + n_augmented] + a[i + n_augmented][j + n_augmented]) / 2.0 + (rand 0.0..1.0)
a[i + n_augmented][j +( n_augmented) / 2] = (a[i + n_augmented][j] + a[i + n_augmented][j + n_augmented] )/ 2.0 + (rand 0.0..1.0)
a[i + (n_augmented) / 2][j]  = (a[i + n_augmented][j] + a[i][j] )/ 2.0 + (rand 0.0..1.0)
a[i + (n_augmented) / 2][j +( n_augmented) / 2] = ((a[i][j+(n_augmented)/2] + a[i + (n_augmented) / 2][j + n_augmented] + a[i + n_augmented][j +( n_augmented) / 2]  + a[i + (n_augmented) / 2][j]))/ 4.0  + (rand 0.0..1.0)
j = j + n_augmented
end
i = i + n_augmented
end
# binding.pry
# p_arr(a)
n_augmented = n_augmented / 2


end

Demonstrating a case for n = 2: I have omitted the random-displacement rand 0.0..1.0to check if the heightmap chucks are getting calculated fine.

Here is how the iterations look like

n_augmented = 4
9     0   5.5     0     2
0     0     0     0     0
6.5     0   5.5     0   4.5
0     0     0     0     0
4     0   5.5     0     7
n_augmented = 2
9  7.25   5.5  3.75     2
7.75 6.625   5.5 4.375  3.25
6.5   6.0   5.5   5.0   4.5
5.25 5.375   5.5 5.625  5.75
4  4.75   5.5  6.25     7


If I form a mesh using this alone, it results in a plane like:

Considering the random displacement for this case, it results in :

For n = 5, It looks like :

This is not desirable since I cannot see any peaks and slopes.

Here is the ruby script : CODE

The magnitude of your random displacement is not changing appropriately. For your initial points, you're using a random value between 0 and 10. But for the subdivisions you're only using a random value between 0 and 1.

What you probably want to do is to start with a random value between 0 and 10, and then cut the range in half on each iteration. So on the 2nd iteration, the range of random values would only be between 0 and 5. On the 3rd iteration it would be between 0 and 2.5, etc. That should get you something with peaks and valleys.

Once you have that, you can increase the Y scale to make the peaks taller or valleys deeper, or decrease it to reduce the range.

• Thanks. Got it working and also posted the updated code in my answer. Y- scale in your answer referring to the initial upper range of the random value? Apr 7, 2019 at 4:46
• Yes, you can either increase the first random values, or scale all values afterwards. Apr 7, 2019 at 5:17

What you probably want to do is to start with a random value between 0 and 10, and then cut the range in half on each iteration. So on the 2nd iteration, the range of random values would only be between 0 and 5. On the 3rd iteration it would be between 0 and 2.5, etc. That should get you something with peaks and valleys.

Thanks for this step. Here is my updated source if it helps anyone and here is the result:

def p_arr(arr)
width = arr.flatten.max.to_s.size+2
for i in (0..arr.length - 1)
for j in (0..arr.length - 1)
print arr[i][j].to_s.rjust(width + 2) + " "
end
puts ""
end
end
def getrnd(range)
rand -range.to_f..range.to_f
end

n = 8
arr = Array.new(2**n + 1) { Array.new(2**n +1) { 0 } }
n_augmented = 2**n
arr[0][0] = rand(10 + 1)
arr[0][n_augmented] = rand(10 + 1)
arr[n_augmented][0] = rand(10 + 1)
arr[n_augmented][n_augmented] = rand(10 + 1)

a = arr
n_augmented_copy = n_augmented
range = 50.0
while n_augmented > 1
i = 0
while(i < n_augmented_copy)
j = 0
while(j < n_augmented_copy)
a[i][j+(n_augmented)/2] = (a[i][j] + a[i][j+n_augmented]) / 2.0  + getrnd(range)
a[i + (n_augmented) / 2][j + n_augmented] = (a[i][j + n_augmented] + a[i + n_augmented][j + n_augmented]) / 2.0 + getrnd(range)
a[i + n_augmented][j +( n_augmented) / 2] = (a[i + n_augmented][j] + a[i + n_augmented][j + n_augmented] )/ 2.0 + getrnd(range)
a[i + (n_augmented) / 2][j]  = (a[i + n_augmented][j] + a[i][j] )/ 2.0 + getrnd(range)
a[i + (n_augmented) / 2][j +( n_augmented) / 2] = ((a[i][j+(n_augmented)/2] + a[i + (n_augmented) / 2][j + n_augmented] + a[i + n_augmented][j +( n_augmented) / 2]  + a[i + (n_augmented) / 2][j]))/ 4.0  + getrnd(range)
j = j + n_augmented
end
i = i + n_augmented
end
range = range / 2
n_augmented = n_augmented / 2
end

board = n_augmented_copy + 1
c = 0
k = board + 1
#Print vertices
File.open("mountain.obj", 'w') { |file|
for i in 0..a.length - 1
for j in 0..a.length - 1
file.write("v " + j.to_f.to_s + " " + a[i][j].to_f.to_s + " " + i.to_f.to_s+ "\n")
end
end

for i in 0..a.length - 2
for i in 0..a.length - 2
file.write("f " + (c + i + 1).to_s  + " " + k.to_s + " " + (c + i + 2).to_s + "\n")
file.write("f " + (c + i + 2).to_s + " "  + (k).to_s + " " + (k+1).to_s  + "\n")
k=k+1
end
k = k +1
c += board
end
}