1 讲解
首先封装一下上节降到的画圆方法
- 有一点不同之处,在于smoothstep()函数传入的参数为r, r - blur,注意第一个数大于第二个数,这样产生的效果是
- if (d < r - blur) return 1;
- else if (d > r) return 0;
- d位于两者之间,返回0~1之间平滑过渡的数
// 形参(纹理坐标,圆心,半径,边界模糊长度)
float Circle(vec2 uv, vec2 p, float r, float blur)
{
float d = length(uv - p);
float c = smoothstep(r, r - blur, d);
return c;
}
画一个黄色头
mask
(遮罩),很形象的名字。Circle函数
使得屏幕上以(0,0)为圆心向外0.4个坐标单位的像素(此处忽略模糊部分)的mask变量都为1.0,而外面所有像素mask都为0,即mask在圆内为1,圆外为0- 之后用每个像素的col,乘以自身的mask变量的效果就是:
只有mask为1的像素才会正常显示黄色
,mask为0的最终颜色值col为(0,0,0)
// 背景底色
vec3 col = vec3(1.0, 1.0, 0.0);
float mask = Circle(uv, vec2(0.0), 0.4, 0.05);
col *= mask; // 在圆内的像素col * 1为黄色,圆外为col * 0为黑色
fragColor = vec4(vec3(col), 1.0);
画眼睛
- 对mask变量用加减法
- 思路很简单,刚刚我们已经画了一个圆作为头,头范围内的像素mask都为1。我们只需要再画两个眼睛,这两个眼睛内部像素mask也都为1,然后用头-这俩眼睛,即可
float mask = Circle(uv, vec2(0.0) , 0.4, 0.05); // 头
mask -= Circle(uv, vec2(-0.15, 0.1), 0.07, 0.05); // 左眼
mask -= Circle(uv, vec2( 0.15, 0.1), 0.07, 0.05); // 右眼
嘴巴
- 相同的思路,第一个圆画在原点,第二个圆稍微向上偏移0.1个单位,然后用第一个圆 减去 第二个圆,然后就能看到下图所示,嘴巴部分mouth变量为1,可视化一下就是
fragColor = vec4(mouth.xyz, 1.0);
float mouth = Circle(uv, vec2(0.0), 0.3, 0.01);
mouth -= Circle(uv, vec2(0.0, 0.1), 0.3, 0.01);
用之前的结果 减去 嘴巴部分就能得到一个黄色笑脸
mask -= mouth;
再次提醒,笑脸是通过每个像素的mask变量乘以颜色值col实现的。mask就是让屏幕上一部分像素为1,另一部分像素为0,然后对所有像素都用col * mask作为最终颜色。那么就只有mask为1的像素能显示col的颜色, col为0的像素只能显示黑色。
遮罩这个词就是这么来的,相当于给屏幕蒙上一个遮罩,只有这个遮罩内部的的像素可以有颜色,或者其他的操作等等,当然遮罩也不只是区分0,1也可以是其他数。
2 完整代码
float Circle(vec2 uv, vec2 p, float r, float blur)
{
float d = length(uv - p);
float c = smoothstep(r, r - blur, d);
return c;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = fragCoord/iResolution.xy;
uv -= 0.5;
uv.x *= iResolution.x / iResolution.y;
vec3 col = vec3(1.0, 1.0, 0.0);
float mask = Circle(uv, vec2(0.0), 0.4, 0.05);
mask -= Circle(uv, vec2(-0.15, 0.1), 0.07, 0.01);
mask -= Circle(uv, vec2(0.15, 0.1), 0.07, 0.01);
float mouth = Circle(uv, vec2(0.0), 0.3, 0.01);
mouth -= Circle(uv, vec2(0.0, 0.1), 0.3, 0.01);
if (mouth < 0.0) // 嘴巴的两个圆相减,会让头顶往上的部分像素的mouth变量值为-1, 防止bug,把它们置零
mouth = 0.0;
mask -= mouth;
col *= mask;
fragColor = vec4(col, 1.0);
}
效果
最后封装整个笑脸作为一个函数
float Circle(vec2 uv, vec2 p, float r, float blur)
{
float d = length(uv - p);
float c = smoothstep(r, r - blur, d);// 界内返回0,1 ,小于边界返回1, 大于边界返回0
return c;
}
float Smiley(vec2 uv, vec2 p, float size)
{
// 对笑脸做任何移动、缩放、旋转等操作,直接操作uv坐标是最方便快捷的
uv -= p;
uv /= size;
// 头
float mask = Circle(uv, vec2(0.), .4, .01);
// 眼睛
mask -= Circle(uv, vec2(-.15, .1), .07, .01);
mask -= Circle(uv, vec2(.15, .1), .07, .01);
// 嘴巴
float mouth = Circle(uv, vec2(.0), .3, .01);
mouth -= Circle(uv, vec2(.0, .1), .3, .01);
if (mouth < 0.)
mouth = 0.;
mask -= mouth;
return mask;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = fragCoord/iResolution.xy;// [0,1]
uv -= .5;// [-0.5,0.5]
uv.x *= iResolution.x / iResolution.y; // 让像素变成正方形(uv坐标依然是x要多于1的)
vec3 col = vec3(0.);
vec2 pos = vec2(sin(iTime) / 2., sin(2. * iTime) / 4.);
float size = sin(2. *iTime) / 10. + .2;
float mask = Smiley(uv, pos, size);
col = vec3(1., 1., 0.) * mask;
fragColor = vec4(col, 1.);
}