Я хочу создать внутренний теневой шейдер, как в фотошопе - PullRequest
1 голос
/ 26 марта 2019

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

varying vec2 v_texCoord;
varying vec4 v_fragmentColor;
uniform vec3 u_outlineColor;
uniform float u_threshold;
uniform float u_radius;

void main()
{
    float radius = u_radius;
    vec4 accum = vec4(0.0);
    vec4 normal = vec4(0.0);

    normal = texture2D(CC_Texture0, vec2(v_texCoord.x, v_texCoord.y));

    accum += texture2D(CC_Texture0, vec2(v_texCoord.x - radius, v_texCoord.y - radius));
    accum += texture2D(CC_Texture0, vec2(v_texCoord.x + radius, v_texCoord.y - radius));
    accum += texture2D(CC_Texture0, vec2(v_texCoord.x + radius, v_texCoord.y + radius));
    accum += texture2D(CC_Texture0, vec2(v_texCoord.x - radius, v_texCoord.y + radius));

    accum *= u_threshold;
    accum.rgb =  u_outlineColor * accum.a;
    accum.a = 0.0;

    normal = ( accum * (1.0 - normal.a)) + (normal * normal.a);

    gl_FragColor = v_fragmentColor * normal;
}

1 Ответ

2 голосов
/ 26 марта 2019

Прежде всего, вам нужно вычислить преобразование расстояния вашего изображения. Лучше всего использовать алгоритм jump flooding .

Jump Flooding

1) Укажите значимые пиксели, например, с помощью функции шага.

        fixed4 frag (v2f i) : SV_Target
        {
            return step( _Threshold, tex2D( _MainTex, i.uv ).a );
        }

2) Визуализация начального изображения для алгоритма скачкообразного затопления. В начальном изображении кодируйте (0,0) для значимых пикселей и фактическое положение УФ для незначительных (на изображении выше, RG кодирует U, а BA кодирует V с точностью 16 бит).

        fixed4 frag (v2f i) : SV_Target
        {
            return lerp(
                fixed4( 0.0, 0.0, 0.0, 0.0 ),
                fixed4( EncodeFloatRG( i.uv.x ), EncodeFloatRG( i.uv.y ) ),                 
                1.0 - step( 0.01, tex2D( _MainTex, i.uv ).r )
            );        
        }

3-10) Прыжки с шагами. На каждом шаге вы должны перебирать «соседние» пиксели и находить, какой из них содержит закодированную позицию UV, ближайшую к позиции UV текущего пикселя. Наводнение должно начинаться с самых дальних «соседей», а затем постепенно уменьшать расстояние поиска.

        void JumpFlooding(half2 uv, half2 duv, inout half2 nearestPos, inout half nearestDist)
        {
            fixed4 seed = tex2D( _MainTex, uv + duv * _MainTex_TexelSize.xy );
            half2 pos = half2( DecodeFloatRG( seed.xy ), DecodeFloatRG( seed.zw ) );
            if( length(pos) > 0.0 )
            {
                half dist = distance( uv, pos );
                if( dist < nearestDist )
                {
                    nearestDist = dist;
                    nearestPos = pos;
                }
            }
        }

        fixed4 frag (v2f i) : SV_Target
        {
            half2 nearestPos = half2( 0, 0 );
            half nearestDist = 2.0;

            JumpFlooding( i.uv, half2( -_Offset, -_Offset ), nearestPos, nearestDist );
            JumpFlooding( i.uv, half2( -_Offset, 0 ), nearestPos, nearestDist );
            JumpFlooding( i.uv, half2( -_Offset, _Offset ), nearestPos, nearestDist );
            JumpFlooding( i.uv, half2( 0, _Offset ), nearestPos, nearestDist );
            JumpFlooding( i.uv, half2( _Offset, _Offset ), nearestPos, nearestDist );
            JumpFlooding( i.uv, half2( _Offset, 0 ), nearestPos, nearestDist );
            JumpFlooding( i.uv, half2( _Offset, -_Offset ), nearestPos, nearestDist );
            JumpFlooding( i.uv, half2( 0, -_Offset ), nearestPos, nearestDist );
            JumpFlooding( i.uv, half2( 0, 0 ), nearestPos, nearestDist );

            return fixed4( EncodeFloatRG( nearestPos.x ), EncodeFloatRG( nearestPos.y ) );
        }

11) Наконец, последний результат алгоритма скачкообразного затопления закодирован в поле расстояния.

        fixed4 frag (v2f i) : SV_Target
        {
            fixed4 seed = tex2D( _MainTex, i.uv );
            half2 pos = half2( DecodeFloatRG( seed.xy ), DecodeFloatRG( seed.zw ) );
            float dist = distance( i.uv, pos );                 
            return EncodeFloatRGBA( dist );
        }

И теперь вы можете использовать поле расстояния для рисования внутренней тени.

Inner shadow

        fixed4 frag (v2f i) : SV_Target
        {
            half4 color = _ShadowColor;

            half dist = DecodeFloatRGBA( tex2D(_DistTex, i.uv) );

            color.a *= ( 1.0 - smoothstep( _ShadowMinRange, _ShadowMaxRange, dist ) );

            return color;                          
        }
...