Как размыть результат фрагмента шейдера? - PullRequest
4 голосов
/ 07 января 2012

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

Код Cg:

    float Luminance( float4 Color ){
    return 0.6 * Color.r + 0.3 * Color.g + 0.1 * Color.b;
}

            struct v2f {
                float4 pos : SV_POSITION;
                float2 uv_MainTex : TEXCOORD0;
            };

            float4 _MainTex_ST;

            v2f vert(appdata_base v) {
                v2f o;
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                o.uv_MainTex = TRANSFORM_TEX(v.texcoord, _MainTex);
                return o;
            }

            sampler2D _MainTex;
            sampler2D _Gradient;
            sampler2D _NoiseO;
            sampler2D _NoiseT;

            float4 frag(v2f IN) : COLOR {

                half4 nO = tex2D (_NoiseO, IN.uv_MainTex);
                half4 nT = tex2D (_NoiseT, IN.uv_MainTex);
                float4 turbulence = nO + nT;
                float lum = Luminance(turbulence);
                half4 c = tex2D (_MainTex, IN.uv_MainTex);
                if (lum >= 1.0f){

                    float pos = lum - 1.0f;
                    if( pos > 0.98f ) pos = 0.98f;
                    if( pos < 0.02f ) pos = 0.02f;
                    float2 texCord = (pos, pos);
                    half4 turb = tex2D (_Gradient, texCord);
                    //turb.a = 0.0f;
                    return turb;
                }
                else return c;
            }

1 Ответ

1 голос
/ 17 января 2012

Мне кажется, что этот шейдер эмулирует альфа-тестирование между текстурой, похожей на буфер, (передаваемой через sampler2D _MainTex) и сгенерированной яркостью облака (представленной float lum), сопоставленной с градиентом.Это усложняет ситуацию, потому что вы не можете просто подделать размытие и позволить альфа-блендингу позаботиться обо всем остальном.Вам также нужно будет изменить процедуру альфа-тестирования, чтобы вместо этого эмулировать альфа-смесь или соответствующим образом реструктурировать конвейер рендеринга.Сначала мы рассмотрим размытие облаков.

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

Вы можете эмулировать альфа-смешивание, используя lerp (линейную интерполяцию) между цветом turb и c с функцией lerp () (в зависимости от того, какой язык шейдера вы используете).повторное использование).Вы, вероятно, захотите что-то, похожее на return lerp(c, turb, 1 - pos); вместо return turb; ... Я ожидаю, что вы захотите постоянно подстраивать это, пока не поймете и начнете получать желаемые результаты.(Например, вы можете предпочесть lerp(c, turb, 1 - pow(pos,4)))

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

Редактировать: Я не рассматривал случай, когда сэмплеры _NoiseO и _NoiseT постоянно менялись, поэтому простое указание размыть их было минимально полезным советом.Вы можете эмулировать размытие, используя фильтр нескольких нажатий.Самый простой способ состоит в том, чтобы взять равномерно расположенные образцы, взвесить их и суммировать, чтобы получить окончательный цвет.(Как правило, вы хотите, чтобы сами веса составляли 1.)

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

Например, если мы собирались с первым случаем, и мы хотелисэмплер из сэмплера _Noise0 и немного размытие, мы могли бы использовать этот прямоугольный фильтр (где все веса одинаковы и сумма равна 1, что дает среднее значение):

// Untested code.
half4 nO  = 0.25 * tex2D(_Noise0, IN.uv_MainTex + float2(         0,          0))
          + 0.25 * tex2D(_Noise0, IN.uv_MainTex + float2(         0, g_offset.y))
          + 0.25 * tex2D(_Noise0, IN.uv_MainTex + float2(g_offset.x,          0))
          + 0.25 * tex2D(_Noise0, IN.uv_MainTex + float2(g_offset.x, g_offset.y))

В качестве альтернативы, если мы хотеливывод всего облака выглядит размытым, мы бы обернули часть генерации облака в функцию и вызвали бы ее вместо tex2D() для отводов.

// More untested code.
half4 genCloud(float2 tc) {
    half4 nO = tex2D (_NoiseO, IN.uv_MainTex);
    half4 nT = tex2D (_NoiseT, IN.uv_MainTex);
    float4 turbulence = nO + nT;
    float lum = Luminance(turbulence);
    float pos = lum - 1.0;
    if( pos > 0.98f ) pos = 0.98f;
    if( pos < 0.02f ) pos = 0.02f;
    float2 texCord = (pos, pos);
    half4 turb = tex2D (_Gradient, texCord);
    // Figure out how you'd generate your alpha blending constant here for your lerp
    turb.a = ACTUAL_ALPHA;
    return turb;
}

И фильтрация с несколькими отводами будет выглядеть следующим образом:

// And even more untested code.
half4 cloudcolor = 0.25 * genCloud(IN.uv_MainTex + float2(         0,          0))
                 + 0.25 * genCloud(IN.uv_MainTex + float2(         0, g_offset.y))
                 + 0.25 * genCloud(IN.uv_MainTex + float2(g_offset.x,          0))
                 + 0.25 * genCloud(IN.uv_MainTex + float2(g_offset.x, g_offset.y))
return lerp(c, cloudcolor, cloudcolor.a);

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

...