我们已经介绍了不少的Shader绘图Api,这篇文章就开始用这些Api进行实践,用Shadertoy绘制个蘑菇头,效果与代码如下。
// text color
vec4 textColor = vec4(1.,1.,1.,1.);
// Draw the head
vec4 drawHead(vec2 uv, vec4 color) {
vec4 resCol = color;
vec2 headUv = uv;
vec4 hatCol = vec4(.7,0.1,0.1,1.);
vec4 hatLineCol = vec4(1.);
float headTLineF = abs(pow(headUv.x, 2.)/pow(3.04,2.)+pow((headUv.y-2.125), 2.)/pow(2.14, 2.));
float headTLine = smoothstep(1.0, 0.9, headTLineF);
resCol = mix(resCol, hatLineCol, headTLine);
float hatLerp = smoothstep(1.0, 0.9, abs(pow(headUv.x, 2.)/pow(1.5,2.)+pow(headUv.y-3.25, 2.)));
resCol = mix(resCol, hatCol, hatLerp);
if(2.45<abs(headUv.x) && abs(headUv.x)<=3.1) {
float headLAndR = smoothstep(0.04,0.05,0.56*sqrt((1.-pow((headUv.y-2.13)/1.13, 2.)))+ 2.45 - abs(headUv.x));
resCol = mix(resCol, hatCol, headLAndR);
}
return resCol;
}
// Draw the face
vec4 drawFace(vec2 uv, vec4 color) {
vec4 resColor = color;
vec2 faceUv = uv;
vec4 faceLineCol = vec4(0.0);
vec4 faceFillCol = vec4(1.);
if(faceUv.y >= 0.18) {
float faceTLineF = sqrt(1.-(pow(faceUv.x,2.)/1.62))+0.1 - faceUv.y;
float faceTLine = smoothstep(0.03, 0.02, abs(faceTLineF));
resColor = mix(resColor, faceLineCol, faceTLine);
}
if(faceUv.y<=0.783) {
float faceBLineF = length(pow(faceUv.x, 2.) + pow(faceUv.y-0.1, 2.));
float faceBLine = smoothstep(1.5, 1.49, abs(faceBLineF));
resColor = mix(resColor, faceFillCol, faceBLine);
}
float faceEyeOutF = pow(abs(faceUv.x) - 0.5, 2.)/pow(0.15,2.)+pow(faceUv.y-0.52, 2.)/pow(0.23,2.) - 1.;
float faceEyeOut = smoothstep(0.4, 0.02, abs(faceEyeOutF));
resColor = mix(resColor, faceLineCol, faceEyeOut);
float faceEyeInF = abs(pow(abs(faceUv.x)-0.5, 2.)+pow(faceUv.y-0.52,2.)-1./100.);
float faceEyeIn = smoothstep(0.006, 0.004, abs(faceEyeInF));
resColor = mix(resColor, faceLineCol, faceEyeIn);
if(faceUv.x>= -0.55 && faceUv.x<=0.55) {
float smouthTLineF = abs(-0.625 * sqrt(1.-pow(faceUv.x, 2.))+0.3 - faceUv.y);
float smouthTLine = smoothstep(0.03, 0.02,smouthTLineF);
resColor = mix(resColor, faceLineCol, smouthTLine);
}
if(faceUv.y <= -0.222) {
float smouthBLineF = abs(1.75*pow(faceUv.x,2.)-0.8-faceUv.y);
float smouthBLine = smoothstep(0.03, 0.02, smouthBLineF);
resColor = mix(resColor, faceLineCol, smouthBLine);
}
if(faceUv.y>=-0.73 && faceUv.y<=-0.5) {
float smouthILineF = abs(0.2*sqrt(1.-pow(faceUv.x,2.)/0.04)-0.7-faceUv.y);
float smouthILine = smoothstep(.1, 0.12, smouthILineF);
resColor = mix(resColor, vec4(.7,0.1,0.1,1.), smouthILine);
}
return resColor;
}
void _drawLine(float coord, float s, float t, float c1, float c2, inout vec4 res) {
// The limit is between s and t
float st = step(s, c1) - step(t, c1);
float stV = coord - c2;
float str = step(abs(stV), 0.06);
res = mix(res, textColor,str*st);
}
void _drawSlash(float k, float x, float y, float b, float minV, float maxV, float bold, inout vec4 res) {
// The limit is between minV and maxV
float sl = step(minV, y) - step(maxV, y);
float slV = k * x + b - y;
float slr = step(abs(slV), bold);
res = mix(res, textColor,slr*sl);
}
// draw the text
vec4 drawText(vec2 fragCoord, vec4 color) {
float a = sin(iTime)+4.5;
float b = cos(iTime)+ 11.;
vec4 resColor = color;
vec2 uv = 8.*(2.0 * fragCoord-iResolution.xy)/iResolution.y;
float sl = -0.4 * sqrt(1. - pow(uv.y-7.+b, 2.)/0.2) - 6. - a - uv.x;
float slV = smoothstep(0.2, 0.1, abs(sl));
resColor = mix(resColor, textColor, slV);
float sr = 0.4 * sqrt(1. - pow(uv.y-6.1+b, 2.)/0.2) - 6. - a - uv.x;
float srV = smoothstep(0.2, 0.1, abs(sr));
resColor = mix(resColor, textColor, srV);
_drawLine(7.375-b, -6.28-a, -5.7 - a, uv.x, uv.y, resColor);
_drawLine(5.73-b, -6.3-a, -5.8 - a, uv.x, uv.y, resColor);
_drawLine(-4.58-a, 5.658-b, 7.445 - b, uv.y, uv.x, resColor);
_drawLine(6.56-b, -4.58-a, -3.75 - a, uv.x, uv.y, resColor);
_drawLine(-3.75-a, 5.658-b, 7.445 - b, uv.y, uv.x, resColor);
// A
if(uv.y >= 5.658 - b) {
float at = -2.5*abs(uv.x+2.2+a)+7.42-b-uv.y;
float atV = smoothstep(0.2, 0.1, abs(at));
resColor = mix(resColor, textColor, atV);
}
_drawLine(6.56-b, -2.544-a, -1.856-a, uv.x, uv.y, resColor);
// D
_drawLine(-0.5 - a, 5.76-b, 7.35-b, uv.y, uv.x, resColor);
float dr = sqrt(1.- pow((uv.y-6.56+b), 2.)/0.64)-0.5-a-uv.x;
float drV = smoothstep(0.2, 0.1, abs(dr));
resColor = mix(resColor, textColor, drV);
// E
_drawLine(7.28 - b, 1.5-a, 2.5-a, uv.x, uv.y, resColor);
_drawLine(6.56 - b, 1.5-a, 2.5-a, uv.x, uv.y, resColor);
_drawLine(5.76 - b, 1.5-a, 2.5-a, uv.x, uv.y, resColor);
_drawLine(1.5 - a, 5.76-b, 7.28-b, uv.y, uv.x, resColor);
// R
_drawLine(3.5 - a, 5.65-b, 7.28-b, uv.y, uv.x, resColor);
if(uv.x>=3.5-a) {
float rl = -200.*pow((uv.y-6.8+b), 8.)+4.5-a-uv.x;
float rlv = smoothstep(0.2, 0.1, abs(rl));
resColor = mix(resColor, textColor, rlv);
}
_drawSlash(-1., uv.x+a, uv.y, 10.5-b, 5.65-b, 6.35-b, 0.15, resColor);
// 实
_drawSlash(-2., uv.x-7.+a, uv.y, 7.9-b, 7.9-b, 8.22-b, 0.15, resColor);
_drawSlash(-7., uv.x-6.+a, uv.y, 7.8-b, 7.2-b, 7.7-b, 0.4, resColor);
_drawLine(7.7 - b, 6.-a, 8.-a, uv.x, uv.y, resColor);
_drawSlash(9., uv.x-8.+a, uv.y, 7.7-b, 7.2-b, 7.7-b, 0.4, resColor);
_drawSlash(-1., uv.x-6.5+a, uv.y, 7.2-b, 6.9-b, 7.2-b, 0.1, resColor);
_drawSlash(-1., uv.x-6.4+a, uv.y, 7.-b, 6.7-b, 7.-b, 0.1, resColor);
_drawLine(6.6-b, 6.-a, 8.-a, uv.x, uv.y, resColor);
_drawSlash(2., uv.x-7.+a, uv.y, 6.6-b, 5.65-b, 7.2-b, 0.15, resColor);
_drawSlash(-2., uv.x-7.+a, uv.y, 6.6-b, 5.65-b, 6.6-b, 0.15, resColor);
// 验
_drawLine(7.7 - b, 9.4-a, 10.4-a, uv.x, uv.y, resColor);
_drawLine(10.4 - a, 7.-b, 7.7-b, uv.y, uv.x, resColor);
_drawLine(9.5 - a, 6.6-b, 7.7-b, uv.y, uv.x, resColor);
_drawLine(6.6 - b, 9.5-a, 10.4-a, uv.x, uv.y, resColor);
_drawLine(10.4 - a, 5.7-b, 6.6-b, uv.y, uv.x, resColor);
_drawSlash(-1., uv.x-10.4+a, uv.y, 5.7-b, 5.7-b, 6.-b, 0.1, resColor);
_drawSlash(.2, uv.x-10.4+a, uv.y, 6.3-b, 6.12-b, 6.3-b, 0.03, resColor);
if(uv.y > 7.-b) {
float yr = -1.*abs(1.3*(uv.x-11.5+a))+8.-b-uv.y;
float yrV = smoothstep(0.2, 0.1, abs(yr));
resColor = mix(resColor, textColor, yrV);
}
_drawLine(-2.2+9.-b, 11.-a, 12.-a, uv.x, uv.y, resColor);
_drawSlash(-2., uv.x-11.+a, uv.y, -2.7+9.-b, 6.-b, 6.4-b, 0.1, resColor);
_drawSlash(-2., uv.x-11.5+a, uv.y, -2.7+9.-b, 6.-b, 6.4-b, 0.1, resColor);
_drawSlash(2., uv.x-12.+a, uv.y, -2.7+9.-b, 6.-b, 6.4-b, 0.1, resColor);
_drawLine(5.8 - b, 10.9-a, 12.2-a, uv.x, uv.y, resColor);
// 室
_drawSlash(-2., uv.x-14.7+a, uv.y, 7.9-b, 7.9-b, 8.22-b, 0.1, resColor);
_drawSlash(-7., uv.x-13.5+a, uv.y, 7.8-b, 7.2-b, 7.7-b, 0.4, resColor);
_drawLine(7.7 - b, 13.5-a, 15.5-a, uv.x, uv.y, resColor);
_drawSlash(9., uv.x-15.5+a, uv.y, 7.7-b, 7.2-b, 7.7-b, 0.4, resColor);
_drawLine(7.2 - b, 13.9-a, 15.1-a, uv.x, uv.y, resColor);
_drawSlash(1.8, uv.x-14.3+a, uv.y, 7.2-b, 6.7-b, 7.2-b, 0.2, resColor);
_drawLine(6.7 - b, 14.-a, 15.-a, uv.x, uv.y, resColor);
_drawSlash(1.8, uv.x-14.3+a, uv.y, 7.2-b, 6.7-b, 7.2-b, 0.2, resColor);
_drawSlash(-1., uv.x-15.+a, uv.y, 6.7-b, 6.6-b, 6.9-b, 0.1, resColor);
_drawLine(6.22 - b, 14.-a, 15.-a, uv.x, uv.y, resColor);
_drawLine(5.8 - b, 13.8-a, 15.3-a, uv.x, uv.y, resColor);
_drawLine(14.5 - a, 5.8-b, 6.5-b, uv.y, uv.x, resColor);
return resColor;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
// Normalized pixel coordinates
vec2 uv = 5.*(2.0 * fragCoord-iResolution.xy)/iResolution.y;
vec4 col = vec4(0.,0.,0.,0.);
textColor = vec4(abs(sin(iTime)),abs(cos(iTime)+sin(iTime)),abs(cos(iTime)),1.);
vec4 res = drawHead(uv, col);
res = drawFace(uv, res);
res = drawText(fragCoord, res);
// Output to screen
fragColor = res;
}
在绘制文字的功能时,主要用到直线与斜线的绘制,这里主要解析下这两个函数功能的原理与作用。
在smoothstep函数中我们已经对两个smoothstep函数相减操作进行了讲解,同样的,两个step函数相减操作也具备同样的效果,如:
// 当y<-1时值为0,
// 当-1<=y<=1时值为1,
// 当1<y时值为0
float v = step(-1., y) - step(1., y);
接下来就要讲解下如何把线性函数转为线段,并且线段可设置相应宽度,这个思想贯穿着整个绘制的始终,如:
y = 0.5 * x
已知 0.5 * x = y, 那么 0.5 * x - y = 0;如果把等式右边看作线性函数所定义的线段宽度,那么0肯定是看不到线段的,并且在等式左边计算过程中肯定有正负值的结果输出,而线段宽度肯定是非负值,所以就可以把线性函数转为如下方式:
// 此时线性方程0.5 * x-y所形成的线宽为0.1
if(abs(0.5*x-y) <= .01) {
outputCol = vec4(1.);
}
可以试着在Shadertoy中对上述线性函数进行图像输出,可得出如下结果:
为了消除if判断,可写为如下方式:
step(abs(slV), 0.01);
再通过mix函数对两个step结果相乘后混合:
res = mix(res, textColor, str*st);
感兴趣的伙伴可关注如下二维码,随缘,阿弥陀佛: