【问题标题】:Image size independent shader图像大小独立着色器
【发布时间】:2020-06-01 09:07:19
【问题描述】:

我正在尝试为绘制圆形的图像材质创建着色器,而不管图像本身的纵横比如何。

在 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);
}

给出以下输出

如果我删除uv.x *= iResolution.x/iResolution.y; 线,圆圈将根据当前的纵横比扭曲。

现在我想在 Unity 中创建相同的效果,所以我尝试了(在我看来)相同的方法。 _MainTex_TexelSize 包含纹理的宽度/高度(来自docs):

{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
        }
    }
}

着色器按原样编译,但圆圈仍会根据图像的纵横比进行拉伸。

我认为这可能是由于使用 o.uv = TRANSFORM_TEX(v.uv, _MainTex); 设置 uv 的方式,所以我尝试将其除以图像的大小:

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

但是这没有任何作用

像这样设置 uv 的方式不同

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

导致圆的中心向右上方移动,但当纵横比改变时仍会变形。

我错过了/做错了什么步骤来获得像我在 shadertoy 中获得的纵横比独立结果?

【问题讨论】:

    标签: unity3d shader hlsl


    【解决方案1】:

    输入纹理_MainTex的纵横比与输出的纵横比无关*。在 shadertoy 示例中,输出是屏幕,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;
    }
    

    您可以使用derivatives 计算纵横比。这里 dx 和 dy 是每个像素的 uv 变化量。如果您希望 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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-10-20
      • 1970-01-01
      • 2013-03-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多