两段代码
最近在看 Shadertoy 网站,发现很多 shader 都有这行代码。
vec2 uv = (2.0 * fragCoord.xy - iResolution.xy) / min(iResolution.y, iResolution.x);这行代码一时看不明白。慢慢细看,实际上是跟如下代码等价的。
vec2 uv = fragCoord.xy / iResolution.xy
uv = 2.0 * uv - 1.0;
if (iResolution.x > iResolution.y) {
uv.x *= iResolution.x / iResolution.y;
} else {
uv.y *= iResolution.y / iResolution.x;
}在 shadertoy 中,iResolution 表示画布像素高宽。
fragCoord.xy / iResolution.xy会将坐标转换到 [0, 1] 之间。uv = 2.0 * uv - 1.0将坐标转换到 [-1, 1] 之间,中央为原点(0,0)。- 随后的判断,保持短边为 [-1, 1],长的那条边坐标相应放大。
等价解释
上述两段代码等价,一下子比较难看出来,分两种情况分析。当横屏时,iResolution.x > iResolution.y。第一段代码相应为
vec2 uv = (2.0 * fragCoord.xy - iResolution.xy) / iResolution.y;
=>
uv.x = (2.0 * fragCoord.x - iResolution.x) / iResolution.y
uv.y = (2.0 * fragCoord.y - iResolution.y) / iResolution.y
=>
uv.x = ((2.0 * fragCoord.x - iResolution.x) / iResolution.x) * (iResolution.x / iResolution.y)
uv.y = ((2.0 * fragCoord.y - iResolution.y) / iResolution.y) * 1.0;
=>
uv.x = (2.0 * fragCoord.x / iResolution.x - 1.0) * (iResolution.x / iResolution.y)
uv.y = (2.0 * fragCoord.y / iResolution.y - 1.0) * 1.0;从而就跟这几行代码等价了。
vec2 uv = fragCoord.xy / iResolution.xy
uv = 2.0 * uv - 1.0;
uv.x *= iResolution.x / iResolution.y;同理也可以分析竖屏时的情况。shadertoy 都是横屏,因而有时会省略 iResolution.x > iResolution.y 这个判断。也很经常写成
vec2 uv = 2.0 * (fragCoord.xy / iResolution.xy) - 1.0;
uv.x *= iResolution.x / iResolution.y;但这样将效果移植到竖屏的手机上,坐标是不对的。
例子
以下这个例子,在画布中央画了一个红色的圆。为简单,画出的圆是有锯齿。
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = (2.0 * fragCoord.xy - iResolution.xy) / min(iResolution.y, iResolution.x);
float len = length(uv);
if (len <= 0.5) {
fragColor = vec4(1.0, 0.0, 0.0, 1.0);
} else {
fragColor = vec4(0.0, 0.0, 0.0, 1.0);
}
}当横屏时(宽大于高),会显示成。

当竖屏时(宽小于高),会显示成。

想画出没有锯齿的圆,可以这样写
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = (2.0 * fragCoord.xy - iResolution.xy) / min(iResolution.y, iResolution.x);
float d = length(uv) - 0.5;
float t = clamp(d * min(iResolution.y,iResolution.x) * 0.25, 0.0, 1.0);
vec4 bg = vec4(0.0, 0.0, 0.0, 1.0);
vec4 circle = vec4(1.0, 0.0, 0.0, 1.0 - t);
fragColor = mix(bg, circle, circle.a);
}实际上参考了这里。A Simple Circle。