public static Channel erode4(Channel channel, float rain_amount, float vaporization, int rain_freq, int iterations) { Channel water_map = new Channel(channel.width, channel.height).fill(0f); Channel water_map_diff = new Channel(channel.width, channel.height).fill(0f); Channel height_map_diff = new Channel(channel.width, channel.height).fill(0f); Console.Write("Hydraulic erosion 4: "); for (int i = 0; i < iterations; i++) { Console.Write("."); // save frames /* if (channel.width > 128 && i%10 == 0) { if (i < 10) { channel.toLayer().saveAsPNG("erosion00" + i); } else if (i < 100) { channel.toLayer().saveAsPNG("erosion0" + i); } else { channel.toLayer().saveAsPNG("erosion" + i); } } */ // rain erodes the underlying terrain if (i%rain_freq == 0) { water_map.channelAdd(channel.copy().multiply(rain_amount)); } // water and sediment transport for (int y = 1; y < channel.height - 1; y++) { for (int x = 1; x < channel.width - 1; x++) { // calculate total heights and height differences float h = channel.getPixel(x, y) + water_map.getPixel(x, y); float h1 = channel.getPixel(x, y + 1) + water_map.getPixel(x, y + 1); float h2 = channel.getPixel(x - 1, y) + water_map.getPixel(x - 1, y); float h3 = channel.getPixel(x + 1, y) + water_map.getPixel(x + 1, y); float h4 = channel.getPixel(x, y - 1) + water_map.getPixel(x, y - 1); float d1 = h - h1; float d2 = h - h2; float d3 = h - h3; float d4 = h - h4; // calculate amount of water to transport float total_height = 0; float total_height_diff = 0; int cells = 1; if (d1 > 0) { total_height_diff+= d1; total_height+= h1; cells++; } if (d2 > 0) { total_height_diff+= d2; total_height+= h2; cells++; } if (d3 > 0) { total_height_diff+= d3; total_height+= h3; cells++; } if (d4 > 0) { total_height_diff+= d4; total_height+= h4; cells++; } if (cells == 1) { continue; } float avr_height = total_height/cells; float water_amount = Math.Min(water_map.getPixel(x, y), h - avr_height); water_map_diff.putPixel(x, y, water_map_diff.getPixel(x, y) - water_amount); float total_height_diff_inv = water_amount/total_height_diff; // transport water if (d1 > 0) { water_amount = d1*total_height_diff_inv; water_map_diff.putPixel(x, y + 1, water_map_diff.getPixel(x, y + 1) + water_amount); height_map_diff.putPixel(x, y + 1, height_map_diff.getPixel(x, y + 1) - 0.1f*water_amount); } if (d2 > 0) { water_amount = d2*total_height_diff_inv; water_map_diff.putPixel(x - 1, y, water_map_diff.getPixel(x - 1, y) + water_amount); height_map_diff.putPixel(x - 1, y, height_map_diff.getPixel(x - 1, y) - 0.1f*water_amount); } if (d3 > 0) { water_amount = d3*total_height_diff_inv; water_map_diff.putPixel(x + 1, y, water_map_diff.getPixel(x + 1, y) + water_amount); height_map_diff.putPixel(x + 1, y, height_map_diff.getPixel(x + 1, y) - 0.1f*water_amount); } if (d4 > 0) { water_amount = d4*total_height_diff_inv; water_map_diff.putPixel(x, y - 1, water_map_diff.getPixel(x, y - 1) + water_amount); height_map_diff.putPixel(x, y - 1, height_map_diff.getPixel(x, y - 1) - 0.1f*water_amount); } } } // apply changes to water map water_map.channelAddNoClip(water_map_diff); water_map_diff.fill(0f); // apply changes to height map channel.channelAddNoClip(height_map_diff); height_map_diff.fill(0f); // vaporize water channel.channelAddNoClip(water_map.copy().channelSubtract(water_map.addClip(-vaporization)).multiply(0.5f)); } // force evaporation of remaining water channel.channelAdd(water_map.multiply(0.5f)); Console.WriteLine("DONE"); return channel; }
public static Channel erode3(Channel channel, Channel rain_map, float vaporization, int rain_freq, int iterations) { Channel vapor_map = rain_map.copy().multiply(0.5f); Channel height_map_diff = new Channel(channel.width, channel.height).fill(0f); Channel water_map = new Channel(channel.width, channel.height).fill(0f); Channel water_map_diff = new Channel(channel.width, channel.height).fill(0f); Channel sediment_map = new Channel(channel.width, channel.height).fill(0f); Channel sediment_map_diff = new Channel(channel.width, channel.height).fill(0f); Console.Write("Hydraulic erosion 3: "); for (int i = 0; i < iterations; i++) { Console.Write("."); // save frames /* if (channel.width > 128 && i%8 == 0) { if (i < 10) { channel.toLayer().saveAsPNG("erosion00" + i); } else if (i < 100) { channel.toLayer().saveAsPNG("erosion0" + i); } else { channel.toLayer().saveAsPNG("erosion" + i); } } */ // rain if (i%rain_freq == 0) { water_map.channelAdd(rain_map); } // water and sediment transport for (int y = 1; y < channel.height - 1; y++) { for (int x = 1; x < channel.width - 1; x++) { // calculate total heights and height differences float h = channel.getPixel(x, y) + water_map.getPixel(x, y); float h1 = channel.getPixel(x, y + 1) + water_map.getPixel(x, y + 1) + sediment_map.getPixel(x, y + 1); float h2 = channel.getPixel(x - 1, y) + water_map.getPixel(x - 1, y) + sediment_map.getPixel(x - 1, y); float h3 = channel.getPixel(x + 1, y) + water_map.getPixel(x + 1, y) + sediment_map.getPixel(x + 1, y); float h4 = channel.getPixel(x, y - 1) + water_map.getPixel(x, y - 1) + sediment_map.getPixel(x, y - 1); float d1 = h - h1; float d2 = h - h2; float d3 = h - h3; float d4 = h - h4; // calculate amount of water and sediment to transport float total_height = 0; float total_height_diff = 0; int cells = 1; if (d1 > 0) { total_height_diff+= d1; total_height+= h1; cells++; } if (d2 > 0) { total_height_diff+= d2; total_height+= h2; cells++; } if (d3 > 0) { total_height_diff+= d3; total_height+= h3; cells++; } if (d4 > 0) { total_height_diff+= d4; total_height+= h4; cells++; } if (cells == 1) { continue; } float avr_height = total_height/cells; float water_amount = Math.Min(water_map.getPixel(x, y), h - avr_height); water_map_diff.putPixel(x, y, water_map_diff.getPixel(x, y) - water_amount); float water_inv = water_amount/total_height_diff; float sediment_amount = sediment_map.getPixel(x, y); sediment_map_diff.putPixel(x, y, sediment_map_diff.getPixel(x, y) - sediment_amount); float sediment_inv = sediment_amount/total_height_diff; float dissolve; // transport water and sediment and dissolve more material if (d1 > 0) { water_map_diff.putPixel(x, y + 1, water_map_diff.getPixel(x, y + 1) + d1*water_inv); dissolve = 10f*d1*water_amount; sediment_map_diff.putPixel(x, y + 1, sediment_map_diff.getPixel(x, y + 1) + d1*sediment_inv + dissolve); height_map_diff.putPixel(x, y + 1, height_map_diff.getPixel(x, y + 1) - dissolve); } if (d2 > 0) { water_map_diff.putPixel(x - 1, y, water_map_diff.getPixel(x - 1, y) + d2*water_inv); dissolve = 10f*d2*water_amount; sediment_map_diff.putPixel(x - 1, y, sediment_map_diff.getPixel(x - 1, y) + d2*sediment_inv + dissolve); height_map_diff.putPixel(x - 1, y, height_map_diff.getPixel(x - 1, y) - dissolve); } if (d3 > 0) { water_map_diff.putPixel(x + 1, y, water_map_diff.getPixel(x + 1, y) + d3*water_inv); dissolve = 10f*d3*water_amount; sediment_map_diff.putPixel(x + 1, y, sediment_map_diff.getPixel(x + 1, y) + d3*sediment_inv + dissolve); height_map_diff.putPixel(x + 1, y, height_map_diff.getPixel(x + 1, y) - dissolve); } if (d4 > 0) { water_map_diff.putPixel(x, y - 1, water_map_diff.getPixel(x, y - 1) + d4*water_inv); dissolve = 10f*d4*water_amount; sediment_map_diff.putPixel(x, y - 1, sediment_map_diff.getPixel(x, y - 1) + d4*sediment_inv + dissolve); height_map_diff.putPixel(x, y - 1, height_map_diff.getPixel(x, y - 1) - dissolve); } } } // apply changes to water map water_map.channelAddNoClip(water_map_diff); // apply changes to sediment map sediment_map.channelAddNoClip(sediment_map_diff); // apply changes to height map channel.channelAddNoClip(height_map_diff); // water vaporization water_map.addClip(-vaporization); // sedimentation sediment_map_diff = sediment_map.copy().channelSubtract(water_map); sediment_map.channelSubtract(sediment_map_diff); channel.channelAddNoClip(sediment_map_diff); // clear diff maps water_map_diff.fill(0f); height_map_diff.fill(0f); sediment_map_diff.fill(0f); } // force evaporation of remaining water //channel.channelAdd(water_map.multiply(0.5f)); Console.WriteLine("DONE"); return channel; }