Почему я вижу эти смешанные артефакты в моем шейдере GLSL? - PullRequest
1 голос
/ 26 апреля 2019

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

Во-первых, вот поведение, за которым я следую (повторяется с использованием слоев Photoshop):

Photoshop screenshot #1

Обратите внимание, что все три цветовых слоя настроены на режим смешивания "Linear Dodge (Add)", который, насколько я понимаю, является "аддитивным" режимом наложения Photoshop.

Если я сливаю цветовые слои и оставляю результирующий слой с установленным смешиванием «Нормальный», я могу изменить цвет фона, как мне будет угодно.

Photoshop screenshot #2

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

Вот мой код шейдера в его текущем состоянии.

const int MAX_SHAPES = 10;
vec2 spread = vec2(0.3, 0.3);
vec2 offset = vec2(0.0, 0.0);
float shapeSize = 0.3;
const float s = 1.0;
float shapeColors[MAX_SHAPES * 3] = float[MAX_SHAPES * 3] (
  s, 0.0, 0.0,
  0.0, s, 0.0,
  0.0, 0.0, s,
  s, 0.0, 0.0,
  s, 0.0, 0.0,
  s, 0.0, 0.0,
  s, 0.0, 0.0,
  s, 0.0, 0.0,
  s, 0.0, 0.0,
  s, 0.0, 0.0
);

vec2 motionFunction (float i) {
  float t = iTime;

  return vec2(
    (cos(t * 0.31 + i * 3.0) + cos(t * 0.11 + i * 14.0) + cos(t * 0.78 + i * 30.0) + cos(t * 0.55 + i * 10.0)) / 4.0,
    (cos(t * 0.13 + i * 33.0) + cos(t * 0.66 + i * 38.0) + cos(t * 0.42 + i * 83.0) + cos(t * 0.9 + i * 29.0)) / 4.0
  );
}

float blend (float src, float dst, float alpha) {
  return alpha * src + (1.0 - alpha) * dst;
}

void mainImage (out vec4 fragColor, in vec2 fragCoord) {
    float aspect = iResolution.x / iResolution.y;
    float x = (fragCoord.x / iResolution.x) - 0.5;
    float y = (fragCoord.y / iResolution.y) - 0.5;
    vec2 pixel = vec2(x, y / aspect);

    vec4 totalColor = vec4(0.0, 0.0, 0.0, 0.0);
    for (int i = 0; i < MAX_SHAPES; i++) {
        if (i >= 3) {
            break;
        }

        vec2 shapeCenter = motionFunction(float(i));

        shapeCenter *= spread;
        shapeCenter += offset;
        float dx = shapeCenter.x - pixel.x;
        float dy = shapeCenter.y - pixel.y;
        float d = sqrt(dx * dx + dy * dy);
        float ratio = d / shapeSize;
        float intensity = 1.0 - clamp(ratio, 0.0, 1.0);
        totalColor.x = totalColor.x + shapeColors[i * 3 + 0] * intensity;
        totalColor.y = totalColor.y + shapeColors[i * 3 + 1] * intensity;
        totalColor.z = totalColor.z + shapeColors[i * 3 + 2] * intensity;
        totalColor.w = totalColor.w + intensity;
    }

    float alpha = clamp(totalColor.w, 0.0, 1.0);
    float background = 0.0;
    fragColor = vec4(
        blend(totalColor.x, background, alpha),
        blend(totalColor.y, background, alpha),
        blend(totalColor.z, background, alpha),
        1.0
    );
}

А вот версия ShaderToy, где вы можете посмотреть ее вживую - https://www.shadertoy.com/view/wlf3RM Или как видео - https://streamable.com/un25t

Визуальные артефакты должны быть довольно очевидными, но вот видео, которое указывает на них: https://streamable.com/kxaps (Я думаю, что они гораздо более распространены в видео, которое было связано до этого, хотя. Движение действительно заставляет их всплывать.)

Также в качестве статического изображения для сравнения: enter image description here

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

1 Ответ

2 голосов
/ 26 апреля 2019

Внутренние линии - это то, где totalColor.w достигает 1, и поэтому alpha находится внутри 1. Внешние, которые вы обрисовали белым, - это края окружностей.

Я изменил вашу ссылку ShaderToy, изменив float alpha = clamp(totalColor.w, 0.0, 1.0); на float alpha = 1.0; и float intensity = 1.0 - clamp(ratio, 0.0, 1.0); на float intensity = smoothstep(1.0, 0.0, ratio); (чтобы сгладить края окружностей), и теперь она выглядит как первое изображение.

...