Máy tính tạo ra đất nứt


43

Viết chương trình lấy số nguyên từ 0 đến 65535 (2 16 -1) và tạo ra một hình ảnh 500 × 500 pixel duy nhất trông giống nhất có thể với 6 hình ảnh thực tế này của đất bị nứt:

mẫu đất nứt 1 mẫu đất nứt 2 mẫu đất nứt 3 mẫu đất nứt 4 mẫu đất nứt 5 mẫu đất nứt 6
Đây là những hình thu nhỏ, nhấp vào chúng để xem hình ảnh kích thước đầy đủ 500 × 500.

Mục tiêu ở đây là làm cho máy tính của bạn tạo ra hình ảnh càng linh hoạt càng tốt . Vì vậy, lý tưởng nhất là nếu bất kỳ hình ảnh nào được tạo ra bởi chương trình của bạn được trộn lẫn với 6 hình ảnh ở trên, ai đó lần đầu tiên nhìn thấy hình ảnh sẽ không thể phân biệt được máy tính được tạo ra ngoài hình ảnh thật.

Tuy nhiên, photorealism hoàn hảo là khó khăn, vì vậy chỉ cần làm tốt nhất có thể. Đây là một vì vậy các câu trả lời có kết quả thực tế hơn sẽ được nâng cao hơn và có nhiều khả năng giành chiến thắng.

Quy tắc

  • Bạn có thể sử dụng các chức năng xử lý hình ảnh hoặc thư viện.

  • Bạn có thể dựa trên thuật toán của mình dựa trên thông tin được thu thập từ 6 hình ảnh mẫu, nhưng hình ảnh đầu ra có thể 65536 (2 16 ) của bạn phải khác biệt về mặt trực quan với nhau hình ảnh mẫu, đặc biệt là liên quan đến việc sắp xếp các vết nứt. Bạn phải thực sự tạo ra hình ảnh của mình, không chỉ xoay và dịch một lựa chọn từ một bức ảnh có sẵn.

  • Bạn không nên mã hóa đầu ra của bạn. Một thuật toán chung nên được sử dụng và các số lớn hơn 65535 về mặt lý thuyết sẽ tạo ra các đầu ra hợp lệ. (Tôi đã giới hạn nó chỉ để chứa các loại số nguyên tối đa nhỏ.)

  • Số nguyên đầu vào có thể được coi là một hạt giống dẫn đến hình ảnh đầu ra đất bị nứt ngẫu nhiên. Nó nên được xác định mặc dù, do đó, cùng một đầu vào sẽ luôn luôn dẫn đến cùng một đầu ra.

  • Các hình ảnh đầu ra phải chính xác 500 × 500 pixel.

  • Các hình ảnh đầu ra có thể được lưu trong bất kỳ định dạng tệp hình ảnh phổ biến, hoặc hiển thị đơn giản.

  • Hãy chắc chắn bao gồm một vài hình ảnh đầu ra ví dụ trong câu trả lời của bạn và số đầu vào tương ứng của chúng.

  • Câu trả lời có nhiều phiếu nhất sẽ thắng. Các cử tri tất nhiên nên đưa ra các câu trả lời cố gắng tạo ra các hình ảnh tương tự như 6 mẫu và hạ thấp các câu trả lời phá vỡ các quy tắc hoặc đưa ra kết quả không nhất quán.

6 hình ảnh mẫu được lấy từ texturelib.com . Các lựa chọn diện tích 1000 × 1000 pixel được lấy từ hai hình ảnh lớn hơn của đất bị nứt và sau đó thay đổi kích thước thành 500 × 500. Bạn có thể sử dụng phân tích từ những hình ảnh lớn hơn này trong chương trình của mình nhưng đầu ra phải bắt chước cụ thể 6 hình ảnh mẫu được chọn.


6
Tôi đã bỏ phiếu để đóng thử thách này là Quá rộng vì nó thiếu các tiêu chí hợp lệ khách quan .
admBorkBork

4
@HelkaHomba Cho dù một thách thức cũ đã được đón nhận hay không nên không liên quan đến việc liệu một thách thức bây giờ có phù hợp với các quy tắc của trang web như được quyết định bởi sự đồng thuận hay không. PopCons đã có cuộc thảo luận rất lớn trong những tháng qua, một trong những kết quả là tất cả các PopCons cần một tiêu chí hợp lệ khách quan. Thử thách này không có điều đó. Do đó, nó quá rộng.
admBorkBork

15
Các quy tắc hiện tại về khuyết điểm nhạc pop rất ngu ngốc đến nỗi tôi nhân cơ hội này để bỏ qua chúng và xem nó hoạt động ra sao. Chủ đề này đã được đưa lên trên meta, nơi nó được thảo luận cho đến chết nhưng thực tế không có gì thay đổi, vì vậy tôi nghĩ cơ hội tốt nhất để xảy ra là giữ một số khuyết điểm pop và xem cách họ làm.
xnor

6
Các tiêu chí hợp lệ khách quan ở đây là "duy nhất" (khác với 65535 khác) và "500x500 pixel". Sự tương đồng với các hình ảnh ví dụ không thể được xác định một cách khách quan, hoặc đây sẽ không phải là một cuộc thi phổ biến mà là một thách thức về mã.
trichoplax

14
Tôi thấy những khuyết điểm pop xấu như "làm cho một cái gì đó đẹp" không bị hạn chế, và những khuyết điểm pop tốt như "phù hợp với đặc điểm kỹ thuật này" với con người bỏ phiếu về cái nào phù hợp nhất. Tôi chắc chắn xem thử thách này là loại tốt.
trichoplax

Câu trả lời:


30

Toán học

Một sơ đồ Voronoi trông giống như bản vẽ này, từ Wikipedia, hiển thị 19 ô, mỗi ô chứa một điểm giống. Một ô bao gồm các tiểu vùng của điểm mà điểm tạo tương ứng gần với bất kỳ điểm hạt giống nào khác.

voronoi

Mã dưới đây tạo ra một sơ đồ từ 80 điểm ngẫu nhiên (trong vùng hình vuông bị ràng buộc bởi (-1, -1) và (1,1)).

Nó sử dụng các nguyên thủy đa giác (ở dạng 2D) trong sơ đồ để xây dựng khối đa diện (ở dạng 3D). Hãy tưởng tượng rằng mỗi đa giác có, ngay dưới nó, một bản dịch (-, 08 in z) của chính nó. Hãy nghĩ về hai đa giác là mặt trên và mặt dưới của khối đa diện. "Các mặt bên" sau đó được thêm vào để hoàn thành khối đa diện.

Mỗi khối đa diện sau đó được dịch ra ngoài, từ tâm hình ảnh, trên mặt phẳng xy; nó di chuyển từ giữa Độ lớn của bản dịch thay đổi trực tiếp với khoảng cách giữa điểm ngẫu nhiên ban đầu của khối đa diện và tâm của màn hình. Điều này "trải ra" của khối đa diện trong mặt phẳng xy dẫn đến các kẽ hở.

crackedMud[1]

một

crackedMud[65535]

Cuối cùng

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}}]
  ] 

Bạn có thể làm tốt để thích nghi với điều này với một nhà sản xuất mô hình kính vỡ.
Sparr

@Sparr, vâng, nó trông giống như kính vỡ (hoặc gạch).
DavidC

Chơi gôn ........?
con mèo

@cat Không, nó không được đánh gôn.
DavidC

@DavidC Tất cả các khoảng trắng ở đâu? Bạn có viết nó như thế không? Do Wolfram thi hành mã không thể đọc được?
mèo

24

Java

Tôi đã sử dụng một cách tiếp cận dựa trên sơ đồ Voronoi đệ quy. Các kết quả đầu ra trông không thực tế lắm, nhưng tôi đoán chúng ổn.

Dưới đây là một số hình ảnh ví dụ (được thay đổi kích thước thành 250x250 để nó không lấp đầy toàn bộ màn hình):

0:

Hình 0

1:

Hình 1

Thêm chi tiết về thuật toán:

Tất cả các hình ảnh trong phần này đang sử dụng cùng một hạt giống.

Thuật toán bắt đầu bằng cách tạo sơ đồ Voronoi với 5 điểm:

Sơ đồ Voronoi

Nếu chúng ta nhìn vào các hình ảnh gốc trong thử thách, chúng ta có thể thấy rằng các đường thẳng không giống nhau, vì vậy chúng ta cân nhắc khoảng cách bằng một giá trị ngẫu nhiên, dựa trên góc tới điểm, ngoài ra, các góc gần hơn sẽ cho các giá trị gần hơn :

Biểu đồ Voronoi có trọng số

Bây giờ, chúng tôi vẽ đệ quy các loại sơ đồ Voronoi bên trong mỗi vùng, với đường kẻ mỏng hơn và trong suốt hơn, và xóa nền, với độ sâu đệ quy tối đa là 3 và chúng tôi nhận được:

Voronoi đệ quy

Bây giờ, chúng ta chỉ cần thêm nền màu nâu nhạt, và chúng ta đã hoàn thành!

Làm xong!

Mã số:

Mã này bao gồm ba lớp, Main.java, VoronoiPoint.javaVector.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: (Lớp này được sao chép từ một trong các dự án khác của tôi, vì vậy nó chứa một số mã không cần thiết)

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);
    }
}

Nhưng tôi không muốn biên dịch một loạt các lớp Java!

Đây là một tệp JAR mà bạn có thể chạy để tự tạo các hình ảnh này. Chạy như là java -jar Soil.jar number, numberhạt giống ở đâu (có thể là bất cứ thứ gì lên đến 2 31 -1), hoặc chạy như java -jar Soil.jar, và nó tự chọn một hạt giống. Sẽ có một số đầu ra gỡ lỗi.


Vì một số lý do, tôi thấy những hình ảnh đó khá thực tế và cũng hoàn toàn giả mạo. Việc thiếu bóng tự nhiên đang ném tôi đi.
Gây tử vong vào

Nếu nó hữu ích, bạn có thể tải lên hình ảnh có kích thước đầy đủ và biến chúng thành hình thu nhỏ nhỏ như trong bài đăng thử thách hoặc hình ảnh trung bình vừa với 2 để có ít không gian theo chiều dọc. Trong nguồn của thử thách, bạn có thể thấy cách thêm "s" vào địa chỉ imgur làm cho hình ảnh trở nên nhỏ và bạn cũng có thể sử dụng "m" cho phương tiện. Nguồn cũng cho thấy làm thế nào để làm cho hình ảnh nhỏ thành một liên kết đến hình ảnh kích thước đầy đủ.
trichoplax

2
Tôi nghĩ rằng màu sắc có thể gần hơn rất nhiều - nhiều màu xám hơn, ít màu be hơn. Nhưng nếu không thì trả lời tốt đẹp!
Sở thích của Calvin

12

Python 3 (sử dụng thư viện Kivy và GLSL)

Hình ảnh được tạo đầu tiên

nhập mô tả hình ảnh ở đây

Mã Python:

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()

Tập tin KV:

#:kivy 1.9

ShaderSurface:
    canvas:
        Color:
            rgb: 1, 1, 1
        Rectangle:
            size: self.size
            pos: self.pos
            texture: root.fbo.texture

Mã GLSL:

---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.);
}

Hàm voronoi trong mã GLSL là từ Íñigo Quílez. Mỗi phép tính liên quan đến voronoi xảy ra trong shader mảnh hoàn toàn với một số hàm nhiễu thủ tục để tạo các đốm và làm xáo trộn các đường của mẫu voronoi một chút.

Bằng cách nhấn khoảng trắng, hạt giống sẽ được tăng thêm 1 và một hình ảnh mới sẽ được tạo và lưu dưới dạng .pngtệp.

Cập nhật: Đã thêm biến dạng lense, họa tiết và quang sai màu để làm cho nó chân thực hơn. Đã thêm mẫu phụ voronoi.


Điều này cũng có thể lấy một hạt giống như đầu vào?
trichoplax

Lớp ShaderSurface có một thành viên lớp seed. Điều này sẽ được dẫn đến shader như một biến float đồng nhất. Trong chức năng bẻ khóa của shader, hạt giống được sử dụng để dịch điểm theo giá trị của hạt giống.
Gábor Fekete

1

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"));
    }
}

Tạo một hỗn hợp gồm hai sơ đồ ngẫu nhiên, sau đó được chạy qua một phát hiện cạnh đơn giản và cuối cùng được chuyển đổi thành kết quả cuối cùng.

Một số kết quả đầu ra:

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây

Một số bước trung gian cho bước cuối cùng đó:

nhập mô tả hình ảnh ở đây

(Sơ đồ voronoi đầu tiên)

nhập mô tả hình ảnh ở đây

(Tổng hợp của hai sơ đồ voronoi)

nhập mô tả hình ảnh ở đây

(Sau bước phát hiện cạnh nhưng trước khi đổi màu cuối cùng)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.