C #, WPF
Tôi đã thử nghiệm một cuộc đi bộ ngẫu nhiên , hoạt động tốt hơn tôi dự đoán. Tôi bắt đầu ở đâu đó trên bản đồ, đi bộ đến một ô liền kề ngẫu nhiên và tăng giá trị chiều cao của nó , sau đó chuyển sang tiếp theo và cứ thế. Điều này được lặp đi lặp lại hàng ngàn lần và cuối cùng dẫn đến một bản đồ chiều cao như thế này (100 x 100):
Sau đó, tôi "rời rạc" bản đồ, giảm số lượng giá trị xuống các mức độ cao nhất định và gán địa hình / màu dựa trên chiều cao đó:
Các địa hình tương tự như quần đảo tương tự:
Tăng số bước ngẫu nhiên và mức độ cao để có thêm địa hình đồi núi:
Mã
Các tính năng: Tái tạo địa hình bằng một nút. Hiển thị địa hình 3D và bản đồ 2D. Thu phóng (bánh xe chuột) và cuộn 3D (phím mũi tên). Nhưng nó không hiệu quả lắm - xét cho cùng, điều này được viết hoàn toàn bằng WPF, không phải DirectX hay OpenGL.
MainWindow.xaml:
<Window x:Class="VoxelTerrainGenerator.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Voxel Terrain Generator" Width="550" Height="280" KeyUp="Window_KeyUp">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Viewport3D x:Name="ViewPort" MouseWheel="ViewPort_MouseWheel">
<Viewport3D.Camera>
<OrthographicCamera x:Name="Camera" Position="-100,-100,150" LookDirection="1,1,-1" UpDirection="0,0,1" Width="150" />
<!--<PerspectiveCamera x:Name="Camera" Position="-100,-100,150" LookDirection="1,1,-1" UpDirection="0,0,1" />-->
</Viewport3D.Camera>
</Viewport3D>
<Grid Grid.Column="1" Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Image Grid.Row="0" x:Name="TopViewImage"/>
<Button Grid.Row="1" Margin="0 10 0 0" Click="Button_Click" Content="Generate Terrain" />
</Grid>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Media.Media3D;
namespace VoxelTerrainGenerator
{
public partial class MainWindow : Window
{
const int RandomSteps = 20000;
const int MapLengthX = 100;
const int MapLengthY = 100;
const int MaxX = MapLengthX - 1;
const int MaxY = MapLengthY - 1;
const bool ForceIntoBounds = true;
readonly Random Random = new Random();
readonly List<Color> ColorsByHeight = new List<Color>
{
Color.FromArgb(0, 0, 50),
Color.FromArgb(170, 170, 20),
Color.FromArgb(0, 150, 0),
Color.FromArgb(0, 140, 0),
Color.FromArgb(0, 130, 0),
Color.FromArgb(0, 120, 0),
Color.FromArgb(0, 110, 0),
Color.FromArgb(100, 100, 100),
};
public MainWindow()
{
InitializeComponent();
TopViewImage.Width = MapLengthX;
TopViewImage.Height = MapLengthY;
}
public int[,] CreateRandomHeightMap()
{
var map = new int[MapLengthX, MapLengthY];
int x = MapLengthX/2;
int y = MapLengthY/2;
for (int i = 0; i < RandomSteps; i++)
{
x += Random.Next(-1, 2);
y += Random.Next(-1, 2);
if (ForceIntoBounds)
{
if (x < 0) x = 0;
if (x > MaxX) x = MaxX;
if (y < 0) y = 0;
if (y > MaxY) y = MaxY;
}
if (x >= 0 && x < MapLengthX && y >= 0 && y < MapLengthY)
{
map[x, y]++;
}
}
return map;
}
public int[,] Normalized(int[,] map, int newMax)
{
int max = map.Cast<int>().Max();
float f = (float)newMax / (float)max;
int[,] newMap = new int[MapLengthX, MapLengthY];
for (int x = 0; x < MapLengthX; x++)
{
for (int y = 0; y < MapLengthY; y++)
{
newMap[x, y] = (int)(map[x, y] * f);
}
}
return newMap;
}
public Bitmap ToBitmap(int[,] map)
{
var bitmap = new Bitmap(MapLengthX, MapLengthY);
for (int x = 0; x < MapLengthX; x++)
{
for (int y = 0; y < MapLengthY; y++)
{
int height = map[x, y];
if (height > 255)
{
height = 255;
}
var color = Color.FromArgb(255, height, height, height);
bitmap.SetPixel(x, y, color);
}
}
return bitmap;
}
public Bitmap ToColorcodedBitmap(int[,] map)
{
int maxHeight = ColorsByHeight.Count-1;
var bitmap = new Bitmap(MapLengthX, MapLengthY);
for (int x = 0; x < MapLengthX; x++)
{
for (int y = 0; y < MapLengthY; y++)
{
int height = map[x, y];
if (height > maxHeight)
{
height = maxHeight;
}
bitmap.SetPixel(x, y, ColorsByHeight[height]);
}
}
return bitmap;
}
private void ShowTopView(int[,] map)
{
using (var memory = new System.IO.MemoryStream())
{
ToColorcodedBitmap(map).Save(memory, ImageFormat.Png);
memory.Position = 0;
var bitmapImage = new System.Windows.Media.Imaging.BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memory;
bitmapImage.CacheOption = System.Windows.Media.Imaging.BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
TopViewImage.Source = bitmapImage;
}
}
private void Show3DView(int[,] map)
{
ViewPort.Children.Clear();
var light1 = new AmbientLight(System.Windows.Media.Color.FromArgb(255, 75, 75, 75));
var lightElement1 = new ModelUIElement3D();
lightElement1.Model = light1;
ViewPort.Children.Add(lightElement1);
var light2 = new DirectionalLight(
System.Windows.Media.Color.FromArgb(255, 200, 200, 200),
new Vector3D(0, 1, -0.1));
var lightElement2 = new ModelUIElement3D();
lightElement2.Model = light2;
ViewPort.Children.Add(lightElement2);
for (int x = 0; x < MapLengthX; x++)
{
for (int y = 0; y < MapLengthY; y++)
{
int height = map[x, MapLengthY-y-1];
for (int h = 0; h <= height; h++)
{
Color color = ColorsByHeight[h];
if (height > 0 && h == 0)
{
// No water under sand
color = ColorsByHeight[1];
}
ViewPort.Children.Add(CreateCube(x, y, h, 1,
System.Windows.Media.Color.FromArgb(255, color.R, color.G, color.B)));
}
}
}
}
private ModelVisual3D CreateCube(int x, int y, int z, int length,
System.Windows.Media.Color color)
{
List<Point3D> positions = new List<Point3D>()
{
new Point3D(x, y, z),
new Point3D(x + length, y, z),
new Point3D(x + length, y + length, z),
new Point3D(x, y + length, z),
new Point3D(x, y, z + length),
new Point3D(x + length, y, z + length),
new Point3D(x + length, y + length, z + length),
new Point3D(x, y + length, z + length),
};
List<List<int>> quads = new List<List<int>>
{
new List<int> {3,2,1,0},
new List<int> {0,1,5,4},
new List<int> {2,6,5,1},
new List<int> {3,7,6,2},
new List<int> {3,0,4,7},
new List<int> {4,5,6,7},
};
double halfLength = (double)length / 2.0;
Point3D cubeCenter = new Point3D(x + halfLength, y + halfLength, z + halfLength);
var mesh = new MeshGeometry3D();
foreach (List<int> quad in quads)
{
int indexOffset = mesh.Positions.Count;
mesh.Positions.Add(positions[quad[0]]);
mesh.Positions.Add(positions[quad[1]]);
mesh.Positions.Add(positions[quad[2]]);
mesh.Positions.Add(positions[quad[3]]);
mesh.TriangleIndices.Add(indexOffset);
mesh.TriangleIndices.Add(indexOffset+1);
mesh.TriangleIndices.Add(indexOffset+2);
mesh.TriangleIndices.Add(indexOffset+2);
mesh.TriangleIndices.Add(indexOffset+3);
mesh.TriangleIndices.Add(indexOffset);
double centroidX = quad.Select(v => mesh.Positions[v].X).Sum() / 4.0;
double centroidY = quad.Select(v => mesh.Positions[v].Y).Sum() / 4.0;
double centroidZ = quad.Select(v => mesh.Positions[v].Z).Sum() / 4.0;
Vector3D normal = new Vector3D(
centroidX - cubeCenter.X,
centroidY - cubeCenter.Y,
centroidZ - cubeCenter.Z);
for (int i = 0; i < 4; i++)
{
mesh.Normals.Add(normal);
}
}
Material material = new DiffuseMaterial(new System.Windows.Media.SolidColorBrush(color));
GeometryModel3D model = new GeometryModel3D(mesh, material);
ModelVisual3D visual = new ModelVisual3D();
visual.Content = model;
return visual;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
int[,] map = CreateRandomHeightMap();
int[,] normalizedMap = (Normalized(map, ColorsByHeight.Count-1));
ShowTopView(normalizedMap);
Show3DView(normalizedMap);
ToBitmap(Normalized(map, 255)).Save("heightmap-original.png");
ToBitmap(Normalized(normalizedMap, 255)).Save("heightmap.png");
ToColorcodedBitmap(normalizedMap).Save("terrainmap.png");
}
private void ViewPort_MouseWheel(object sender, MouseWheelEventArgs e)
{
// Zoom in or out
Camera.Width -= (double)e.Delta / 100;
}
private void Window_KeyUp(object sender, KeyEventArgs e)
{
// Scrolling by moving the 3D camera
double x = 0;
double y = 0;
if (e.Key == Key.Left)
{
x = +10;
y = -10;
}
else if (e.Key == Key.Up)
{
x = -10;
y = -10;
}
else if (e.Key == Key.Right)
{
x = -10;
y = +10;
}
else if (e.Key == Key.Down)
{
x = +10;
y = +10;
}
Point3D cameraPosition = new Point3D(
Camera.Position.X + x,
Camera.Position.Y + y,
Camera.Position.Z);
Camera.Position = cameraPosition;
}
}
}