我浏览了整个互联网并研究了柏林的噪音,但是,我仍感到困惑。
我正在使用java和 libgdx 。我有一个Perlin课程可以工作并产生噪音,但我不确定它给出的值是否正确。我如何检查它实际输出的是Perlin噪音?
如果我的实施是正确的,我不知道从那里去哪里制作随机地形。我如何将Perlin噪音映射到瓷砖?目前我有4个基本瓷砖;水,沙子,岩石和草。
package com.bracco.thrive.world;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.Texture;
public class WorldGeneration {
Perlin noise = new Perlin();
private SpriteBatch spriteBatch;
//private boolean debug = false;
private TextureRegion[] regions = new TextureRegion[4];
private Texture texture;
float x = 110;
float y = 120;
float originX = 0;
float originY = 16;
float width = 16;
float height = 16;
float scaleX = 1;
float scaleY = 1;
float rotation = 1;
@SuppressWarnings("static-access")
public void createWorld(){
spriteBatch = new SpriteBatch();
texture = new Texture(Gdx.files.internal("assets/data/textures/basictextures.png"));
regions[0] = new TextureRegion(texture,0,0,16,16); //grass
regions[1] = new TextureRegion(texture,16,0,16,16); //water
regions[2] = new TextureRegion(texture,0,17,16,16); //sand
regions[3] = new TextureRegion(texture,17,17,16,16); //rock
float[][] seed = noise.GenerateWhiteNoise(50, 50);
for (int i = 0;i < seed.length; i++){
for ( int j = 0; j < seed[i].length; j++){
System.out.println(seed[i][j] + " ");
}
}
float[][] seedE = noise.GenerateSmoothNoise( seed, 6);
for (int i = 0;i < seedE.length; i++){
for ( int j = 0; j < seedE[i].length; j++){
System.out.println(seedE[i][j] + " ");
}
}
float[][] perlinNoise = noise.GeneratePerlinNoise(seedE, 8);
for (int i = 0;i < perlinNoise.length; i++){
for ( int j = 0; j < perlinNoise[i].length; j++){
System.out.println(perlinNoise[i][j] + " ");
}
}
}
public void render(){
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
spriteBatch.begin();
//spriteBatch.draw(texture, 0, 0, 16, 16);
for (int i = 0; i < regions.length; i++){
spriteBatch.draw(regions[i],75 * (i + 1),100);
}
spriteBatch.end();
}
}
package com.bracco.thrive.world;
import java.util.Random;
public class Perlin {
public static float[][] GenerateWhiteNoise(int width,int height){
Random random = new Random((long) (Math.round(Math.random() * 100 * Math.random() * 10))); //Seed to 0 for testing
float[][] noise = new float[width][height];
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++){
noise[i][j] = (float)(Math.random() % 1);
}
}
return noise;
}
float[][] GenerateSmoothNoise(float[][] baseNoise, int octave)
{
int width = baseNoise.length;
int height = baseNoise.length;
float[][] smoothNoise = new float[width][height];
int samplePeriod = 1 << octave; // calculates 2 ^ k
float sampleFrequency = 1.0f / samplePeriod;
for (int i = 0; i < width; i++)
{
//calculate the horizontal sampling indices
int sample_i0 = (i / samplePeriod) * samplePeriod;
int sample_i1 = (sample_i0 + samplePeriod) % width; //wrap around
float horizontal_blend = (i - sample_i0) * sampleFrequency;
for (int j = 0; j < height; j++)
{
//calculate the vertical sampling indices
int sample_j0 = (j / samplePeriod) * samplePeriod;
int sample_j1 = (sample_j0 + samplePeriod) % height; //wrap around
float vertical_blend = (j - sample_j0) * sampleFrequency;
//blend the top two corners
float top = Interpolate(baseNoise[sample_i0][sample_j0],
baseNoise[sample_i1][sample_j0], horizontal_blend);
//blend the bottom two corners
float bottom = Interpolate(baseNoise[sample_i0][sample_j1],
baseNoise[sample_i1][sample_j1], horizontal_blend);
//final blend
smoothNoise[i][j] = Interpolate(top, bottom, vertical_blend);
}
}
return smoothNoise;
}
float Interpolate(float x0, float x1, float alpha)
{
return x0 * (1 - alpha) + alpha * x1;
}
float[][] GeneratePerlinNoise(float[][] baseNoise, int octaveCount)
{
int width = baseNoise.length;
int height = baseNoise[0].length;
float[][][] smoothNoise = new float[octaveCount][][]; //an array of 2D arrays containing
float persistance = 0.5f;
//generate smooth noise
for (int i = 0; i < octaveCount; i++)
{
smoothNoise[i] = GenerateSmoothNoise(baseNoise, i);
}
float[][] perlinNoise = new float[width][height];
float amplitude = 1.0f;
float totalAmplitude = 0.0f;
//blend noise together
for (int octave = octaveCount - 1; octave >= 0; octave--)
{
amplitude *= persistance;
totalAmplitude += amplitude;
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
perlinNoise[i][j] += smoothNoise[octave][i][j] * amplitude;
}
}
}
//normalisation
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
perlinNoise[i][j] /= totalAmplitude;
}
}
return perlinNoise;
}
}
答案 0 :(得分:4)
perlin噪音的正确性
关于你的perlin噪音是否“正确”;最简单的方法是看你的perlin噪音(或基于几个八度音程的技术分形噪音)是否正常工作是使用你的perlin噪音的值来生成灰度图像,这个图像应该看起来喜欢某种景观(连绵起伏的丘陵或山脉,取决于您为持久性选择的参数(以及较少程度的八度音阶)。一些perlin噪音的例子是:
低俗:
或
高度不满:
或
高度差异(缩小):
这些灰度图像由以下代码生成
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class ImageWriter {
//just convinence methods for debug
public static void greyWriteImage(double[][] data){
//this takes and array of doubles between 0 and 1 and generates a grey scale image from them
BufferedImage image = new BufferedImage(data.length,data[0].length, BufferedImage.TYPE_INT_RGB);
for (int y = 0; y < data[0].length; y++)
{
for (int x = 0; x < data.length; x++)
{
if (data[x][y]>1){
data[x][y]=1;
}
if (data[x][y]<0){
data[x][y]=0;
}
Color col=new Color((float)data[x][y],(float)data[x][y],(float)data[x][y]);
image.setRGB(x, y, col.getRGB());
}
}
try {
// retrieve image
File outputfile = new File("saved.png");
outputfile.createNewFile();
ImageIO.write(image, "png", outputfile);
} catch (IOException e) {
//o no!
}
}
public static void main(String args[]){
double[][] data=new double[2][4];
data[0][0]=0.5;
data[0][5]=1;
data[1][0]=0.7;
data[1][6]=1;
greyWriteImage(data);
}
}
此代码假设每个条目介于0和1之间,但perlin噪声通常在-1和1之间产生,根据您的实施进行缩放。假设你的perlin噪声会给出任何x,y的值,那么你可以使用下面的代码运行它
//generates 100 by 100 data points within the specified range
double iStart=0;
double iEnd=500;
double jStart=0;
double jEnd=500;
double[][] result=new double[100][100];
for(int i=0;i<100;i++){
for(int j=0;j<100;j++){
int x=(int)(iStart+i*((iEnd-iStart)/100));
int y=(int)(jStart+j*((jEnd-jStart)/100));
result[i][j]=0.5*(1+perlinNoise.getNoise(x,y));
}
}
ImageWriter.greyWriteImage(result);
我的意图是整数x和y。如果您不是这样,请随时修改
映射到磁贴
这完全取决于您,您需要定义perlin噪声值的某些范围以创建某些切片。但要注意的是,perlin噪声偏向于0.假设2D你可以通过半字面上的景观类比获得不错的结果,低值=水,低值=沙,中值=草,高值=雪。
还要注意,在一些实施方式中(例如,我的世界生物群系和洞穴),将几个随机值组合以产生总体结果。见https://softwareengineering.stackexchange.com/questions/202992/randomization-of-biomes/203040#203040
改进的想法
如果你发现perlin噪声的产生太慢,那么考虑单纯形噪声,它具有非常相似的特性,但效率更高(特别是在更高的尺寸时)。然而,单面噪声在数学上要复杂得多。
答案 1 :(得分:3)
我意识到这是一个有点老问题,但我想发布我的解决方案,因为我发现很难找到有用的例子。
我也在研究这个问题,起初我发现你的代码在外观上接合工作时有用,但是当我想改变图像的大小时,平滑的噪音不能适当地缩放,我找不到一个修复代码的方法。
经过更多的研究后,我发现你的SmoothNoise实现非常狡猾,所以我从一个可靠的来源(http://lodev.org/cgtutor/randomnoise.html)重新实现了它。
这是我的噪音等级,它可以生成并处理任何类型的噪音:
package com.heresysoft.arsenal.utils;
public class Noise
{
public static double[] blend(double[] noise1, double[] noise2, double persistence)
{
if (noise1 != null && noise2 != null && noise1.length > 0 && noise1.length == noise2.length)
{
double[] result = new double[noise1.length];
for (int i = 0; i < noise1.length; i++)
result[i] = noise1[i] + (noise2[i] * persistence);
return result;
}
return null;
}
public static double[] normalize(double[] noise)
{
if (noise != null && noise.length > 0)
{
double[] result = new double[noise.length];
double minValue = noise[0];
double maxValue = noise[0];
for (int i = 0; i < noise.length; i++)
{
if (noise[i] < minValue)
minValue = noise[i];
else if (noise[i] > maxValue)
maxValue = noise[i];
}
for (int i = 0; i < noise.length; i++)
result[i] = (noise[i] - minValue) / (maxValue - minValue);
return result;
}
return null;
}
public static double[] perlinNoise(int width, int height, double exponent)
{
int[] p = new int[width * height];
double[] result = new double[width * height];
/*final int[] permutation = {151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23,
190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174,
20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230,
220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169,
200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147,
118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44,
154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104,
218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192,
214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24,
72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180};*/
for (int i = 0; i < p.length / 2; i++)
p[i] = p[i + p.length / 2] = (int) (Math.random() * p.length / 2);//permutation[i];
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
double x = i * exponent / width; // FIND RELATIVE X,Y,Z
double y = j * exponent / height; // OF POINT IN CUBE.
int X = (int) Math.floor(x) & 255; // FIND UNIT CUBE THAT
int Y = (int) Math.floor(y) & 255; // CONTAINS POINT.
int Z = 0;
x -= Math.floor(x); // FIND RELATIVE X,Y,Z
y -= Math.floor(y); // OF POINT IN CUBE.
double u = fade(x); // COMPUTE FADE CURVES
double v = fade(y); // FOR EACH OF X,Y,Z.
double w = fade(Z);
int A = p[X] + Y, AA = p[A] + Z, AB = p[A + 1] + Z, // HASH COORDINATES OF
B = p[X + 1] + Y, BA = p[B] + Z, BB = p[B + 1] + Z; // THE 8 CUBE CORNERS,
result[j + i * width] = lerp(w, lerp(v, lerp(u, grad(p[AA], x, y, Z), // AND ADD
grad(p[BA], x - 1, y, Z)), // BLENDED
lerp(u, grad(p[AB], x, y - 1, Z), // RESULTS
grad(p[BB], x - 1, y - 1, Z))),// FROM 8
lerp(v, lerp(u, grad(p[AA + 1], x, y, Z - 1), // CORNERS
grad(p[BA + 1], x - 1, y, Z - 1)), // OF CUBE
lerp(u, grad(p[AB + 1], x, y - 1, Z - 1), grad(p[BB + 1], x - 1, y - 1, Z - 1))));
}
}
return result;
}
public static double[] smoothNoise(int width, int height, double zoom)
{
if (zoom > 0)
{
double[] noise = whiteNoise(width, height);
double[] result = new double[width * height];
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
double x = i / zoom;
double y = j / zoom;
// get fractional part of x and y
double fractX = x - (int) x;
double fractY = y - (int) y;
// wrap around
int x1 = ((int) x + width) % width;
int y1 = ((int) y + height) % height;
// neighbor values
int x2 = (x1 + width - 1) % width;
int y2 = (y1 + height - 1) % height;
// smooth the noise with bilinear interpolation
result[j + i * width] = fractX * fractY * noise[y1 + x1 * width]
+ fractX * (1 - fractY) * noise[y2 + x1 * width]
+ (1 - fractX) * fractY * noise[y1 + x2 * width]
+ (1 - fractX) * (1 - fractY) * noise[y2 + x2 * width];
}
}
return result;
}
return null;
}
public static double[] turbulence(int width, int height, double zoom)
{
// http://lodev.org/cgtutor/randomnoise.html
double[] result = new double[width * height];
double initialZoom = zoom;
while (zoom >= 1)
{
result = blend(result, smoothNoise(width, height, zoom), zoom);
zoom /= 2.0;
}
for (int i = 0; i < result.length; i++)
result[i] = (128.0 * result[i] / initialZoom);
return result;
}
public static double[] whiteNoise(int width, int height)
{
double[] result = new double[width * height];
for (int i = 0; i < width * height; i++)
result[i] = Math.random();
return result;
}
private static double fade(double t)
{
return t * t * t * (t * (t * 6 - 15) + 10);
}
private static double lerp(double t, double a, double b)
{
return a + t * (b - a);
}
private static double grad(int hash, double x, double y, double z)
{
int h = hash & 15; // CONVERT LO 4 BITS OF HASH CODE
double u = h < 8 ? x : y, // INTO 12 GRADIENT DIRECTIONS.
v = h < 4 ? y : h == 12 || h == 14 ? x : z;
return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
}
}
以下是如何使用smoothNoise函数的示例:
double[] data = Noise.normalize(Noise.smoothNoise(width, height, 32));
for (int i = 0; i < data.length; i++)
data[i] = 255*data[i];
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
img.getRaster().setPixels(0, 0, width, height, data);
以下是如何使用湍流函数的示例:
double[] data = Noise.normalize(Noise.turbulence(width, height, 32));
for (int i = 0; i < data.length; i++)
data[i] = 255*data[i];
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
img.getRaster().setPixels(0, 0, width, height, data);
以下是如何使用perlinNoise函数的示例:
double[] data = Noise.normalize(Noise.perlinNoise(width, height, 7));
for (int i = 0; i < data.length; i++)
data[i] = 255 * data[i];
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
img.getRaster().setPixels(0, 0, width, height, data);