1、原教学视频链接
2、Shadertoy网址链接
在这个网站上进行像素着色器代码的编辑,有两点需要了解:

首先,我们把屏幕空间的坐标映射到纹理坐标空间[0,1]。可以通过除以视口分辨率来实现
vec2 uv = fragCoord/iResolution.xy;
为了方便,可以选择把坐标系原点(0,0)移动到屏幕正中心
uv -= vec2(0.5, 0.5);
我们的视口一般不会是正方形,比如800x600这种分辨率,因此x轴的像素数量是比y轴要多200个的,而我们的x,y坐标取值范围都在[0,1],这就会导致对每个像素而言,x轴上的长度要比y轴上的更小。所以:在竖轴上,(0,0)到(0,3)有3个像素,而横轴上的(0,0)到(3,0)有4个像素。 在这种情况下直接画圆,得到的会是椭圆,因为最终显示器上的像素都是 正方形的 。竖轴上3个像素,横轴上4个像素,必然得到一个椭圆

因此,需要把纹理空间的像素们也变成正方形的,很明显我们每个像素的水平方向长度是不够的,因此要把纹理坐标的x分量扩大。只需要简单乘以视口宽高比
uv.x *= iResolution.x / iResolution.y;
让圆的边界更平滑,原理就不讲咯,很简单,可以参考这篇文章。我没搜它的源码,但是它的伪代码长下面这样。
它与超级常见的clamp()的唯二区别就是
float smoothstep(float t1, float t2, float t)
{
// 分支中相等的情况没写,无所谓的,放哪个区间都可以
if (t1 < t2)
{
if (t < t1)
return 0;
else if (t > t1 && t < t2)
return f(t);
else if (t > t2)
return 1;
}
if (t1 > t2)
{
if (t < t1)
return 1;
else if (t > t1 && t < t2)
return f(t);
else if (t > t2)
return 0;
}
if (t1 == t2)
{
if (t < t1)
return 0;
else
return 1;
}
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = fragCoord/iResolution.xy;
uv -= 0.5; // 左下角(-0.5,-0.5),右上角(0.5,0.5)
uv.x *= iResolution.x / iResolution.y; // 让像素变成正方形, 区间也变了,懒得算了哈,明显的x轴的上区间上限更大了
float d = length(uv);
float r = 0.3;
if (d > r)
c = 1.0;
else
c = 0.0;
fragColor = vec4(vec3(c), 1.0);
}
结果图,有锯齿

如果采用smoothstep()平滑过度一下
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = fragCoord/iResolution.xy;// [0,1]
uv -= 0.5;// [-0.5,0.5]
uv.x *= iResolution.x / iResolution.y; // 让像素变成正方形
float d = length(uv);
float r = 0.3;
float c = smoothstep(r, r + .2, d);
fragColor = vec4(vec3(c), 1.);
}
是不是很滑

如果更改一下参数,还可以实现更模糊的效果
