Per-Vertex Normalals от перлин-шума? - PullRequest
8 голосов
/ 28 июня 2011

Я генерирую ландшафт в геометрическом шейдере Opengl и у меня возникают проблемы с вычислением нормалей для освещения.Я генерирую ландшафт динамически в каждом кадре с помощью функции перлин-шума, реализованной в геометрическом шейдере.Из-за этого мне нужен эффективный способ вычисления нормалей на вершину на основе функции шума (без текстуры или чего-либо еще).Я могу взять кросс-произведение двух сторон, чтобы получить грани нормали, но они генерируются динамически с геометрией, поэтому я не могу затем вернуться и сгладить грани нормали для вершинных нормалей.Как я могу получить нормали вершин на лету, просто используя функцию шума, которая генерирует высоту моего рельефа в плоскости y (следовательно, высота составляет от 1 до -1).Я считаю, что мне нужно сэмплировать функцию шума 4 раза для каждой вершины, но я попробовал что-то вроде следующего, и это не сработало ...

vec3 xP1 = vertex + vec3(1.0, 0.0, 0.0);
vec3 xN1 = vertex + vec3(-1.0, 0.0, 0.0);
vec3 zP1 = vertex + vec3(0.0, 0.0, 1.0);
vec3 zN1 = vertex + vec3(0.0, 0.0, -1.0);

float sx = snoise(xP1) - snoise(xN1);
float sz = snoise(zP1) - snoise(zN1);

vec3 n = vec3(-sx, 1.0, sz);
normalize(n);

return n;

Выше на самом деле генерировалось освещение, которое перемещалось как perlinшум!Итак, есть ли какой-нибудь совет, как мне правильно получить нормали для каждой вершины?

Ответы [ 2 ]

8 голосов
/ 28 июня 2011

Нормаль - это вектор, перпендикулярный касательной (также известный как наклон). Наклон функции является ее производной; для n измерений его n частных производных. Таким образом, вы производите выборку шума вокруг центральной точки P и при P ± (δx, 0) и P ± (0, δy), причем δx, δy выбираются настолько малыми, насколько это возможно, но достаточно большими для численной устойчивости. Это дает вам касательные в каждом направлении. Затем вы берете их произведение, нормализуете результат и получаете нормальное значение P.

5 голосов
/ 28 июня 2011

Вы не сказали точно, как вы на самом деле генерировали позиции.Поэтому я предполагаю, что вы используете шум Перлина для генерации значений высоты на карте высот.Итак, для любой позиции X, Y в hieghtmap вы используете функцию 2D-шума для генерации значения Z.

Итак, давайте предположим, что ваша позиция вычисляется следующим образом:

vec3 CalcPosition(in vec2 loc) {
    float height = MyNoiseFunc2D(loc);
    return vec3(loc, height);
}

Это создает трехмерную позицию.Но в каком пространстве находится эта позиция?Вот в чем вопрос.

Большинство шумовых функций ожидают, что loc будут двумя значениями в некотором конкретном диапазоне с плавающей точкой.Насколько хороша ваша шумовая функция, будет определять, в каком диапазоне вы можете передавать значения. Теперь, если не гарантируется, что 2D-позиции вашего модельного пространства находятся в пределах диапазона шумовой функции, вам необходимо преобразовать их в этот диапазон, выполнить вычисления изатем преобразуйте его обратно в пространство модели.

Таким образом, теперь у вас есть 3D-позиция.Преобразование для значений X и Y простое (обратное преобразование к пространству шумовой функции), но что из Z?Здесь вы должны применить какую-то шкалу к высоте.Функция шума будет возвращать число в диапазоне [0, 1), поэтому вам нужно масштабировать этот диапазон до того же пространства модели, к которому будут подходить ваши значения X и Y.Обычно это делается путем выбора максимальной высоты и масштабирования положения соответствующим образом.Поэтому наша пересмотренная позиция вычисления выглядит примерно так:

vec3 CalcPosition(in vec2 modelLoc, const in mat3 modelToNoise, const in mat4 noiseToModel)
{
    vec2 loc = modelToNoise * vec3(modelLoc, 1.0);
    float height = MyNoiseFunc2D(loc);
    vec4 modelPos = noiseToModel * vec4(loc, height, 1.0);
    return modelPos.xyz;
}

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

Хорошо, теперь, когда мы установили, что вам нужно помнить следующее:ничто не имеет смысла, если вы не знаете, в каком пространстве оно находится. Ваше нормальное положение, ваши позиции, ничего не имеет значения, пока вы не установите, в каком пространстве оно находится.

Эта функция возвращает позиции в пространстве модели.Нам нужно вычислить нормали в пространстве модели .Для этого нам понадобятся 3 позиции: текущая позиция вершины и две позиции, которые немного смещены относительно текущей позиции.Позиции, которые мы получаем , должны находиться в пространстве модели, иначе наша нормаль не будет.

Следовательно, нам нужна следующая функция:

void CalcDeltas(in vec2 modelLoc, const in mat3 modelToNoise, const in mat4 noiseToModel, out vec3 modelXOffset, out vec3 modelYOffset)
{
    vec2 loc = modelToNoise * vec3(modelLoc, 1.0);
    vec2 xOffsetLoc = loc + vec2(delta, 0.0);
    vec2 yOffsetLoc = loc + vec2(0.0, delta);
    float xOffsetHeight = MyNoiseFunc2D(xOffsetLoc);
    float yOffsetHeight = MyNoiseFunc2D(yOffsetLoc);
    modelXOffset = (noiseToModel * vec4(xOffsetLoc, xOffsetHeight, 1.0)).xyz;
    modelYOffset = (noiseToModel * vec4(yOffsetLoc, yOffsetHeight, 1.0)).xyz;
}

Очевидно,Вы можете объединить эти две функции в одну.

Значение delta является небольшим смещением в пространстве ввода текстуры шума.Размер этого смещения зависит от вашей шумовой функции;он должен быть достаточно большим, чтобы возвращать высоту, которая значительно отличается от высоты, возвращаемой фактической текущей позицией.Но оно должно быть маленьким достаточно, чтобы вы не вытягивали из случайных частей распределения шума.

Вы должны узнать свою функцию шума.

Теперь, когдау вас есть три позиции (текущая позиция, смещение по x и смещение по оси y) в пространстве модели, вы можете вычислить нормаль вершины в пространстве модели:

vec3 modelXGrad = modelXOffset - modelPosition;
vec3 modelYGrad = modelYOffset - modelPosition;

vec3 modelNormal = normalize(cross(modelXGrad, modelYGrad));

Отсюда выполните обычныевещи.Но никогда не забывайте следить за пробелами ваших различных векторов.

О, и еще одна вещь: это должно быть сделано в шейдере vertex .Нет смысла делать это в геометрическом шейдере, поскольку ни одно из вычислений не влияет на другие вершины.Пусть параллелизм GPU сработает для вас.

...