Skip to content

Commit 4ab9061

Browse files
authored
Add octaved Perlin noise generator (#25)
* 添加 Noise 基架 * 修复包引用 * 修改测试 * 更新 Nuget.Config * 完成 Octaved Noise * Fix typo
1 parent 782dc4b commit 4ab9061

File tree

8 files changed

+267
-0
lines changed

8 files changed

+267
-0
lines changed

src/NuGet.config renamed to NuGet.config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
<clear />
77
<add key="orleans-prerlease" value="https://dotnet.myget.org/F/orleans-prerelease/api/v3/index.json" />
88
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
9+
<add key="imageSharp" value="https://www.myget.org/F/imagesharp/api/v3/index.json"/>
910
</packageSources>
1011
<config>
1112
<add key="repositoryPath" value="..\packages" />
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netstandard1.0</TargetFramework>
5+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
6+
<CodeAnalysisRuleSet>../../build/Analyzers.ruleset</CodeAnalysisRuleSet>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta004" PrivateAssets="All" />
11+
<PackageReference Include="System.ValueTuple" Version="4.4.0" />
12+
</ItemGroup>
13+
14+
<ItemGroup>
15+
<AdditionalFiles Include="..\..\build\stylecop.json" />
16+
</ItemGroup>
17+
18+
</Project>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace MineCase.Algorithm.Noise
6+
{
7+
public interface INoise
8+
{
9+
double Noise(double x, double y, double z);
10+
}
11+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace MineCase.Algorithm.Noise
6+
{
7+
public class OctavedNoise<TNoise> : INoise
8+
where TNoise : INoise
9+
{
10+
private readonly TNoise _innerNoise;
11+
private readonly int _octaves;
12+
private readonly double _persistence;
13+
14+
public OctavedNoise(TNoise innerNoise, int octaves, double persistence)
15+
{
16+
_innerNoise = innerNoise;
17+
_octaves = octaves;
18+
_persistence = persistence;
19+
}
20+
21+
public double Noise(double x, double y, double z)
22+
{
23+
double total = 0;
24+
double frequency = 1;
25+
double amplitude = 1;
26+
double maxValue = 0;
27+
28+
for (int i = 0; i < _octaves; i++)
29+
{
30+
total += _innerNoise.Noise(x * frequency, y * frequency, z * frequency) * amplitude;
31+
maxValue += amplitude;
32+
amplitude *= _persistence;
33+
frequency *= 2;
34+
}
35+
36+
return total / maxValue;
37+
}
38+
}
39+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace MineCase.Algorithm.Noise
6+
{
7+
/// <summary>
8+
/// Implementation for Improved Perlin Noise (http://mrl.nyu.edu/~perlin/noise/)
9+
/// </summary>
10+
public class PerlinNoise : INoise
11+
{
12+
/// <summary>
13+
/// Permutation
14+
/// </summary>
15+
private readonly int[] _p = new int[512];
16+
17+
/// <summary>
18+
/// Initializes a new instance of the <see cref="PerlinNoise"/> class.
19+
/// </summary>
20+
/// <param name="seed">Seed for generating permutaion.</param>
21+
public PerlinNoise(int seed)
22+
{
23+
var random = new Random(seed);
24+
for (int i = 0; i < 256; i++)
25+
_p[i + 256] = _p[i] = random.Next(0, 256);
26+
}
27+
28+
public double Noise(double x, double y, double z)
29+
{
30+
var xcoord = Split(x);
31+
var ycoord = Split(y);
32+
var zcoord = Split(z);
33+
34+
var u = Fade(xcoord.remainder);
35+
var v = Fade(ycoord.remainder);
36+
var w = Fade(zcoord.remainder);
37+
38+
int aaa, aba, aab, abb, baa, bba, bab, bbb;
39+
aaa = _p[_p[_p[xcoord.integer] + ycoord.integer] + zcoord.integer];
40+
aba = _p[_p[_p[xcoord.integer] + ycoord.integer + 1] + zcoord.integer];
41+
aab = _p[_p[_p[xcoord.integer] + ycoord.integer] + zcoord.integer + 1];
42+
abb = _p[_p[_p[xcoord.integer] + ycoord.integer + 1] + zcoord.integer + 1];
43+
baa = _p[_p[_p[xcoord.integer + 1] + ycoord.integer] + zcoord.integer];
44+
bba = _p[_p[_p[xcoord.integer + 1] + ycoord.integer + 1] + zcoord.integer];
45+
bab = _p[_p[_p[xcoord.integer + 1] + ycoord.integer] + zcoord.integer + 1];
46+
bbb = _p[_p[_p[xcoord.integer + 1] + ycoord.integer + 1] + zcoord.integer + 1];
47+
48+
double x1, x2, y1, y2;
49+
x1 = Lerp(
50+
Grad(aaa, xcoord.remainder, ycoord.remainder, zcoord.remainder),
51+
Grad(baa, xcoord.remainder - 1, ycoord.remainder, zcoord.remainder),
52+
u);
53+
x2 = Lerp(
54+
Grad(aba, xcoord.remainder, ycoord.remainder - 1, zcoord.remainder),
55+
Grad(bba, xcoord.remainder - 1, ycoord.remainder - 1, zcoord.remainder),
56+
u);
57+
y1 = Lerp(x1, x2, v);
58+
59+
x1 = Lerp(
60+
Grad(aab, xcoord.remainder, ycoord.remainder, zcoord.remainder - 1),
61+
Grad(bab, xcoord.remainder - 1, ycoord.remainder, zcoord.remainder - 1),
62+
u);
63+
x2 = Lerp(
64+
Grad(abb, xcoord.remainder, ycoord.remainder - 1, zcoord.remainder - 1),
65+
Grad(bbb, xcoord.remainder - 1, ycoord.remainder - 1, zcoord.remainder - 1),
66+
u);
67+
y2 = Lerp(x1, x2, v);
68+
69+
return (Lerp(y1, y2, w) + 1) / 2;
70+
}
71+
72+
private static (int integer, double remainder) Split(double value)
73+
{
74+
var integer = (int)value;
75+
return (integer % 256, value - integer);
76+
}
77+
78+
private static double Fade(double t)
79+
{
80+
// 6t^5 - 15t^4 + 10t^3
81+
return t * t * t * (t * (t * 6 - 15) + 10);
82+
}
83+
84+
// Source: http://riven8192.blogspot.com/2010/08/calculate-perlinnoise-twice-as-fast.html
85+
public static double Grad(int hash, double x, double y, double z)
86+
{
87+
switch (hash & 0xF)
88+
{
89+
case 0x0: return x + y;
90+
case 0x1: return -x + y;
91+
case 0x2: return x - y;
92+
case 0x3: return -x - y;
93+
case 0x4: return x + z;
94+
case 0x5: return -x + z;
95+
case 0x6: return x - z;
96+
case 0x7: return -x - z;
97+
case 0x8: return y + z;
98+
case 0x9: return -y + z;
99+
case 0xA: return y - z;
100+
case 0xB: return -y - z;
101+
case 0xC: return y + x;
102+
case 0xD: return -y + z;
103+
case 0xE: return y - x;
104+
case 0xF: return -y - z;
105+
default: throw new ArgumentException();
106+
}
107+
}
108+
109+
public static double Lerp(double a, double b, double x) =>
110+
a + x * (b - a);
111+
}
112+
}

src/MineCase.sln

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,14 @@ EndProject
2222
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A0B013BF-4BEB-4044-AA59-A9ED3AFBF99D}"
2323
ProjectSection(SolutionItems) = preProject
2424
..\build\Analyzers.ruleset = ..\build\Analyzers.ruleset
25+
..\NuGet.config = ..\NuGet.config
2526
..\build\stylecop.json = ..\build\stylecop.json
2627
EndProjectSection
2728
EndProject
29+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MineCase.Algorithm", "MineCase.Algorithm\MineCase.Algorithm.csproj", "{47C1F452-E59C-42E3-8799-BF09444D8384}"
30+
EndProject
31+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{D53C0015-5761-4018-A50A-63C919C00D35}"
32+
EndProject
2833
Global
2934
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3035
Debug|Any CPU = Debug|Any CPU
@@ -59,10 +64,17 @@ Global
5964
{199335D6-9726-42E1-94F2-7D95932D4DDC}.Debug|Any CPU.Build.0 = Debug|Any CPU
6065
{199335D6-9726-42E1-94F2-7D95932D4DDC}.Release|Any CPU.ActiveCfg = Release|Any CPU
6166
{199335D6-9726-42E1-94F2-7D95932D4DDC}.Release|Any CPU.Build.0 = Release|Any CPU
67+
{47C1F452-E59C-42E3-8799-BF09444D8384}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
68+
{47C1F452-E59C-42E3-8799-BF09444D8384}.Debug|Any CPU.Build.0 = Debug|Any CPU
69+
{47C1F452-E59C-42E3-8799-BF09444D8384}.Release|Any CPU.ActiveCfg = Release|Any CPU
70+
{47C1F452-E59C-42E3-8799-BF09444D8384}.Release|Any CPU.Build.0 = Release|Any CPU
6271
EndGlobalSection
6372
GlobalSection(SolutionProperties) = preSolution
6473
HideSolutionNode = FALSE
6574
EndGlobalSection
75+
GlobalSection(NestedProjects) = preSolution
76+
{35618D69-85F0-4C38-8224-04A2D75825B6} = {D53C0015-5761-4018-A50A-63C919C00D35}
77+
EndGlobalSection
6678
GlobalSection(ExtensibilityGlobals) = postSolution
6779
SolutionGuid = {7AB995C3-E961-463C-8A55-3149C51761B5}
6880
EndGlobalSection

tests/UnitTest/MineCase.UnitTest.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,21 @@
66
</PropertyGroup>
77

88
<ItemGroup>
9+
<PackageReference Include="ImageSharp" Version="1.0.0-alpha9-00187" />
910
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0-preview-20170810-02" />
1011
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
1112
<PackageReference Include="xunit" Version="2.3.0-beta4-build3742" />
1213
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.0-beta4-build3742" />
1314
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta004" PrivateAssets="All" />
15+
<PackageReference Include="System.Numerics.Vectors" Version="4.4.0" />
1416
</ItemGroup>
1517

1618
<ItemGroup>
1719
<AdditionalFiles Include="..\..\build\stylecop.json" />
1820
</ItemGroup>
1921

2022
<ItemGroup>
23+
<ProjectReference Include="..\..\src\MineCase.Algorithm\MineCase.Algorithm.csproj" />
2124
<ProjectReference Include="..\..\src\MineCase.NBT\MineCase.Nbt.csproj" />
2225
<ProjectReference Include="..\..\src\MineCase.Protocol\MineCase.Protocol.csproj" />
2326
</ItemGroup>

tests/UnitTest/NoiseTest.cs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Reflection;
5+
using System.Runtime.CompilerServices;
6+
using System.Text;
7+
using ImageSharp;
8+
using MineCase.Algorithm.Noise;
9+
using Xunit;
10+
11+
namespace MineCase.UnitTest
12+
{
13+
public class NoiseTest
14+
{
15+
public readonly string RootDir;
16+
17+
public NoiseTest()
18+
{
19+
RootDir = SetRootDir();
20+
}
21+
22+
private static string SetRootDir([CallerFilePath]string fileName = null) =>
23+
Path.Combine(Path.GetDirectoryName(fileName), "bin");
24+
25+
[Fact]
26+
public void TestPerlinNoise3D()
27+
{
28+
const int xExtent = 100;
29+
const int yExtent = 100;
30+
31+
using (var file = File.OpenWrite(Path.Combine(RootDir, "PerlinNoise3D.bmp")))
32+
using (var image = new Image<ImageSharp.PixelFormats.Rgb24>(xExtent, yExtent))
33+
{
34+
var noise = new PerlinNoise(100);
35+
for (int x = 0; x < xExtent; x++)
36+
{
37+
for (int y = 0; y < yExtent; y++)
38+
{
39+
var color = (byte)(noise.Noise(x / 10.0, 0, y / 10.0) * 255);
40+
image[x, y] = new ImageSharp.PixelFormats.Rgb24(color, color, color);
41+
}
42+
}
43+
44+
image.SaveAsBmp(file);
45+
}
46+
}
47+
48+
[Fact]
49+
public void TestOctavedPerlinNoise3D()
50+
{
51+
const int xExtent = 100;
52+
const int yExtent = 100;
53+
54+
using (var file = File.OpenWrite(Path.Combine(RootDir, "OctavedPerlinNoise3D.bmp")))
55+
using (var image = new Image<ImageSharp.PixelFormats.Rgb24>(xExtent, yExtent))
56+
{
57+
var noise = new OctavedNoise<PerlinNoise>(new PerlinNoise(100), 8, 1);
58+
for (int x = 0; x < xExtent; x++)
59+
{
60+
for (int y = 0; y < yExtent; y++)
61+
{
62+
var color = (byte)(noise.Noise(x / 10.0, 0, y / 10.0) * 255);
63+
image[x, y] = new ImageSharp.PixelFormats.Rgb24(color, color, color);
64+
}
65+
}
66+
67+
image.SaveAsBmp(file);
68+
}
69+
}
70+
}
71+
}

0 commit comments

Comments
 (0)