[Question]
  [
Write a program that takes in an integer from 0 to 65535 (216-1) and generates a *unique* 500×500 pixel image that looks as similar as possible to these 6 real life images of cracked soil:
[![cracked soil sample 1](https://i.stack.imgur.com/GqnaPs.png)](https://i.stack.imgur.com/GqnaP.png) [![cracked soil sample 2](https://i.stack.imgur.com/IYtgDs.png)](https://i.stack.imgur.com/IYtgD.png) [![cracked soil sample 3](https://i.stack.imgur.com/6uWqus.png)](https://i.stack.imgur.com/6uWqu.png) [![cracked soil sample 4](https://i.stack.imgur.com/Aa3YVs.png)](https://i.stack.imgur.com/Aa3YV.png) [![cracked soil sample 5](https://i.stack.imgur.com/C4WJbs.png)](https://i.stack.imgur.com/C4WJb.png) [![cracked soil sample 6](https://i.stack.imgur.com/WVXRls.png)](https://i.stack.imgur.com/WVXRl.png)  
**These are thumbnails, click them to see the full-size 500×500 images.**
The goal here is to **make your computer generated images as photorealistic as possible**. So ideally, if any of the images output by your program were mixed in with the 6 images above, someone seeing the images for the first time would not be able to tell the computer generated ones apart from the real ones.
Perfect photorealism is tricky however, so just do the best you can. This is a [popularity-contest](/questions/tagged/popularity-contest "show questions tagged 'popularity-contest'") so the answers that have more realistic outputs will be upvoted more and be more likely to win.
## Rules
* You may use image processing functions or libraries.
* You may base your algorithm on information gathered from the 6 sample images, but your 65536 (216) possible output images should be visually distinct from each other *and* the sample images, especially with regard to the arrangement of the cracks. You must truly generate your images, don't just rotate and translate a selection from a preexisting photo.
* You should not otherwise hardcode your outputs. A generic algorithm should be used and numbers larger than 65535 should theoretically produce valid outputs. (I've restricted it merely to accommodate small-maximum integer types.)
* The input integer can be thought of as a seed that results in a random cracked soil output image. It should be deterministic though, so the same input should always result in the same output.
* The output images must be exactly 500×500 pixels.
* The output images may be saved in any common image file format, or simply displayed.
* Be sure to include a few example output images in your answer, and their corresponding input numbers.
* **The answer with the most votes wins.** Voters should of course upvote answers that attempt to produce images similar to the 6 samples, and downvote answers that break rules or give inconsistent results.
The 6 sample images were taken from [texturelib.com](http://texturelib.com/). 1000×1000 pixel area selections were taken from [two larger images of cracked soil](http://texturelib.com/texture/?path=/Textures/soil/cracked/soil_cracked_0038) and then resized to 500×500. You may use analysis from these larger images in your program but the output should specifically imitate the 6 chosen sample images.
  
[Answer]
# Mathematica
A [Voronoi diagram](https://en.wikipedia.org/wiki/Voronoi_diagram) looks like this drawing, from Wikipedia, showing 19 cells, each containing a single seed point. A cell consists of the subregion of points which the respective generating point is closer to than any of the other seed points.
[![voronoi](https://i.stack.imgur.com/gcvjD.png)](https://i.stack.imgur.com/gcvjD.png)
The code below generates a diagram from 80 random points (in the square region bound by (-1,-1) and (1,1)).
It uses the polygon primitives (in 2D) in the diagram to build polyhedra (in 3D). Imagine that each polygon has, just under it, a translation (-.08 in z) of itself. Think of the two polygons as the upper and lower face of a polyhedron. "Side faces" are then added to complete the polyhedron.
Each polyhedron is then translated outwards, from the center of the image, on the xy plane; it moves away from the middle. The magnitude of the translation varies directly with the distance between the polyhedron's original generating random point and the center of the screen. This "spreading out" of the polyhedra in the xy plane results in crevices.
```
crackedMud[1]
```
## [one](https://i.stack.imgur.com/pPB0K.jpg)
```
crackedMud[65535]
```
## [last](https://i.stack.imgur.com/o7cwm.jpg)
## Code
```
ClearAll[polyhedronFromPolygon, voronoiPolygons, generatingPointFromPolygon, crackedMud]
(* polyhedronFromPolygon returns a single polyhedron from a polygon *)
polyhedronFromPolygon[polygon_] :=      
 Module[{twoPolygons, verticesOfUpperPolygonCell, nVertices, n = 1},
 verticesOfUpperPolygonCell = Join @@ (polygon[[1]] /. {x_, y_} :> {{x, y, 0}, {x, y, -.08}});
 (* number of vertices in a single *Voronoi* cell *)
 nVertices = Length[verticesOfUpperPolygonCell]/2;   
      
(*vertex indices of the upper and lower polygon faces *)  
twoPolygons = Select[Range@(2*nVertices), #] & /@ {OddQ, EvenQ};    
(*vertex indices of a rectangular face of the polyhedron *)
While[n < nVertices + 1, AppendTo[twoPolygons,
    {twoPolygons[[1, n]], twoPolygons[[2, n]], 
     twoPolygons[[2, If[n + 1 < nVertices + 1, n + 1, 1]]], 
     twoPolygons[[1, If[n + 1 < nVertices + 1, n + 1, 1]]]}]; n++];
(*the graphics complex returned is a polyhedron, even though it says Polygon *)
 GraphicsComplex[verticesOfUpperPolygonCell, Polygon[twoPolygons]] ] 
(* takes two dimensional coordinates and returns all of the cells of a Voronoi diagram *)
voronoiPolygons[pts_] := 
Module[{voronoiRegion, data},
  voronoiRegion = VoronoiMesh[pts, ImageSize -> Medium, 
  PlotTheme -> "Lines", Axes -> True, AxesOrigin -> {0, 0}];
  data = Join @@ (MeshPrimitives[voronoiRegion, 2][[All, 1]] /. {x_, y_} :> {{x, y, 0}, {x, y, .04}});
 (* the mesh primitives are the polygons *)
  MeshPrimitives[voronoiRegion, 2]]   
(* Returns, in 3D, the point which was used to generate the nth Voronoi cell. *)
generatingPointFromPolygon[n_, points_, pgons_] := 
 FirstCase[points, {x_, y_} /; RegionMember[pgons[[n]], {x, y}] :> {x,y,0}]
crackedMud[seedNumber_] :- 
 Module[{pts, pts3D, geometricImage, nPts, polygons, polyhedra, centerPtinImage},
  SeedRandom[seedNumber];
  nPts = 80;
  pts = RandomReal[{-1, 1}, {nPts, 2}];
  pts3D = pts /. {x_, y_} :> {x, y, .0};
  polygons = voronoiPolygons[pts];
  polyhedra = polyhedronFromPolygon /@ polygons;
  centerPtinImage =   (Mean /@ (PlotRange /. 
        AbsoluteOptions[
         Graphics3D[{polyhedra, Blue, Point@pts3D}, Axes -> False, 
         Boxed -> False]])) /. {x_Real, y_, _} :> {x, y, 0};
  geometricImage =
  Graphics3D[{RGBColor[0.75, 0.75, 0.8], EdgeForm[Darker@Gray],
        (* # is the nth polygon which yields the nth polyhedron *)
        (* generatingPointFromPolygon returns the point the generated the #th polygon *)
        
     GeometricTransformation[{polyhedronFromPolygon[polygons[[#]]]},   
        TranslationTransform[(generatingPointFromPolygon[#, pts, polygons] - centerPtinImage)/5]] & /@ Range@nPts},
         Axes -> False,  Boxed -> False, ViewPoint -> {0., -1, 1.5}, 
         Background -> Black, ImageSize -> 1200];
     (*ImageTrim returns a 500 by 500 pixel clip from the center of the image *)
     ImageTrim[
        (*ImageEffect speckles the image *)
        ImageEffect[Rasterize[geometricImage], {"Noise", 1/5}], 
     {{250, 250}, {750, 750}}]
  ] 
```
[Answer]
# Java
I used an approach based on recursive Voronoi diagrams. The outputs doesn't look very realistic, but I guess they're okay.
Here are some example images (resized to 250x250 so that it doesn't fill the entire screen):
0: 
[![Image 0](https://i.stack.imgur.com/ne8ds.png)](https://i.stack.imgur.com/ne8ds.png)
1:
[![Image 1](https://i.stack.imgur.com/8gjiW.png)](https://i.stack.imgur.com/8gjiW.png)
## More details about the algorithm:
All images in this section are using the same seed.
The algorithm starts by generating a Voronoi diagram with 5 points:
[![Voronoi diagram](https://i.stack.imgur.com/0oIeq.png)](https://i.stack.imgur.com/0oIeq.png)
If we look at the original images in the challenge, we can see that the lines aren't all straight like that, so we weigh the distance by a random value, based on the angle to the point, also, closer angles gives closer values:
[![Weighted Voronoi diagram](https://i.stack.imgur.com/D4rpi.png)](https://i.stack.imgur.com/D4rpi.png)
Now, we recursively draw these kinds of Voronoi diagrams inside of each region, with thinner and more transparent line, and remove the background, with a maximum recursion depth of 3, and we get:
[![Recursive Voronoi](https://i.stack.imgur.com/zopXn.png)](https://i.stack.imgur.com/zopXn.png)
Now, we just add the pale brown background, and we're done!
[![Done!](https://i.stack.imgur.com/oDFnA.png)](https://i.stack.imgur.com/oDFnA.png)
## Code:
The code consists of three classes, `Main.java`, `VoronoiPoint.java` and `Vector.java`:
`Main.java`:
```
import java.awt.Desktop;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Random;
import javax.imageio.ImageIO;
public class Main {
    public static int WIDTH = 500;
    public static int HEIGHT = 500;
    public static int RECURSION_LEVELS = 3;
    public static int AMOUNT_OF_POINTS = 5;
    public static int ROTATION_RESOLUTION = 600;
    public static int ROTATION_SMOOTHNESS = 10;
    public static int BACKGROUND = 0xFFE0CBAD;
    public static Random RAND;
    public static void main(String[] args) {
        int seed = new Random().nextInt(65536);
        if (args.length == 1) {
            System.out.println(Arrays.toString(args));
            seed = Integer.parseInt(args[0]);
        } else {
            System.out.println("Generated seed: " + seed);
        }
        RAND = new Random(seed);
        ArrayList<Vector> points = new ArrayList<Vector>();
        for (int x = 0; x < WIDTH; x++) {
            for (int y = 0; y < HEIGHT; y++) {
                points.add(new Vector(x, y));
            }
        }
        BufferedImage soil = generateSoil(WIDTH, HEIGHT, seed, points, AMOUNT_OF_POINTS, RECURSION_LEVELS);
        BufferedImage background = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
        for (int x = 0; x < background.getWidth(); x++) {
            for (int y = 0; y < background.getHeight(); y++) {
                background.setRGB(x, y, BACKGROUND ^ (RAND.nextInt(10) * 0x010101));
            }
        }
        Graphics g = background.getGraphics();
        g.drawImage(soil, 0, 0, null);
        g.dispose();
        String fileName = "soil";
        File output = new File(fileName + ".png");
        for (int i = 0; output.exists(); i++) {
            output = new File(fileName + i + ".png");
        }
        try {
            ImageIO.write(background, "png", output);
            Desktop.getDesktop().open(output);
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("Done. Saved as " + output);
    }
    private static BufferedImage generateSoil(int width, int height, int seed, ArrayList<Vector> drawPoints,
            int amountOfPoints, int recursionLevel) {
        BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        ArrayList<VoronoiPoint> points = new ArrayList<VoronoiPoint>();
        for (int i = 0; i < amountOfPoints; i++) {
            points.add(new VoronoiPoint(drawPoints.get(RAND.nextInt(drawPoints.size()))));
        }
        HashMap<Integer, ArrayList<Vector>> pointMaps = new HashMap<Integer, ArrayList<Vector>>();
        for (VoronoiPoint point : points) {
            pointMaps.put(point.hashCode(), new ArrayList<Vector>());
        }
        System.out.println(pointMaps);
        System.out.println(points);
        for (Vector v : drawPoints) {
            VoronoiPoint closest = null;
            VoronoiPoint secondClosest = null;
            for (VoronoiPoint point : points) {
                double distance = point.getMultiplicativeDistanceTo(v);
                if (closest == null || distance < closest.getMultiplicativeDistanceTo(v)) {
                    secondClosest = closest;
                    closest = point;
                } else if (secondClosest == null || distance < secondClosest.getMultiplicativeDistanceTo(v)) {
                    secondClosest = point;
                }
            }
            int col = 0;
            if (Math.abs(closest.getMultiplicativeDistanceTo(v)
                    - secondClosest.getMultiplicativeDistanceTo(v)) < (recursionLevel * 5 / RECURSION_LEVELS)) {
                col = 0x01000000 * (recursionLevel * 255 / RECURSION_LEVELS);
            } else {
                pointMaps.get(closest.hashCode()).add(v);
            }
            result.setRGB((int) v.getX(), (int) v.getY(), col);
        }
        Graphics g = result.getGraphics();
        if (recursionLevel > 0) {
            for (ArrayList<Vector> pixels : pointMaps.values()) {
                if (pixels.size() > 10) {
                    BufferedImage img = generateSoil(width, height, seed, pixels, amountOfPoints,
                            recursionLevel - 1);
                    g.drawImage(img, 0, 0, null);
                }
            }
        }
        g.dispose();
        return result;
    }
    public static int modInts(int a, int b) {
        return (int) mod(a, b);
    }
    public static double mod(double a, double b) {
        a = a % b;
        while (a < 0)
            a += b;
        return a;
    }
}
```
`VoronoiPoint.java`:
```
public class VoronoiPoint {
    private Vector pos;
    private double[] distances;
    public VoronoiPoint(Vector pos) {
        this.pos = pos;
        distances = new double[Main.ROTATION_RESOLUTION];
        for (int i = 0; i < distances.length; i++)
            distances[i] = Main.RAND.nextFloat() / 2 + 0.51;
        for (int iter = 0; iter < Main.ROTATION_SMOOTHNESS; iter++) {
            for (int i = 0; i < distances.length; i++) {
                distances[i] = (distances[Main.modInts(i - Main.RAND.nextInt(4) - 2, distances.length)] + distances[i]
                        + distances[Main.modInts(i + Main.RAND.nextInt(4) - 2, distances.length)]) / 3;
            }
        }
    }
    public Vector getPos() {
        return pos;
    }
    public double getRotationFromAngle(double radians) {
        return distances[(int) (Main.mod(Math.toDegrees(radians) / 360, 1) * distances.length)];
    }
    public double getRotationFromVector(Vector vec) {
        return getRotationFromAngle(Math.atan2(pos.getY() - vec.getY(), -(pos.getX() - vec.getX())));
    }
    public double getMultiplicativeDistanceTo(Vector other) {
        return pos.getLengthTo(other) * getRotationFromVector(other);
    }
    public String toString() {
        return "VoronoiPoint(pos=[" + pos.getX() + ", " + pos.getY() + "])";
    }
    public int hashCode() {
        return distances.hashCode() ^ pos.hashCode();
    }
}
```
`Vector.java`: (This class is copied from one of my other projects, so it contains some unnecessary code)
```
package com.loovjo.soil;
import java.util.ArrayList;
import java.util.Random;
public class Vector {
    private static final float SMALL = 1f / Float.MAX_EXPONENT * 100;
    private float x, y;
    public Vector(float x, float y) {
        this.setX(x);
        this.setY(y);
    }
    public Vector(int x, int y) {
        this.setX(x);
        this.setY(y);
    }
    public Vector(double x, double y) {
        this.setX((float) x);
        this.setY((float) y);
    }
    public float getY() {
        return y;
    }
    public void setY(float y) {
        this.y = y;
    }
    public float getX() {
        return x;
    }
    public void setX(float x) {
        this.x = x;
    }
    /*
     * Gets the length ^ 2 This is faster than getting the length.
     */
    public float getLengthToSqrd(float x, float y) {
        return (float) ((this.x - x) * (this.x - x) + (this.y - y) * (this.y - y));
    }
    public float getLengthToSqrd(Vector v) {
        return getLengthToSqrd(v.x, v.y);
    }
    public float getLengthSqrd() {
        return getLengthToSqrd(0, 0);
    }
    public float getLengthTo(float x, float y) {
        return (float) Math.sqrt(getLengthToSqrd(x, y));
    }
    public float getLengthTo(Vector v) {
        return getLengthTo(v.x, v.y);
    }
    public float getLength() {
        return getLengthTo(0, 0);
    }
    public Vector setLength(float setLength) {
        float length = getLength();
        x *= setLength / length;
        y *= setLength / length;
        return this;
    }
    public float getFastLengthTo(float x, float y) {
        return getFastLengthTo(new Vector(x, y));
    }
    public float getFastLengthTo(Vector v) {
        float taxiLength = getTaxiCabLengthTo(v);
        float chebyDist = getChebyshevDistanceTo(v);
        return Float.min(taxiLength * 0.7f, chebyDist);
    }
    public float getFastLength() {
        return getLengthTo(0, 0);
    }
    public Vector setFastLength(float setLength) {
        float length = getFastLength();
        x *= setLength / length;
        y *= setLength / length;
        return this;
    }
    public float getTaxiCabLengthTo(float x, float y) {
        return Math.abs(this.x - x) + Math.abs(this.y - y);
    }
    public float getTaxiCabLengthTo(Vector v) {
        return getTaxiCabLengthTo(v.x, v.y);
    }
    public float getTaxiCabLength() {
        return getTaxiCabLengthTo(0, 0);
    }
    public Vector setTaxiCabLength(float setLength) {
        float length = getTaxiCabLength();
        x *= setLength / length;
        y *= setLength / length;
        return this;
    }
    public Vector absIfBoth() {
        if (x < 0 && y < 0)
            return new Vector(-x, -y);
        return this;
    }
    public Vector abs() {
        return new Vector(x < 0 ? -x : x, y < 0 ? -y : y);
    }
    public float getChebyshevDistanceTo(float x, float y) {
        return Math.max(Math.abs(this.x - x), Math.abs(this.y - y));
    }
    public float getChebyshevDistanceTo(Vector v) {
        return getChebyshevDistanceTo(v.x, v.y);
    }
    public float getChebyshevDistance() {
        return getChebyshevDistanceTo(0, 0);
    }
    public Vector setChebyshevLength(float setLength) {
        float length = getChebyshevDistance();
        x *= setLength / length;
        y *= setLength / length;
        return this;
    }
    public Vector sub(Vector v) {
        return new Vector(this.x - v.getX(), this.y - v.getY());
    }
    public Vector add(Vector v) {
        return new Vector(this.x + v.getX(), this.y + v.getY());
    }
    public Vector mul(Vector v) {
        return new Vector(this.x * v.getX(), this.y * v.getY());
    }
    public Vector mul(float f) {
        return mul(new Vector(f, f));
    }
    public Vector div(Vector v) {
        return new Vector(this.x / v.getX(), this.y / v.getY());
    }
    public Vector div(float f) {
        return div(new Vector(f, f));
    }
    public Vector mod(Vector v) {
        return new Vector(this.x % v.getX(), this.y % v.getY());
    }
    public Vector mod(int a, int b) {
        return mod(new Vector(a, b));
    }
    public Vector mod(int a) {
        return mod(a, a);
    }
    public String toString() {
        return "Vector(" + getX() + ", " + getY() + ")";
    }
    /*
     * Returns a list with vectors, starting with this, ending with to, and each
     * one having length between them
     */
    public ArrayList<Vector> loop(Vector to, float length) {
        Vector delta = this.sub(to);
        float l = delta.getLength();
        ArrayList<Vector> loops = new ArrayList<Vector>();
        for (float i = length; i < l; i += length) {
            delta.setLength(i);
            loops.add(delta.add(to));
        }
        loops.add(this);
        return loops;
    }
    public boolean intersects(Vector pos, Vector size) {
        pos.sub(this);
        if (pos.getX() < getX())
            return false;
        if (pos.getY() < getY())
            return false;
        return true;
    }
    public Vector copy() {
        return new Vector(x, y);
    }
    public void distort(float d) {
        x += Math.random() * d - d / 2;
        y += Math.random() * d - d / 2;
    }
    @Override
    public boolean equals(Object o) {
        if (o instanceof Vector) {
            Vector v = (Vector) o;
            return getLengthToSquared(v) < SMALL * SMALL;
        }
        return false;
    }
    private float getLengthToSquared(Vector v) {
        return sub(v).getLengthSquared();
    }
    private float getLengthSquared() {
        return x * x + y * y;
    }
    public boolean kindaEquals(Vector o, int i) {
        if (o.x + i < x)
            return false;
        if (o.x - i > x)
            return false;
        if (o.y + i < y)
            return false;
        if (o.y - i > y)
            return false;
        return true;
    }
    /*
     * Gets the direction, from 0 to 8.
     */
    public int getDirection() {
        return (getDirectionInDegrees()) / (360 / 8);
    }
    /*
     * Gets the direction in degrees.
     */
    public int getDirectionInDegrees() {
        return (int) positize((float) Math.toDegrees(Math.atan2(x, -y)), 360f);
    }
    private float positize(float f, float base) {
        while (f < 0)
            f += base;
        return f;
    }
    // 0 = north,
            // 1 = northeast,
            // 2 = east,
            // 3 = southeast,
            // 4 = south,
            // 5 = southwest,
            // 6 = west,
            // 7 = northwest
    public Vector moveInDir(int d) {
        d = d % 8;
        d = (int) positize(d, 8);
        if (d == 0)
            return this.add(new Vector(0, -1));
        if (d == 1)
            return this.add(new Vector(1, -1));
        if (d == 2)
            return this.add(new Vector(1, 0));
        if (d == 3)
            return this.add(new Vector(1, 1));
        if (d == 4)
            return this.add(new Vector(0, 1));
        if (d == 5)
            return this.add(new Vector(-1, 1));
        if (d == 6)
            return this.add(new Vector(-1, 0));
        if (d == 7)
            return this.add(new Vector(-1, -1));
        return this;
    }
    /*
     * Gets the angle in degrees to o.
     */
    public float getRotationTo(Vector o) {
        float d = (float) Math.toDegrees((Math.atan2(y - o.y, -(x - o.x))));
        while (d < 0)
            d += 360;
        while (d > 360)
            d -= 360;
        return d;
    }
    public float getRotation() {
        return getRotationTo(new Vector(0, 0));
    }
    /*
     * In degrees
     */
    public Vector rotate(double n) {
        n = Math.toRadians(n);
        float rx = (float) ((this.x * Math.cos(n)) - (this.y * Math.sin(n)));
        float ry = (float) ((this.x * Math.sin(n)) + (this.y * Math.cos(n)));
        return new Vector(rx, ry);
    }
    public int hashCode() {
        int xx = (int) x ^ (int)(x * Integer.MAX_VALUE);
        int yy = (int) y ^ (int)(y * Integer.MAX_VALUE);
        return new Random(12665 * xx).nextInt() ^ new Random(5349 * yy).nextInt() + new Random((30513 * xx) ^ (19972 * yy)).nextInt();
    }
    public boolean isPositive() {
        return x >= 0 && y >= 0;
    }
    public Vector clone() {
        return new Vector(x, y);
    }
}
```
## But I don't want to compile a bunch of Java classes!
[Here](http://www.mediafire.com/download/dgazw8w4g4qnaiw/Soil.jar) is a JAR file which you can run to generate these images yourself. Run as `java -jar Soil.jar number`, where `number` is the seed (can be anything up to 231-1), or run as `java -jar Soil.jar`, and it chooses a seed by itself. There will be some debug output.
[Answer]
# Python 3 (using Kivy library and GLSL)
First generated image
[![enter image description here](https://i.stack.imgur.com/CwHhI.png)](https://i.stack.imgur.com/CwHhI.png)
**Python code:**
```
import os
os.environ['KIVY_NO_ARGS'] = '1'
from kivy.config import Config
Config.set('input','mouse','mouse,disable_multitouch')
Config.set('graphics', 'width', '500')
Config.set('graphics', 'height', '500')
Config.set('graphics', 'resizable', '0')
Config.set('graphics', 'borderless', '1')
Config.set('graphics', 'fbo', 'force-hardware')
from kivy.app import App
from kivy.graphics import RenderContext, Fbo, Color, Rectangle
from kivy.clock import Clock
from kivy.uix.floatlayout import FloatLayout
from kivy.factory import Factory
from kivy.core.window import Window
class ShaderSurface(FloatLayout):
    seed = 0.
    def __init__(self, **kwargs):
        self.canvas = RenderContext(use_parent_projection=True, use_parent_modelview=True)
        with self.canvas:
            self.fbo = Fbo(size=Window.size, use_parent_projection=True)
        with self.fbo:
            Color(0,0,0)
            Rectangle(size=Window.size)
        self.texture = self.fbo.texture
        super(ShaderSurface, self).__init__(**kwargs)
        self.keyboard = Window.request_keyboard(self.keyboard_closed, self)
        self.keyboard.bind(on_key_down=self.on_key_down)
        Clock.schedule_once(self.update_shader,-1)
    def keyboard_closed(self):
        self.keyboard.unbind(on_key_down=self.on_key_down)
        self.keyboard = None
    def update_shader(self, dt=0.):
        self.canvas['resolution'] = list(map(float, self.size))
        self.canvas['seed'] = self.seed
        self.canvas.ask_update()
    def on_key_down(self, keyboard, keycode, text, modifiers):
        if keycode[1] == 'spacebar':
            self.seed += 1.
            self.update_shader()
            Window.screenshot()
Factory.register('ShaderSurface', cls=ShaderSurface)
class RendererApp(App):
    def build(self):
        self.root.canvas.shader.source = 'cracks_sub.glsl'
if __name__ == '__main__':
    RendererApp().run()
```
**KV file:**
```
#:kivy 1.9
ShaderSurface:
    canvas:
        Color:
            rgb: 1, 1, 1
        Rectangle:
            size: self.size
            pos: self.pos
            texture: root.fbo.texture
```
**GLSL code:**
```
---VERTEX---
uniform vec2        resolution;
in vec2             vPosition;
void main()
{
    gl_Position = vec4(vPosition.xy-resolution/2., 0, 1);
}
---FRAGMENT---
#version 330
precision highp float;
out vec4 frag_color;
uniform vec2 resolution;
uniform float seed;
vec2 tr(vec2 p)
{
    p /= resolution.xy;
    p = -1.0+2.0*p;
    p.y *= resolution.y/resolution.x;
    return p;
}
float hash( float n ){
    return fract(sin(n)*43758.5453);
}
float noise( vec2 uv ){
    vec3 x = vec3(uv, 0);
    vec3 p = floor(x);
    vec3 f = fract(x);
    f       = f*f*(3.0-2.0*f);
    float n = p.x + p.y*57.0 + 113.0*p.z;
    return mix(mix(mix( hash(n+0.0), hash(n+1.0),f.x),
                   mix( hash(n+57.0), hash(n+58.0),f.x),f.y),
               mix(mix( hash(n+113.0), hash(n+114.0),f.x),
                   mix( hash(n+170.0), hash(n+171.0),f.x),f.y),f.z);
}
mat2 m = mat2(0.8,0.6,-0.6,0.8);
float fbm(vec2 p)
{
    float f = 0.0;
    f += 0.5000*noise( p ); p*=m*2.02;
    f += 0.2500*noise( p ); p*=m*2.03;
    f += 0.1250*noise( p ); p*=m*2.01;
    f += 0.0625*noise( p );
    f /= 0.9375;
    return f;
}
vec2 hash2( vec2 p )
{
    return fract(sin(vec2(dot(p,vec2(127.1,311.7)),dot(p,vec2(269.5,183.3))))*43758.5453);
}
float voronoi(vec2 x, out vec2 rt)
{
    vec2 p = floor(x);
    vec2 f = fract(x);
    vec2 mb, mr;
    float res = 8.0;
    for( int j=-1; j<=1; j++)
    for( int i=-1; i<=1; i++)
    {
        vec2 b = vec2(float(i),float(j));
        vec2 r = b+hash2(p+b)-f;
        float d = dot(r,r);
        if( d<res )
        {
            res = d;
            mr = r;
            mb = b;
            rt=r;
        }
    }
    res = 8.0;
    for( int j=-2; j<=2; j++ )
    for( int i=-2; i<=2; i++ )
    {
        vec2 b = mb + vec2(float(i),float(j));
        vec2 r = b + hash2(p+b)-f;
        float d = dot((res*res)*(mr+r),normalize(r-mr));
        res = min(res,d);
    }
    return res;
}
float crack(vec2 p)
{
    float g = mod(seed,65536./4.);
    p.x+=g;
    p.y-=seed-g;
    p.y*=1.3;
    p.x+=noise(p*4.)*.08;
    float k = 0.;
    vec2 rb = vec2(.0);
    k=voronoi(p*2.,rb);
    k=smoothstep(.0,.3,k*.05);
    float v = 0.;
    v=voronoi(rb*4.,rb);
    v=smoothstep(.0,.5,v*.05);
    k*=v;
    k-=fbm(p*128.)*.3;
    return k;
}
void main( void )
{
    vec2 fc = gl_FragCoord.xy;
    vec2 p = tr(fc);
    vec3 col = vec3(.39,.37,.25);
    vec3 abb = vec3(.14,.12,.10)/5.;
    p*=(1.+length(p)*.1);
    col.r*=crack(vec2(p.x+abb.x,p.y));
    col.g*=crack(vec2(p.x+abb.y,p.y));
    col.b*=crack(vec2(p.x+abb.z,p.y));
    col*=smoothstep(4.,1.2,dot(p,p));
    col*=exp(.66);
    //col=vec3(crack(p));
    frag_color = vec4(col,1.);
}
```
The [voronoi](http://www.iquilezles.org/www/articles/voronoilines/voronoilines.htm) function in the GLSL code is from Íñigo Quílez. Every voronoi related calculation happens in the fragment shader entirely with some procedural noise functions to create speckles and to disturb the lines of the voronoi pattern a bit.
By pressing space the seed will be increased by 1 and a new image will be generated and saved as a `.png` file.
**Update:** Added lense distortion, vignetting and chromatic aberration to make it more photo-realistic. Added sub-voronoi pattern.
[Answer]
# Java
```
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;
import java.util.Scanner;
import javax.imageio.ImageIO;
public class CrackedSoil {
    static BufferedImage b;
    static Random rand;
    public static int distance(int col1,int col2){
        Color a=new Color(col1);
        Color b=new Color(col2);
        return (int)(Math.pow(a.getRed()-b.getRed(), 2)+Math.pow(a.getGreen()-b.getGreen(), 2)+Math.pow(a.getBlue()-b.getBlue(), 2));
    }
    public static void edge(){
        boolean[][] edges=new boolean[500][500];
        int threshold=125+rand.nextInt(55);
        for(int x=1;x<499;x++){
            for(int y=1;y<499;y++){
                int rgb=b.getRGB(x, y);
                int del=0;
                for(int i=-1;i<=1;i++){
                    for(int j=-1;i<=j;i++){
                        del+=distance(rgb,b.getRGB(x+i, y+j));
                    }
                }
                edges[x][y]=del>threshold;
            }
        }
        for(int x=0;x<500;x++){
            for(int y=0;y<500;y++){
                if(edges[x][y])b.setRGB(x, y,new Color(4+rand.nextInt(4),4+rand.nextInt(4),4+rand.nextInt(4)).getRGB());
            }
        }
    }
    public static void main(String[]arg) throws IOException{
        b=new BufferedImage(500,500,BufferedImage.TYPE_INT_RGB);
        Scanner scanner=new Scanner(System.in);
        rand=new Random(scanner.nextInt());
        int numPoints=10+rand.nextInt(15);
        Color[]c=new Color[numPoints];
        int[][]ints=new int[numPoints][2];
        int[]weights=new int[numPoints];
        for(int i=0;i<numPoints;i++){
            switch(i%4){
            case 0:ints[i]=new int[]{251+rand.nextInt(240),7+rand.nextInt(240)};break;
            case 1:ints[i]=new int[]{7+rand.nextInt(240),7+rand.nextInt(240)};break;
            case 2:ints[i]=new int[]{7+rand.nextInt(240),251+rand.nextInt(240)};break;
            case 3:ints[i]=new int[]{251+rand.nextInt(240),251+rand.nextInt(240)};break;
            }
            c[i]=new Color(40+rand.nextInt(200),40+rand.nextInt(200),40+rand.nextInt(200));
            weights[i]=50+rand.nextInt(15);
        }
        for(int x=0;x<500;x++){
            for(int y=0;y<500;y++){
                double d=999999;
                Color col=Color.BLACK;
                for(int i=0;i<numPoints;i++){
                    double d2=weights[i]*Math.sqrt((x-ints[i][0])*(x-ints[i][0])+(y-ints[i][1])*(y-ints[i][1]));
                    if(d2<d){
                        d=d2;
                        col=c[i];
                    }
                }
                b.setRGB(x, y,col.getRGB());
            }
        }
        //ImageIO.write(b,"png",new File("voronoi1.png"));
        for(int i=0;i<numPoints/3;i++){
            ints[i]=new int[]{7+rand.nextInt(490),7+rand.nextInt(490)};
            c[i]=new Color(40+rand.nextInt(200),40+rand.nextInt(200),40+rand.nextInt(200));
            weights[i]=50+rand.nextInt(5);
        }
        for(int x=0;x<500;x++){
            for(int y=0;y<500;y++){
                double d=999999;
                Color col=Color.BLACK;
                for(int i=0;i<numPoints/3;i++){
                    double d2=weights[i]*Math.sqrt((x-ints[i][0])*(x-ints[i][0])+(y-ints[i][1])*(y-ints[i][1]));
                    if(d2<d){
                        d=d2;
                        col=c[i];
                    }
                }
                Color col3=new Color(b.getRGB(x, y));
                b.setRGB(x, y,new Color((col3.getRed()+col.getRed()*3)/4,(col3.getGreen()+col.getGreen()*3)/4,(col3.getBlue()+col.getBlue()*3)/4).getRGB());
            }
        }
        //ImageIO.write(b,"png",new File("voronoi2.png"));
        for(int i=2+rand.nextInt(3);i>0;i--)edge();
        //ImageIO.write(b,"png",new File("voronoi_edge.png"));
        for(int x=0;x<500;x++){
            for(int y=0;y<500;y++){
                Color col=new Color(b.getRGB(x, y));
                if(col.getRed()+col.getBlue()+col.getGreen()>50){
                    if(rand.nextDouble()<0.95){
                        b.setRGB(x, y,new Color(150+rand.nextInt(9),145+rand.nextInt(9),135+rand.nextInt(9)).getRGB());
                    }else{
                        b.setRGB(x, y,new Color(120+col.getRed()/7+rand.nextInt(12),115+col.getGreen()/7+rand.nextInt(12),105+col.getBlue()/7+rand.nextInt(12)).getRGB());
                    }
                }
            }
        }
        ImageIO.write(b,"png",new File("soil.png"));
    }
}
```
Creates a composite of two random diagrams which is then run through a simple edge detection and finally converted to the final result.
**Some outputs:**
[![enter image description here](https://i.stack.imgur.com/WB3St.png)](https://i.stack.imgur.com/WB3St.png)
[![enter image description here](https://i.stack.imgur.com/hGjZc.png)](https://i.stack.imgur.com/hGjZc.png)
[![enter image description here](https://i.stack.imgur.com/6QRXX.png)](https://i.stack.imgur.com/6QRXX.png)
**Some of the intermediate steps for that last one:**
[![enter image description here](https://i.stack.imgur.com/aENel.png)](https://i.stack.imgur.com/aENel.png)
(The first voronoi diagram)
[![enter image description here](https://i.stack.imgur.com/WlTr1.png)](https://i.stack.imgur.com/WlTr1.png)
(The composite of the two voronoi diagrams)
[![enter image description here](https://i.stack.imgur.com/hZiom.png)](https://i.stack.imgur.com/hZiom.png)
(After the edge detection step but before the final recoloring)
]