Ландшафт с Геометрией Clipmaps в XNA (Перевод) Часть 1

Оригинал: http://www.ziggyware.com/readarticle.php?article_id=220

Содержание

  • Введение
  • Описание техники
  • Реализация
  • Добавление теней (шейдер)

Загрузки

  • Пустой проект
  • Основная реализация
  • Реализация теней

Введение

В этом учебном пособии я покажу вам, как создать основную систему ландшафта, очень эффективную и имеющую хорошую дальность обзора. Техника разработана Lossasso и Hoppe и названа Геометрией Clipmaps. Вы можете найти оригинал, в котором эта техника описывается более абстрактно (см. здесь). Также есть работа от компании NVidia, в которой говорится о том, как реализовать эту технику полностью на GPU (см.здесь). На самом деле идея весьма проста. Но мне всё же понадобилось потратить какое- то время, чтобы написать код для этого. Так что теперь я хотел бы поделиться своим опытом и надеюсь, что для некоторых это окажется полезным.

Сначала я хочу отразить основную идею, чтобы вам было понятно, о чем идет речь и без прочтения оригинала (если вам не хочется этого делать). Однако я не собираюсь стопроцентно реализовывать оригинальную технику, так как она не очень полезна для движка игр. В моем методе немного ограничивается размер ландшафта, но зато возможно создать достаточно большие и впечатляющие пейзажи. Я также собираюсь показать HLSL-код в файле эффектов, который выполняет основное текстурирование. Таким образом, у вас будет отправная точка, начиная с которой вы можете построить свой собственный движок ландшафта.

Вот небольшая предварительная картина того, что должно получиться в результате. Картина показывает ландшафт, который определен картой высот heightmap размером 1024×1024.

Техника — Текстура clipmaps и геометрия clipmaps

Текстура clipmap имеет тот же самый эффект что и у известного mip-map: представление большой текстурной области с уменьшающимся уровнем детализации. Каждый уровень детализации, хранящийся в mip/clipmap, обычно является регулярной сеткой с разрешением степени два.

Что-то похожее на представление большой области ландшафта было разработано и у Losasso и Hoppe. Уровень детализации зависит от расстояния до центра clipmap, что, в общем, также является положением зрителей.

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


Mimpap википедия

Clipmap техническое описание

Clipmap с 3мя уровнями

Синий: высокое разрешение

Красный: низкое разрешение

Heightmap, геометрия и вложенные сетки.

Источником для значений высот будет простая карта высот heightmap. Я предпочитаю формат DDS R32F, где каждый пиксель хранится как число с плавающей запятой со значением между 0.0f и 1.0f. Этот формат используется во всем учебном пособии.

Зритель может прогуляться по ландшафту, определенному с помощью этой карты высот heightmap. При любом положении зрителя имеется маленькая, высоко детализированная сетка и несколько более больших и менее детализированных сеток ландшафта вокруг зрителя. Каждую такую сетку называют уровнем L с шагом G=2^L размера NxN. У самого мелкого уровня L=0 шаг сетки G=1 (т.е. 2^L=2^0=1). Каждый уровень имеет одно и то же значение N и хранит значения вершин в массиве вершин размера NxN, где N должно быть меньше степени двойки (N = 2^x — 1). Вершины должны быть проиндексированы, чтобы определить треугольники. Таким образом, каждый уровень также хранит множество индексов. У уровней должны быть границы, чтобы определить, где более мелкий вложенный уровень заканчивается и где более крупный начинается. Внутренние вершины уровня, которые покрыты вложенным уровнем, не будут прорисованы, но они должны сохраняться и обновляться.

Самый мелкий уровень с L = 0 вложен в более крупный.

Внутренние вершины этого более крупного уровня обновляются, но они не видны.

Обновление данных уровня за кадр


Уровни покрывают квадратную область ландшафта. По мере движения зрителя, уровни также двигаются в том же направлении. Перемещаясь, перед зрителем получается область в форме L с новыми вершинами, которые не сохранены в текущем уровне. Позади зрителя получается область в форме L со старыми вершинами, которые больше не нужны. У этих форм тот же самый размер области, и мы можем просто сохранить новые вершины вместо старых. Таким образом, нет необходимости в обновлении тех вершин, которые еще видны. Поэтому мы всегда обновляем только часть массива вершин. Это означает, что мы должны повторно вычислить полностью массив индексов, потому что порядок вершин меняется, поэтому первоначальный уровень, который был сохранен в одном месте массива, при следующем обновлении находится уже в другом месте.

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

Больше ничего и нет, чтобы должен был бы сделать центральный процессор.

Области областей и области видимости

У каждого уровня есть только один массив вершин и один массив индексов. Так как каждый уровень будет нарисован одной единственной командой перерисовки, важно знать о способности графических карт рисовать большое количество вершин. Новые поколения графических карт предпочитают большое количество вершин, а не маленькое. Но у всего есть свой предел. Это ваше дело выяснить, какими способностями обладает ваша графическая карта. Я думаю, что уровень с вершинами 63×63 довольно хорошо смотрится и имеет представительный размер. Этот результат приблизительно имеет около 4000 вершин за команду перерисовки и только 5 уровней (5 команд перерисовок) покроют полностью карту вершин heightmap размером 1024×1024 и всё, что находится вне области видимости (ландшафт позади зрителя будет нарисован). Добавление такой оптимизации даст нам даже большую производительность и дальность обзора, если мы этого захотим. В реализации я использую уровни размером 127×127 вершин. Это приблизительно 16000 вершин всего, но так как моя камера всегда находится близко к ландшафту и помещена в центр, больше, чем половина вершин отбрасываются. 5 уровней покрывают область карты вершин heightmap размером 4096×4096. Этого достаточно. Чтобы добавить такую оптимизацию, мы будем делить каждый уровень на части и использовать только те индексы, которые определяют видимую область уровня. Возможно, затратить один массив вершин за часть и иметь множество массивов за уровень, что может дать уровни еще больших размеров, но я думаю, что это сделало бы это учебное пособие еще более сложным. Картина показывает, как мы можем поделить уровень на части. Не волнуйтесь о цветах, мы увидим позже, для чего они.

Уровень разрезан на части. Внутренний прямоугольник может быть поделен еще на 4 части.

Переходы от мелкого к более крупным уровням: Геоморфинг.

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

Ландшафт без геоморфинга.

Видны трещины между уровнями с разной детализацией

Реализация

Сначала нам нужна структура, которая определяет вершину. Мы пытаемся использовать как можно меньше данных, чтобы сохранить пропускную способность низкой, так как мы посылаем данные на каждой команде перерисовки в GPU. И пытаемся держать эти данные с низкой скорость перерисовки. Это приводит нас к использованию, по крайней мере, четырех значений. Три, чтобы определить положение вершины (x, y, z), и одно, дополнительное, чтобы хранить второе значение высоты, чтобы иметь возможность скрыть трещины, если это будет необходимо. Vector4 соответствует нашим потребностям, таким образом, мы объединяем это в структуру вершины.

using System; 
using Microsoft.Xna.Framework; 
using Microsoft.Xna.Framework.Graphics; 
namespace GeoClipmapping 
{ 
 /// <summary> 
 /// Decribes a custom vertex format structure that contains a Vector4 position. 
 /// </summary> 
 public struct VertexPosition4 
 { 
 public Vector4 Position; 
 public static int SizeInBytes = (4) * sizeof(float); 
 public static VertexElement[] VertexElements = new VertexElement[] 
 { 
 new VertexElement( 0, 0, VertexElementFormat.Vector4, 
 VertexElementMethod.Default, 
 VertexElementUsage.Position, 0), 
 }; 
 } 
} 

Затем мы напишем весьма большой класс. Создайте следующий заголовок, чтобы объявить класс Clipmap. Это основная структура данных для техники clipmap.

using System; 
using System.Collections.Generic; 
using System.Text; 
using Microsoft.Xna.Framework; 
using Microsoft.Xna.Framework.Graphics; 
using Microsoft.Xna.Framework.Graphics.PackedVector; 
namespace GeoClipmapping 
{ 
 public class Clipmap 
 { 

Затем добавьте все необходимые переменные и поля. Как мы видели до этого, нам необходимы значения L, G, N области и два массива, чтобы описать уровень. Кроме того нам необходимо значение, которое мы назовем М. Это число вершин между границами крупного и вложенного в него более мелкого уровня. Мы увидим позже, для чего это нужно.

#region Properties and Fields 
// G2 and Mm1G are values that are heavily used by our algorithm. To save 
// calculationtime, we precalculate these two. 
private readonly int G2; // G * 2 
private readonly int Mm1G; // (M - 1) * G 
private int G; 
/// <summary> 
/// Gets the distance between two horizontal/vertical vertices. The finest level L = 0 has 
/// a distance of G = 1. Then every level it doubles so G is always G = 2^L 
/// </summary> 
public int FactorG 
{ 
 get { return G; } 
} 
private int L; 
/// <summary> 
/// Gets the levelindex of current clip. The finest level has the index L = 0 
/// </summary> 
public int FactorL 
{ 
 get { return L; } 
} 
private int M; 
/// <summary> 
/// Gets framesize in number of vertices between outer border of current clip 
/// and outer border of next finer (inner) clip. 
/// </summary> 
public int FactorM 
{ 
 get { return M; } 
} 
private int N; 
/// <summary> 
/// Gets the width of a clip in number of vertices. This is always one less than 
/// power of two (2^x - 1) 
/// </summary> 
public int FactorN 
{ 
 get { return N; } 
} 
private Rectangle clipRegion; 
/// <summary> 
/// Gets the region of the current clip. 
/// </summary> 
public Rectangle ClipRegion 
{ 
 get { return clipRegion; } 
} 
/// <summary> 
/// Value that indicates the height scaling. 
/// It is also represents the maximum terrain height. 
/// </summary> 
private float S = 32; 
private VertexPosition4[] vertices; 
/// <summary> 
/// Gets the vertices of current level. 
/// </summary> 
public VertexPosition4[] Vertices 
{ 
 get { return vertices; } 
} 
private short[] indices; 
/// <summary> 
/// Gets the indices of current level. 
/// </summary> 
public short[] Indices 
{ 
 get { return indices; } 
} 
private VertexDeclaration vertexDeclaration; 
/// <summary> 
/// Gets the used vertexdeclaration. 
/// </summary> 
public VertexDeclaration VertexDeclaration 
{ 
 get { return vertexDeclaration; } 
} 
private PrimitiveType primitiveType = PrimitiveType.TriangleStrip; 
/// <summary> 
/// Gets the used primitivetype. 
/// </summary> 
public PrimitiveType PrimitiveType 
{ 
 get { return primitiveType; } 
} 
/// <summary> 
/// Index to indicate how much vertices are added to the triangle strip. 
/// </summary> 
int stripIndex = 0; 
/// <summary> 
/// Gets the number of triangles that are visible in current frame. 
/// This changes every frame. 
/// </summary> 
public int Triangles 
{ 
 get { return stripIndex >= 3 ? this.stripIndex - 2 : 0; } 
} 
/// <summary> 
/// The used terrain heightfield. This must be set per reference so all clip levels 
/// share the same memory for that variable. The values are between 0.0f and 1.0f 
/// </summary> 
private float[] heightfield; 
/// <summary> 
/// The width and height in vertices of the heightfield. 
/// </summary> 
private int fieldsize; 
/// <summary> 
/// The currentyl used graphicdevice. 
/// </summary> 
private GraphicsDevice device; 
#endregion 
Реклама
Запись опубликована в рубрике Uncategorized. Добавьте в закладки постоянную ссылку.

3 комментария на «Ландшафт с Геометрией Clipmaps в XNA (Перевод) Часть 1»

  1. Bob:

    Hi, is there any way you could also provide us with the Original tutorial in English? Ziggyware is closed and I can’t read Russian…

    Spasiba!

  2. Bob:

    Hi.

    I’ve tried with archive.org, but it only has the intro of the tutorial:
    http://web.archive.org/web/20090502035703/http://www.ziggyware.com/readarticle.php?article_id=220

    The rest of the tutorial is not in their database (Error 404).

    So if you do happen to find it, it would be great if you could upload it somewhere. In any case, thanks for the reply!

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

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

Логотип WordPress.com

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

Фотография Twitter

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

Фотография Facebook

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

Google+ photo

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

Connecting to %s