与图像大小无关的着色器
Image size independent shader
我正在尝试为图像创建着色器 material,无论图像本身的纵横比如何,它都会绘制一个圆。
在 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
包含纹理的width/height(来自docs):
{TextureName}_TexelSize - a float4 property contains texture size information:
- x contains 1.0/width
- y contains 1.0/height
- z contains width
- w contains height
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;
导致圆心移动到右上角,但纵横比改变时仍然扭曲。
我在哪一步 missing/doing 错误地获得了与在 shadertoy 中一样的纵横比独立结果?
输入纹理的纵横比_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;
}
我正在尝试为图像创建着色器 material,无论图像本身的纵横比如何,它都会绘制一个圆。
在 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
包含纹理的width/height(来自docs):
{TextureName}_TexelSize - a float4 property contains texture size information:
- x contains 1.0/width
- y contains 1.0/height
- z contains width
- w contains height
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;
导致圆心移动到右上角,但纵横比改变时仍然扭曲。
我在哪一步 missing/doing 错误地获得了与在 shadertoy 中一样的纵横比独立结果?
输入纹理的纵横比_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;
}