Shader实验室:片元绘图

Number of views 266

我们已经介绍了不少的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中对上述线性函数进行图像输出,可得出如下结果:

image.png

为了消除if判断,可写为如下方式:

step(abs(slV), 0.01);

再通过mix函数对两个step结果相乘后混合:

res = mix(res, textColor, str*st);

感兴趣的伙伴可关注如下二维码,随缘,阿弥陀佛:

image.png

0 Answers