컴퓨터 생성 금이 토양


43

0에서 65535까지의 정수 (2 16 -1)를 사용하고 갈라진 토양의 6 가지 실제 이미지와 가능한 유사하게 보이는 고유 한 500x500 픽셀 이미지를 생성 하는 프로그램을 작성하십시오 .

금이 토양 샘플 1 갈라진 토양 샘플 2 갈라진 토양 샘플 3 갈라진 토양 샘플 4 갈라진 토양 샘플 5 갈라진 토양 샘플 6
썸네일입니다. 클릭하면 전체 크기 500 × 500 이미지를 볼 수 있습니다.

여기서 목표는 컴퓨터에서 생성 된 이미지를 최대한 사실적으로 만드는 것 입니다. 따라서 이상적으로, 프로그램에서 출력 한 이미지 중 하나가 위의 6 개 이미지와 혼합 된 경우, 처음 이미지를 본 사람은 컴퓨터에서 실제 이미지와 별개로 생성 된 이미지를 말할 수 없습니다.

완벽한 포토 리얼리즘은 까다롭기 때문에 최선을 다하십시오. 이것은 이므로보다 사실적인 결과를 가진 답변이 더 많이 투표되고 이길 가능성이 높습니다.

규칙

  • 이미지 처리 기능 또는 라이브러리를 사용할 수 있습니다.

  • 사용자는 6 개 샘플 이미지에서 수집 된 정보에 대한 알고리즘을 기반으로 할 수있다,하지만 65536 (2 16 ) 가능한 출력 영상은 서로로부터 시각적으로 구별 될 수 있어야 하고 , 특히 크랙의 배치와 관련하여, 샘플 이미지. 당신은 진정으로 이미지를 생성하고, 기존의 사진에서 선택한 것을 회전시키고 번역하지 마십시오.

  • 그렇지 않으면 출력을 하드 코딩해서는 안됩니다. 이론적으로 일반적인 알고리즘을 사용해야하며 65535보다 큰 숫자는 이론적으로 유효한 출력을 생성해야합니다. (나는 단지 작은 최대 정수 유형을 수용하도록 제한했습니다.)

  • 입력 정수는 임의의 갈라진 토양 출력 이미지를 생성하는 시드로 생각할 수 있습니다. 그러나 결정 론적이어야하므로 동일한 입력이 항상 동일한 출력을 가져야합니다.

  • 출력 이미지는 정확히 500 × 500 픽셀이어야합니다.

  • 출력 이미지는 일반적인 이미지 파일 형식으로 저장되거나 간단하게 표시 될 수 있습니다.

  • 답변에 출력 이미지 예와 해당 입력 번호를 포함시켜야합니다.

  • 가장 많은 표를 얻은 답이 이깁니다. 유권자들은 물론 6 가지 샘플과 유사한 이미지를 만들려고 시도하는 답변을 투표하고 규칙을 어기거나 일관성이없는 결과를 제공하는 답변을 내려야합니다.

6 개의 샘플 이미지는 texturelib.com 에서 가져 왔습니다 . 갈라진 토양의 두 개의 더 큰 이미지 에서 1000 x 1000 픽셀 영역을 선택한 다음 500 x 500으로 크기를 조정했습니다. 프로그램에서 이러한 큰 이미지의 분석을 사용할 수 있지만 출력은 6 개의 선택된 샘플 이미지를 구체적으로 모방해야합니다.


6
나는 객관적인 타당성 기준 이 없기 때문에이 과제를 너무 광범위 하게 마무리하기로 결정했습니다 .
AdmBorkBork 2016 년

4
@HelkaHomba 여부는 오래된 문제는 잘 받았습니다 또는 도전 여부에 관계가 안되지 지금은 합의에 의해 결정으로 사이트의 규칙을 맞는다. 지난 몇 달 동안 PopCons는 엄청난 토론을 해왔으며 그 결과 중 하나는 모든 PopCon에 객관적인 유효성 기준이 필요하다는 것이 었습니다. 이 도전에는 없습니다. 따라서 너무 넓습니다.
AdmBorkBork 2016 년

15
팝 콘에 대한 현재 규칙은 너무 바보이므로 무시할 수있는 기회를 취하고 그것이 어떻게 작동하는지 봅니다. 이 주제는 메타에 관한 것이었지만 실제로는 아무것도 변하지 않았습니다. 그래서 어떤 일이 일어날 때 가장 좋은 기회는 팝 콘을 지키고 어떻게 행동하는지 보는 것입니다.
xnor

6
여기서 객관적인 유효성 기준은 "고유"(다른 65535와 구별됨) 및 "500x500 픽셀"입니다. 예제 이미지와의 유사성을 객관적으로 정의 할 수 없거나 인기 경연 대회가 아니라 코드 문제 일 수 있습니다.
trichoplax 2016 년

14
나는 "예쁜 것 만들기"와 같은 나쁜 팝 단점을 제한없이 보았으며, "이 사양과 일치"와 같은 좋은 팝 단점은 사람과 가장 잘 어울리는 투표를합니다. 나는 분명히이 도전을 좋은 종류로 본다.
trichoplax 2016 년

답변:


30

매스 매 티카

보로 노이 다이어그램의 모양은 각각 하나의 시드 포인트를 포함하는 셀 (19)을 나타내는, 위키에서 본 도면처럼. 셀은 각 생성 지점이 다른 시드 지점보다 더 가까운 지점의 하위 영역으로 구성됩니다.

보로 노이

아래 코드는 (-1, -1) 및 (1,1)으로 묶인 사각형 영역에서 80 개의 임의의 점으로 다이어그램을 생성합니다.

다이어그램의 다각형 기본 요소 (2D)를 사용하여 다면체 (3D)를 만듭니다. 각 다각형 아래에 그 자체의 번역 (-.08은 z)이 있다고 상상해보십시오. 두 다각형을 다면체의 상하면으로 생각하십시오. 그런 다음 "측면"을 추가하여 다면체를 완성합니다.

그런 다음 각 다면체는 이미지 중심에서 xy 평면으로 바깥쪽으로 변환됩니다. 중간에서 멀어집니다. 변환의 크기는 다면체의 원래 생성 된 임의의 점과 화면 중앙 사이의 거리에 따라 직접 다릅니다. xy 평면에서 다면체의 이러한 "확산"은 틈새를 초래합니다.

crackedMud[1]

하나

crackedMud[65535]

마지막

암호

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

산산이 부서진 유리 패턴 메이커에 적응하는 것이 좋습니다.
Sparr

@Sparr, 예. 부서진 유리 (또는 타일)처럼 보입니다.
DavidC

골프 ........?
고양이

@cat 아니오, 골프를 치지 않았습니다.
DavidC

@DavidC 공백은 어디에 있습니까? 그렇게 쓰시나요? Wolfram은 읽을 수없는 코드를 시행합니까?
고양이

24

자바

나는 재귀 Voronoi 다이어그램을 기반으로 한 접근법을 사용했습니다. 결과물은 매우 사실적으로 보이지 않지만 괜찮습니다.

다음은 이미지 예입니다 (전체 화면을 채우지 않도록 250x250 크기로 조정 됨).

0 :

이미지 0

1:

이미지 1

알고리즘에 대한 자세한 내용 :

이 섹션의 모든 이미지는 동일한 시드를 사용합니다.

이 알고리즘은 5 포인트로 Voronoi 다이어그램을 생성하여 시작됩니다.

보로 노이 다이어그램

챌린지에서 원본 이미지를 보면 선이 모두 똑 바르지 않다는 것을 알 수 있으므로 점에 대한 각도를 기준으로 임의의 값으로 거리를 측정합니다. :

가중 보로 노이 다이어그램

이제 각 영역 내부에 이러한 종류의 보로 노이 다이어그램을 재귀 적으로 얇고 투명한 선으로 그리고 최대 재귀 깊이 3으로 배경을 제거하면 다음과 같은 결과를 얻습니다.

재귀 보로 노이

이제 옅은 갈색 배경을 추가하면 끝났습니다!

끝난!

암호:

코드는 세 개의 클래스로 구성되어 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: (이 클래스는 다른 프로젝트 중 하나에서 복사되었으므로 불필요한 코드가 포함되어 있습니다)

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

그러나 많은 Java 클래스를 컴파일하고 싶지 않습니다!

다음 은 이러한 이미지를 직접 생성하기 위해 실행할 수있는 JAR 파일입니다. 시드는 java -jar Soil.jar number어디에서 실행 number합니까 (최대 2 31 -1까지 가능) 또는으로 실행되며 java -jar Soil.jar시드 자체를 선택합니다. 디버그 출력이있을 것입니다.


어떤 이유로 나는 그 이미지가 상당히 사실적이고 완전히 가짜라는 것을 알았습니다. 자연스러운 그림자가 없기 때문에 나를 버리고 있습니다.
치명적인

도움이되는 경우 전체 크기의 이미지를 업로드하고 챌린지 게시물과 같은 작은 축소판 또는 세로 공간을 덜 차지하기 위해 2 개 크기의 중간 이미지를 만들 수 있습니다. 도전소스 에서 이미지 주소에 "s"를 추가하여 이미지를 작게 만드는 방법과 매체에 "m"을 사용할 수도 있습니다. 소스는 또한 작은 이미지를 전체 크기 이미지에 대한 링크로 만드는 방법을 보여줍니다.
trichoplax

2
색상이 회색에 가깝고 베이지가 적을수록 훨씬 더 가깝습니다. 그러나 그렇지 않으면 좋은 대답입니다!
Calvin 's Hobbies

12

Python 3 (Kivy 라이브러리 및 GLSL 사용)

처음 생성 된 이미지

여기에 이미지 설명을 입력하십시오

파이썬 코드 :

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 파일 :

#:kivy 1.9

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

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

보로 노이 GLSL 코드의 기능 이니 Quílez에서입니다. 모든 보로 노이 관련 계산은 프래그먼트 셰이더에서 전체적으로 절차 적 노이즈 함수를 사용하여 스펙 클을 생성하고 보로 노이 패턴의 선을 약간 방해합니다.

공간을 누르면 시드가 1 씩 증가하고 새로운 이미지가 생성되어 .png파일 로 저장됩니다 .

업데이트 : 렌즈 왜곡, 비네팅 및 색수차를 추가하여보다 사실적으로 만듭니다. 서브 보로 노이 패턴을 추가했습니다.


이것은 씨앗을 입력으로 사용할 수 있습니까?
trichoplax

ShaderSurface 클래스에는 클래스 멤버가 seed있습니다. 균일 한 float 변수로 셰이더에 파이프됩니다. 셰이더의 균열 함수에서 시드는 시드 값으로 포인트를 변환하는 데 사용됩니다.
Gábor Fekete

1

자바

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

두 개의 랜덤 다이어그램을 합성 한 다음 간단한 에지 감지를 거쳐 최종 결과로 변환됩니다.

일부 출력 :

여기에 이미지 설명을 입력하십시오

여기에 이미지 설명을 입력하십시오

여기에 이미지 설명을 입력하십시오

마지막 단계의 중간 단계 중 일부 :

여기에 이미지 설명을 입력하십시오

(첫 번째 보로 노이 다이어그램)

여기에 이미지 설명을 입력하십시오

(두 보로 노이 다이어그램의 합성)

여기에 이미지 설명을 입력하십시오

(가장자리 감지 단계 후 최종 색상 재조정 전)

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.