Создание Ландшафта. Фокусирование на создании Ландщафта (Перевод) Часть 5

Большая часть исправленного кода ландшафта состоит из старого кода ландшафта.

Вот новый код ландшафта:

/// <summary>

/// This is the terrain class.

/// </summary>

public class Terrain

{

/// <summary>

/// Heightmap used to generate the terrain mesh.

/// </summary>

Heightmap _heightmap;

/// <summary>

/// Texture used to represent the lowest heights of the terrain.

/// </summary>

Texture2D _groundTexture;

/// <summary>

/// Texture used to represent the heights above the ground.

/// </summary>

Texture2D _mudTexture;

/// <summary>

/// Texture used to represent the heights above the mud.

/// </summary>

Texture2D _rockTexture;

/// <summary>

/// Texture used to represent the highest heights of the terrain.

/// </summary>

Texture2D _snowTexture;

/// <summary>

/// Vertex declaration of the terrain mesh.

/// </summary>

VertexDeclaration _vertexDeclaration;

/// <summary>

/// Terrain effect shader.

/// </summary>

Effect _effect;

/// <summary>

/// World matrix.

/// </summary>

Matrix _worldMatrix;

/// <summary>

/// Wireframe fill mode flag.

/// </summary>

bool _isWireframe;

/// <summary>

/// List of the terrain patches.

/// </summary>

List<TerrainPatch> _patches;

/// <summary>

/// Patch count.

/// </summary>

int _patchCount;

/// <summary>

/// Drawn patch count.

/// </summary>

int _drawnPatchCount;

/// <summary>

/// Get or set the wireframe fill mode.

/// </summary>

public bool IsWireframe

{

get

{

return _isWireframe;

}

set

{

_isWireframe = value;

}

}

/// <summary>

/// The heightmap used by the terrain.

/// </summary>

public Heightmap Heightmap

{

get

{

return _heightmap;

}

set

{

_heightmap = value;

}

}

/// <summary>

/// Patch count.

/// </summary>

public int PatchCount

{

get

{

return _patchCount;

}

}

/// <summary>

/// Drawn patch count.

/// </summary>

public int DrawnPatchCount

{

get

{

return _drawnPatchCount;

}

}

/// <summary>

/// Default constructor.

/// </summary>

/// <param name=»heightmap»></param>

public Terrain(Heightmap heightmap)

{

_patches = new List<TerrainPatch>();

_heightmap = heightmap;

}

/// <summary>

/// Load the graphics content and also build the mesh.

/// </summary>

public void LoadGraphicsContent()

{

_effect = GameFOT.Instance.ContentManager.Load<Effect>(

«Content/Effects/TerrainEffect»);

_effect.CurrentTechnique = _effect.Techniques[«DefaultTechnique»];

_groundTexture = GameFOT.Instance.ContentManager.Load<Texture2D>(

«Content/Textures/TerrainGround»);

_mudTexture = GameFOT.Instance.ContentManager.Load<Texture2D>(

«Content/Textures/TerrainMud»);

_rockTexture = GameFOT.Instance.ContentManager.Load<Texture2D>(

«Content/Textures/TerrainRock»);

_snowTexture = GameFOT.Instance.ContentManager.Load<Texture2D>(

«Content/Textures/TerrainSnow»);

BuildTerrain();

}

/// <summary>

/// Draw the terrain.

/// </summary>

/// <param name=»gameTime»></param>

/// <param name=»viewMatrix»></param>

/// <param name=»projectionMatrix»></param>

/// <param name=»frustum»></param>

public void Draw(GameTime gameTime,

Matrix viewMatrix,

Matrix projectionMatrix,

BoundingFrustum frustum)

{

int width = _heightmap.Width;

int depth = _heightmap.Depth;

_patchCount = _patches.Count;

_drawnPatchCount = 0;

if (_isWireframe)

{

GameFOT.Instance.GraphicsDevice.RenderState.FillMode = FillMode.WireFrame;

}

else

{

GameFOT.Instance.GraphicsDevice.RenderState.FillMode = FillMode.Solid;

}

GameFOT.Instance.GraphicsDevice.RenderState.DepthBufferEnable = true;

GameFOT.Instance.GraphicsDevice.RenderState.DepthBufferWriteEnable = true;

Matrix worldViewProjection = _worldMatrix * viewMatrix * projectionMatrix;

Vector3 lightDirection = new Vector3(

-20.0f * (float)Math.Sin(gameTime.TotalRealTime.TotalMilliseconds * 0.0001f),

0.0f,

-20.0f * (float)Math.Cos(gameTime.TotalRealTime.TotalMilliseconds * 0.0001f));

GameFOT.Instance.GraphicsDevice.VertexDeclaration = _vertexDeclaration;

_effect.Parameters[«g_matWorldViewProjection»].SetValue(worldViewProjection);

_effect.Parameters[«g_vecLightDirection»].SetValue(lightDirection);

_effect.Parameters[«g_vecHeights»].SetValue(new Vector3(14.0f, 21.0f, 28.0f));

_effect.Parameters[«g_texGround»].SetValue(_groundTexture);

_effect.Parameters[«g_texMud»].SetValue(_mudTexture);

_effect.Parameters[«g_texRock»].SetValue(_rockTexture);

_effect.Parameters[«g_texSnow»].SetValue(_snowTexture);

_effect.Begin();

foreach (EffectPass pass in _effect.CurrentTechnique.Passes)

{

pass.Begin();

for (int i = 0; i < _patches.Count; ++i)

{

// Test the patch against frustum.

if (frustum.Contains(_patches[i].BoundingBox) != ContainmentType.Disjoint)

{

_patches[i].Draw();

++_drawnPatchCount;

}

}

pass.End();

}

_effect.End();

if (_isWireframe)

{

GameFOT.Instance.GraphicsDevice.RenderState.FillMode = FillMode.Solid;

}

}

/// <summary>

/// Build the terrain.

/// </summary>

public void BuildTerrain()

{

int width = _heightmap.Width;

int depth = _heightmap.Depth;

// Clear the terrain patches.

_patches.Clear();

// Compute the world matrix to place the terrain in the middle of the scene.

_worldMatrix = Matrix.CreateTranslation((float)width * -0.5f,

0.0f,

(float)depth * -0.5f);

// Create the terrain patches.

int patchWidth = 16;

int patchDepth = 16;

int patchCountX = width / patchWidth;

int patchCountZ = depth / patchDepth;

int patchCount = patchCountX * patchCountZ;

for (int x = 0; x < patchCountX; ++x)

{

for (int z = 0; z < patchCountZ; ++z)

{

TerrainPatch patch = new TerrainPatch();

patch.BuildPatch(_heightmap,

_worldMatrix,

patchWidth,

patchDepth,

(x + 1) * (patchWidth — 1),

(z + 1) * (patchDepth — 1));

_patches.Add(patch);

}

}

_vertexDeclaration = new VertexDeclaration(GameFOT.Instance.GraphicsDevice,

VertexPositionNormalTexture.VertexElements);

}

/// <summary>

/// Save the terrain to a file.

/// </summary>

/// <param name=»filename»></param>

public void SaveToFile(String filename)

{

_heightmap.SaveToFile(filename);

}

/// <summary>

/// Load the terrain from a file and build the terrain.

/// </summary>

/// <param name=»filename»></param>

public void LoadFromFile(String filename)

{

_heightmap.LoadFromFile(filename);

BuildTerrain();

}

}

В классе GameFOT мы просто добавляем sprite batch и sprite font, чтобы показать счетчик участков и счетчик нарисованных участков.

Вот и все!

Вы можете найти исходный текст в папке P203TerrainPatches.

Дефект деформаци линии

В этой части мы вводим понятие рекурсивного создания ландшафта. В этой специфической части мы сосредоточимся на дефекте деформации линии. В природе существуют такие силы, как тектонические плиты, оползни и эрозия береговых линий, они создают особенности ландшафта — откосы, мезаструктуры и прибрежные утесы. Линии с дефектом позволяют Вам создавать эти виды ландшафта.

Идея этого алгоритма состоит в том, чтобы начатьс с сетки heightfield. Мы вычисляем через нее случайную линию и добавляем значение смещения к каждому значению на одной стороне. После этого мы уменьшаем значение смещения, и повторяем процесс, пока не будем удовлетворены уровнем сгенерированной детали.

Формула уменьшает значение смещения при каждой итерации:

смещение = maxOffset — ((maxOffset — minOffset) * i) / итерации

Когда мы выбираем случайные линии, чтобы пересечься с heightfield, мы выбираем два случайных пункта в пределах heightfield, и используем их, чтобы определить линию.

Кроме того, мы добавляем понятие эрозии при использовании низкого фильтра прохода на heightmap. Этот фильтр уменьшает искажение (в данном случае это дисперсия высот от одного пункта до соседних). Поскольку линии с дефектом создают огромные различия между соседними высотами, мы обеспечиваем параметр таким, чтобы установить частоту фильтра такую же как коэффициент эрозии.

Вот некоторые скриншоты, чтобы иллюстрировать линии с дефектом:


Этот heightmap сгенерирован, используя случайные значения.


Этот heightmap сгенерирован, используя несколько линий с дефектом без фильтрации.


Этот heightmap сгенерирован с большим количеством линий с дефектом без фильтрации.


Этот heightmap сгенерирован, используя много линий с дефектом без фильтрации.


Этот heightmap сгенерирован, используя много линий с дефектом со средней фильтрацией.


Тот же самый heightmap как выше в каркасе, обратите внимание на эффект низкого фильтра прохода.


Этот heightmap сгенерирован с низкой фильтрацией и немногими линиями с дефектом.


Этот heightmap сгенерирован с большим количеством линий с дефектом с тяжелой фильтрацией.

Поскольку вы можете менять параметры настройки создания дефекта, у вас может быть широкий диапазон результатов.

Теперь давайте смотреть, как работает код:

Во-первых, я добавил класс HeightmapFaultSettings, который используется как контейнер для minimumDelta, maximumDelta (оба используются для вычисления смещения), число итераций, число итераций фильтрации, и значение фильтра (между 0.0 и 1.0).

Вот суть heightmap класса, который создает дефект:

/// <summary>

/// Generate a random heightmap using fault line deformation and the passed parameters.

/// </summary>

/// <param name=»width»></param>

/// <param name=»depth»></param>

/// <param name=»min»></param>

/// <param name=»max»></param>

/// <param name=»settings»></param>

public void GenerateFaultHeightmap(int width,

int depth,

float min,

float max,

HeightmapFaultSettings settings)

{

_width = width;

_depth = depth;

_faultSettings = settings;

_heightValues = new float[_width * _depth];

GenerateFaultHeightmap();

}

/// <summary>

/// Generate a random heightmap using fault line deformation and the _faultSettings.

/// </summary>

public void GenerateFaultHeightmap()

{

int x1, z1, dx1, dz1;

int x2, z2, dx2, dz2;

int deltaHeight;

for (int i = 0; i < _faultSettings.Iterations; ++i)

{

// Calculate the deltaHeight for this iteration.

// (linear interpolation from max delta to min delta).

deltaHeight = _faultSettings.MaximumDelta —

((_faultSettings.MaximumDelta —

_faultSettings.MinimumDelta) * i) /

_faultSettings.Iterations;

// Pick two random points on the field for the line.

// (make sure they aren’t identical).

x1 = RandomHelper.Random.Next(_width);

z1 = RandomHelper.Random.Next(_depth);

do

{

x2 = RandomHelper.Random.Next(_width);

z2 = RandomHelper.Random.Next(_depth);

} while (x1 == x2 && z1 == z2);

// dx1, dz1 is a vector in the direction of the line.

dx1 = x2 — x1;

dz1 = z2 — z1;

for (x2 = 0; x2 < _width; ++x2)

{

for (z2 = 0; z2 < _depth; ++z2)

{

// dx2, dz2 is a vector from x1, z1 to the candidate point.

dx2 = x2 — x1;

dz2 = z2 — z1;

// if y component of the cross product is ‘up’, then elevate this point.

if (dx2 * dz1 — dx1 * dz2 > 0)

{

_heightValues[x2 + _width * z2] += (float)deltaHeight;

}

}

}

// Erode the terrain.

if ((_faultSettings.IterationsPerFilter != 0) &&

(i % _faultSettings.IterationsPerFilter) == 0)

{

FilterHeightmap(_faultSettings.FilterValue);

}

}

// Normalize heightmap (height field values in the range _minimumHeight — _maximumHeight.

NormalizeHeightmap();

}

/// <summary>

/// Normalize the heightmap.

/// </summary>

public void NormalizeHeightmap()

{

float min = float.MaxValue;

float max = float.MinValue;

// Get the lowest and the highest values.

for (int x = 0; x < _width; ++x)

{

for (int z = 0; z < _depth; ++z)

{

if (_heightValues[x + z * _width] > max)

{

max = _heightValues[x + z * _width];

}

if (_heightValues[x + z * _width] < min)

{

min = _heightValues[x + z * _width];

}

}

}

// If the heightmap is flat, we set it to the average between _minimumHeight and _maximumHeight.

if (max <= min)

{

for (int x = 0; x < _width; ++x)

{

for (int z = 0; z < _depth; ++z)

{

_heightValues[x + z * _width] = (_maximumHeight — _minimumHeight) * 0.5f;

}

}

return;

}

// Normalize the value between 0.0 and 1.0

//then scale it between _minimumHeight and _maximumHeight.

float diff = max — min;

float scale = _maximumHeight — _minimumHeight;

for (int x = 0; x < _width; ++x)

{

for (int z = 0; z < _depth; ++z)

{

_heightValues[x + z * _width] = (_heightValues[x + z * _width] — min ) /

diff * scale + _minimumHeight;

}

}

}

/// <summary>

/// Filter the heightmap using a low pass filter in all four directions..

/// </summary>

/// <param name=»filterValue»></param>

public void FilterHeightmap(float filterValue)

{

// Erode rows left to right.

for (int j = 0; j < _depth; ++j)

{

for (int i = 1; i < _width; ++i)

{

_heightValues[i + j * _width] = filterValue *

_heightValues[i — 1 + j * _width] +

(1 — filterValue) * _heightValues[i + j * _width];

}

}

// Erode rows right to left.

for (int j = 0; j < _depth; ++j)

{

for (int i = 0; i < _width — 1; ++i)

{

_heightValues[i + j * _width] = filterValue *

_heightValues[i + 1 + j * _width] +

(1 — filterValue) * _heightValues[i + j * _width];

}

}

// Erode columns top to bottom.

for (int j = 1; j < _depth; ++j)

{

for (int i = 0; i < _width; ++i)

{

_heightValues[i + j * _width] = filterValue *

_heightValues[i + (j — 1) * _width] +

(1 — filterValue) * _heightValues[i + j * _width];

}

}

// Erode columns bottom to top.

for (int j = 0; j < _depth — 1; ++j)

{

for (int i = 0; i < _width; ++i)

{

_heightValues[i + j * _width] = filterValue *

_heightValues[i + (j + 1) * _width] +

(1 — filterValue) * _heightValues[i + j * _width];

}

}

}


Реклама
Запись опубликована в рубрике Uncategorized. Добавьте в закладки постоянную ссылку.

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s