Сглаживание шумов с разными амплитудами (часть 2) - PullRequest
0 голосов
/ 27 ноября 2018

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

Я решил использовать контур / теньshape ( Translating / transming - список точек от его центра со смещением / расстоянием ).

Этот контур / тень больше текущего пути.Я использовал этот репозиторий (https://github.com/n-yoda/unity-vertex-effects), чтобы воссоздать тень. И это работает довольно хорошо, за исключением одного факта.

Чтобы узнать высоту всех точек (полученных этим алгоритмом тени (Строка 13 из ModifiedShadow.cs & Строка 69 из CircleOutline.cs )) Я получаю расстояние от текущей точки до центра и делю максимальное расстояние до центра:

float dist = orig.Max(v => (v - Center).magnitude);
foreach Point in poly --> float d = 1f - (Center - p).magnitude / dist;

Где orig - полный список точек, полученных алгоритмом тени, D - высота тени.

Но проблема очевидна, я получаю идеальный круг:

...

В красном и черном, чтобы увидеть контраст:

...

И это не то, чтоЯ хочу:

...

Как видите, это не идеальный градиент. Давайте объясним, что происходит.

Я использую эту библиотеку длягенерировать шумы: https://github.com/Auburns/FastNoise_CSharp

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

...

  • Зеленый фоновый цвет представляет шумы со средней высотой -0,25 и амплитудой 0,3
  • Белый фоновый цвет представляет шумы со средней высотой 0 и амплитудой 0,1
  • Красный означает 1 (общая интерполяция для шумов, соответствующих белым пикселям)
  • Черный означает 0 (общая интерполяция для шумов, соответствующих зеленым пикселям)

Вот почему мыу меня есть такой вывод:

...

На самом деле, я попытался сравнить расстояния каждой отдельной точки до центра, но это выдает странный и неожиданный результат.

На самом деле, я не знаю, что попробовать ...

1 Ответ

0 голосов
/ 28 ноября 2018

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

Например, центральная точка с левой стороны многоугольника может находиться на расстоянии 300 пикселей от центра, а центральная точка справа может быть 5 пикселей.Оба должны быть красного цвета, но при выделении 0 distance from center = red не будет красного, а при выделении min distance from center = red только красный будет справа.

Соответствующие минимальное и максимальное расстояния будут меняться в зависимости от того, где находится точка

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

Следовательно, вы можете сделать это (псевдо-C #):

foreach pixel p in shadow_region {

    // technically, closest shadow pixel which is adjacent to x Pixel: 
    float closestGreen_distance = +inf;
    float closestWhite_distance = +inf;

    // Possibly: find all shadow-adjacent pixels prior to the outer loop 
    // and cache them. Then, you only have to loop through those pixels.
    foreach pixel p2 in shadow {
        float p2Dist = (p-p2).magnitude;

        if (p2 is adjacent to green) {
           if (p2Dist < closestGreen_distance) {
               closestGreen_distance = p2Dist;
           }
        }

        if (p2 is adjacent to white) {
           if (p2Dist < closestWhite_distance) {
               closestWhite_distance = p2Dist;
           }
        }
    }

    float d = 1f - closestWhite_distance / (closestWhite_distance + closestGreen_distance)
}

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

foreach (Point p in value)
{
    float minOuterDistance = outerPoints.Min(p2 => (p - p2).magnitude);
    float minInnerDistance = innerPoints.Min(p2 => (p - p2).magnitude);

    float d = 1f - minInnerDistance / (minInnerDistance + minOuterDistance);

    Color32? colorValue = func?.Invoke(p.x, p.y, d);

    if (colorValue.HasValue)
        target[F.P(p.x, p.y, width, height)] = colorValue.Value;
}

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


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

Создайте внешние "желтые" вершины, перейдя к каждой розовой вершине и следуя ее нормали наружу.Создайте внутренние «синие» вершины, перейдя к каждой розовой вершине и следуя за ее нормальным внутренним слоем.

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

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

Один шаг - игнорировать любые синие / желтые цвета, которые имеют внешние нормали, которые указывают на текущий теневой пиксель.

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

чрезвычайно грубый псевдокод:

list yellow_vertex_list = new list 
list blue_vertex_list = new list 
foreach pink vertex p:
    given float dist;
    vertex yellowvertex = new vertex(p+normal*dist)
    vertex bluevertex = new vertex(p-normal*dist)

    yellow_vertex_list.add(yellowvertex)
    blue_vertex_list.add(bluevertex)

create shadow

for each pixel p in shadow:
    foreach vertex v in blue_vertex_list
        if v.normal points towards v: break;
        if v is the wrong side of inside-out region: break;
        if v is closest so far:
            closest_blue = v
            closest_blue_dist = (v-p).magnitude

    foreach vertex v in yellow_vertex_list
        if v.normal points towards v break;
        if v is the wrong side of inside-out region: break;
        if v is closest so far:
            closest_yellow = v
            closest_yellow_dist = (v-p).magnitude


    float d = 1f - closest_blue_dist / (closest_blue_dist + closest_yellow_dist)
...