Независимый от размера изображения шейдер - PullRequest
1 голос
/ 17 февраля 2020

Я пытаюсь создать шейдер для материала изображения, который имеет aws круг независимо от соотношения сторон самого изображения.

В Shadertoy (hlsl) я могу сделать следующее, чтобы создать круглый круг, независимо от соотношения сторон:

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord/iResolution.xy;
    uv -= 0.5;
    uv.x *= iResolution.x/iResolution.y; // < this compensates for the aspect ratio

    float l = length(uv);
    float s = smoothstep(0.5, 0.55, l);

    vec4 col = vec4(s);
    fragColor = vec4(col);
}

, который дает следующий вывод circular circle

Если я удалю линию uv.x *= iResolution.x/iResolution.y;, круг будет деформироваться в зависимости от текущего соотношения сторон.

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

{TextureName} _TexelSize - свойство float4 содержит информацию о размере текстуры:
- x содержит 1,0 / ширина
- y содержит 1,0 / высота
- z содержит ширину
- w содержит высоту

Shader "Unlit/Shader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100
        Blend SrcAlpha OneMinusSrcAlpha
        Cull off

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float4 _MainTex_TexelSize;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                o.uv -= 0.5;
                o.uv.x *= _MainTex_TexelSize.z / _MainTex_TexelSize.w;
                return o;
            }

            float DrawCircle(float2 uv, float radius, float fallOff)
            {
                float d = length(uv);
                return smoothstep(radius, fallOff, d);
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);

                float c = DrawCircle(i.uv, 0.5, 0.55);
                col = lerp(col, fixed4(1,0,0,1), c);
                return col;
            }
            ENDCG
        }
    }
}

Шейдер компилируется как есть, но круг будет по-прежнему растягиваться в зависимости от соотношения сторон изображения. stretched circle

Я подумал, что это может быть связано с тем, как настроены ультрафиолетовые лучи с использованием o.uv = TRANSFORM_TEX(v.uv, _MainTex);, поэтому я попытался разделить это на размер изображения:

o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.uv / _MainTex_TexelSize.zw;
o.uv -= 0.5;

Однако это ничего не сделало

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

o.uv = v.uv / _MainTex_TexelSize.zw;
o.uv / _MainTex_TexelSize.zw;
o.uv -= 0.5;

приводит к смещению центра круга в верхний правый угол, но все еще деформируется при изменении пропорций.

Какой шаг я пропускаю / делаю неправильно, чтобы получить независимый результат соотношения сторон, как в шадертой?

1 Ответ

1 голос
/ 17 февраля 2020

Соотношение сторон входной текстуры _MainTex не имеет ничего общего с соотношением сторон выходного сигнала *. В примере с шадертой этот вывод является экраном, а iResolution дает вам размеры экрана (эквивалент в единице равен _ScreenParams). Если вы хотите нарисовать четырехугольник, который не является полноэкранным, вы должны сопоставить форматное соотношение четырехугольников с форматным соотношением _MainTex, чтобы использовать _MainTex_TexelSize, или просто укажите форматное соотношение или размеры в свойстве шейдера (то есть в основном то, что делает _ScreenParams):

float _Aspect;    
fixed4 frag(v2f i) : SV_Target
{
    i.uv -= .5;     
    i.uv.x *= _Aspect;

    fixed4 col = tex2D(_MainTex, i.uv); 

    float c = DrawCircle(i.uv, .5, .55);        
    col = lerp(col, fixed4(1,0,0,1), c);    
    return col;
}

Вы можете рассчитать соотношение сторон с помощью производных . Здесь dx и dy - количество ультрафиолетовых изменений на пиксель. Это также будет полезно, если вы хотите, чтобы, например, fallOff всегда было 10 пикселей.

fixed4 frag(v2f i) : SV_Target
{
    i.uv -= .5; 
    float dx = ddx(i.uv.x);
    float dy = ddy(i.uv.y);
    float aspect = dy/dx;
    i.uv.x *= aspect;

    fixed4 col = tex2D(_MainTex, i.uv); 

    float c = DrawCircle(i.uv, .5, .55);
    col = lerp(col, fixed4(1,0,0,1), c);
    return col;
}
...