0에서 65535까지의 정수 (2 16 -1)를 사용하고 갈라진 토양의 6 가지 실제 이미지와 가능한 유사하게 보이는 고유 한 500×500 픽셀 이미지를 생성 하는 프로그램을 작성하십시오 .
썸네일입니다. 클릭하면 전체 크기 500 × 500 이미지를 볼 수 있습니다.
여기서 목표는 컴퓨터에서 생성 된 이미지를 최대한 사실적으로 만드는 것 입니다. 따라서 이상적으로, 프로그램에서 출력 한 이미지 중 하나가 위의 6 개 이미지와 혼합 된 경우, 처음 이미지를 본 사람은 컴퓨터에서 실제 이미지와 별개로 생성 된 이미지를 말할 수 없습니다.
완벽한 포토 리얼리즘은 까다롭기 때문에 최선을 다하십시오. 이것은 인기있는 경연 대회 이므로보다 사실적인 결과를 가진 답변이 더 많이 투표되고 이길 가능성이 높습니다.
규칙
-
이미지 처리 기능 또는 라이브러리를 사용할 수 있습니다.
-
사용자는 6 개 샘플 이미지에서 수집 된 정보에 대한 알고리즘을 기반으로 할 수있다,하지만 65536 (2 16 ) 가능한 출력 영상은 서로로부터 시각적으로 구별 될 수 있어야 하고 , 특히 크랙의 배치와 관련하여, 샘플 이미지. 당신은 진정으로 이미지를 생성하고, 기존의 사진에서 선택한 것을 회전시키고 번역하지 마십시오.
-
그렇지 않으면 출력을 하드 코딩해서는 안됩니다. 이론적으로 일반적인 알고리즘을 사용해야하며 65535보다 큰 숫자는 이론적으로 유효한 출력을 생성해야합니다. (나는 단지 작은 최대 정수 유형을 수용하도록 제한했습니다.)
-
입력 정수는 임의의 갈라진 토양 출력 이미지를 생성하는 시드로 생각할 수 있습니다. 그러나 결정 론적이어야하므로 동일한 입력이 항상 동일한 출력을 가져야합니다.
-
출력 이미지는 정확히 500 × 500 픽셀이어야합니다.
-
출력 이미지는 일반적인 이미지 파일 형식으로 저장되거나 간단하게 표시 될 수 있습니다.
-
답변에 출력 이미지 예와 해당 입력 번호를 포함시켜야합니다.
-
가장 많은 표를 얻은 답이 이깁니다. 유권자들은 물론 6 가지 샘플과 유사한 이미지를 만들려고 시도하는 답변을 투표하고 규칙을 어기거나 일관성이없는 결과를 제공하는 답변을 내려야합니다.
6 개의 샘플 이미지는 texturelib.com 에서 가져 왔습니다 . 갈라진 토양의 두 개의 더 큰 이미지 에서 1000 x 1000 픽셀 영역을 선택한 다음 500 x 500으로 크기를 조정했습니다. 프로그램에서 이러한 큰 이미지의 분석을 사용할 수 있지만 출력은 6 개의 선택된 샘플 이미지를 구체적으로 모방해야합니다.
답변
매스 매 티카
보로 노이 다이어그램의 모양은 각각 하나의 시드 포인트를 포함하는 셀 (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}}]
]
답변
자바
나는 재귀 Voronoi 다이어그램을 기반으로 한 접근법을 사용했습니다. 결과물은 매우 사실적으로 보이지 않지만 괜찮습니다.
다음은 이미지 예입니다 (전체 화면을 채우지 않도록 250×250 크기로 조정 됨).
0 :
1:
알고리즘에 대한 자세한 내용 :
이 섹션의 모든 이미지는 동일한 시드를 사용합니다.
이 알고리즘은 5 포인트로 Voronoi 다이어그램을 생성하여 시작됩니다.
챌린지에서 원본 이미지를 보면 선이 모두 똑 바르지 않다는 것을 알 수 있으므로 점에 대한 각도를 기준으로 임의의 값으로 거리를 측정합니다. :
이제 각 영역 내부에 이러한 종류의 보로 노이 다이어그램을 재귀 적으로 얇고 투명한 선으로 그리고 최대 재귀 깊이 3으로 배경을 제거하면 다음과 같은 결과를 얻습니다.
이제 옅은 갈색 배경을 추가하면 끝났습니다!
암호:
코드는 세 개의 클래스로 구성되어 Main.java
, VoronoiPoint.java
및 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
: (이 클래스는 다른 프로젝트 중 하나에서 복사되었으므로 불필요한 코드가 포함되어 있습니다)
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
시드 자체를 선택합니다. 디버그 출력이있을 것입니다.
답변
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
파일 로 저장됩니다 .
업데이트 : 렌즈 왜곡, 비네팅 및 색수차를 추가하여보다 사실적으로 만듭니다. 서브 보로 노이 패턴을 추가했습니다.
답변
자바
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"));
}
}
두 개의 랜덤 다이어그램을 합성 한 다음 간단한 에지 감지를 거쳐 최종 결과로 변환됩니다.
일부 출력 :
마지막 단계의 중간 단계 중 일부 :
(첫 번째 보로 노이 다이어그램)
(두 보로 노이 다이어그램의 합성)
(가장자리 감지 단계 후 최종 색상 재조정 전)