Шумные края, сглаживание краев между гранями с помощью фрагментного шейдера


8

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

введите описание изображения здесь

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

введите описание изображения здесь

То, к чему я стремлюсь, это что-то вроде этого:

введите описание изображения здесь

У каждой вершины есть атрибут, который содержит тип биома, я также могу добавить специальные атрибуты к вершинам на границе между двумя биомами, но я просто не могу понять, как осуществить это в шейдерном коде, очевидно, шум участвует здесь, но как мне сделать его непрерывным на нескольких лицах и на всей границе нескольких биомов?

Я рендеринг с WebGL с использованием THREE.js


Вы пробовали MSAA?
Мило Лу

@Milo Вы пытались прочитать вопрос?
Балинт

Как вы отправляете информацию о биоме шейдеру?
Балинт

@ Bálint Biome Прямо сейчас я передаю цвет с помощью атрибута вершины, когда я на самом деле собираю образцы цветов из реальных текстур, вместо простых цветов я передаю тип биома как целое число.
user1617735

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

Ответы:


9

Другие ответы здесь предлагают использовать текстуру. Вот техника, которая не использует текстуры.

Вы хотите, чтобы границы между шестиугольниками были интересными. Проще сделать интересные границы, когда вы перемещаете их в центр того, что рисуете. Вместо того, чтобы рисовать плитки напрямую, вы рисуете «двойник» плитки. Эта техника называется «угловая плитка» ( здесь и здесь, и здесь ). Двойником шестиугольника является треугольник, поэтому мы будем рисовать эти треугольники вместо шестиугольников:

треугольник двойников шестиугольника

Границы между шестиугольниками теперь находятся в середине визуализированных треугольников, так что это позволит нам делать с ними более интересные вещи. Бонус: вам нужно нарисовать только два треугольника на шестиугольник вместо шести (или двадцати четырех, как вы делаете сейчас).

Внутри каждого из этих треугольников мы хотим, чтобы фрагментный шейдер рисовал шестиугольники. Мы можем сделать это с помощью барицентрических координат . Положите (1,0,0), (0,1,0) и (0,0,1) в каждой вершине треугольника. Внутри треугольника эти координаты будут интерполированы. Фрагментный шейдер получит (a, b, c) и может посмотреть, какое значение является наибольшим - это скажет нам, какой из трех шестиугольников следует нарисовать в этой точке.

float max_n = max(barycentric.r, max(barycentric.g, barycentric.b)); if (max_n == barycentric.r) { color = v_color0; } else if (max_n == barycentric.g) { color = v_color1; } else if (max_n == barycentric.b) { color = v_color2; }

Это для прямых.

Если вы хотите шумные ребра, вы можете добавить шум к барицентрическим координатам:

шестигранник с шумными краями

Играя с амплитудой длина волны / частота шума, вы можете получить некоторые интересные эффекты:

шестигранник с еще более шумными краями

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

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


Теперь это качественный материал для ответов. Наконечник шляпы
Квентин

Отличный ответ! Тонкая коррекция: правильные многоугольники, включая шестиугольники, являются двойственными. Тем не менее, как показывает ваш ответ , тесселяции треугольников и шестиугольников двойственны друг другу.
Эрик Фосс,

0

Я уверен, что «можно» решить с помощью какого-либо алгоритма изображения, но если бы это был я, я бы, вероятно, решил это с помощью текстур. Я делал гексагональные текстуры, помещал их все в текстурный атлас, затем для каждого шестиугольника смотрел на его соседей и решал, какую текстуру применять.

Текстуры должны иметь версии для каждого типа местности плюс версии для каждого типа перехода.

Это похоже на то, сколько систем на основе плиток делают рельеф. Вот пример из 2d игр .

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

Эта статья демонстрирует идею смешивания текстур ландшафта. Я не предлагаю следовать их реализации, но это показывает идею.


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

0

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

// frequency and magnitude of the noise in texels.
uniform float frequency;
uniform float magnitude;

// This function should just look like random smooth noise in x,y
// This one isn't great, but you can experiment.
vec2 noise(vec3 vertexPos) {
    return vec2(sin(vertexPos.x * frequency + vertexPos.y * frequency) * magnitude, 
                cos(vertexPos.y * frequency * 2 + vertexPos.z * frequency) * magnitude);
}

// Sample the texture and preturb it with noise based on the world
// position of the fragment.
vec4 frag(vec2 texCoord, vec3 fragmentPos) {
   return texture(mySampler, texCoord + noise(fragmentPos));
}

где noise- некоторая псевдослучайная функция (использующая, скажем, синусоиды) в трехмерном положении вершины в пространстве модели, которая возвращает шумовое смещение к координате текстуры. Пример текстуры, GL_NEARESTчтобы сохранить четкие границы.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.