ShaderJoy —— 用 Shader 绘制一只可爱的 “小挠斧” 【GLSL】
  7VNj03Sk0rti 2023年11月02日 76 0

ShaderJoy —— Shader 特效乐趣无穷

完整效果图

tiger.gif

设计思路

我设计的这只 “小挠斧” 的造型十分简单,只用到了三种基本图形

  • 圆形
  • (圆头)弧线
  • (圆头)直线

那么在 shader 当中我们该如何绘制这三种基本图形呢? 答案是通过 SDF !【想进一步了解的同学可以搜索我的相关专栏文章 ShaderJoy

限于篇幅,我这里就直接列出了本文所用到的三种 SDF 的函数

  • sdCircle
  • udSegment
  • sdArc

sdCircle 的使用

sdCircle 具体代码分别如下所示

/// @note 圆形
float sdCircle( in vec2 p, in float r )
{
    return length(p) - r;
}

通过圆形我们可以首先绘制出两只耳朵(外耳郭)的基本造型

shadertoy.png

然后同样,绘制出内耳的造型

shadertoy.png

进而绘制出 大脸盘子

shadertoy.png

以及眼睛

shadertoy.png

udSegment 的使用

udSegment 具体代码分别如下所示

/// @note (圆头)直线形
/// @param a 起点
/// @param b 终点
float udSegment( in vec2 p, in vec2 a, in vec2 b )
{
    vec2 ba = b - a;
    vec2 pa = p - a;
    float h = clamp( dot(pa, ba) / dot(ba, ba), 0.0, 1.0 );
    return length(pa - h * ba);
}

通过它,我们可以给 “小挠斧” 绘制一个鼻子

shadertoy.png

是不是已经有点可可爱爱了~ 但是这样还是不行的,因为看起来有点像小熊。。。 毕竟是 “小挠斧” ,应该给它添加一个 的印记! 那就要配合使用我们下一个 SDF 函数了。

sdArc 的使用

sdArc 具体代码分别如下所示

/// @note (圆头)弧形
/// @param sc 角度、弧长
/// @param ra 弧长
/// @param rb 粗细
float sdArc( in vec2 p, in vec2 sc, in float ra, float rb )
{
    // sc is the sin/cos of the arc's aperture
    p.x = abs(p.x);
    return ((sc.y * p.x > sc.x * p.y) ? length(p - sc * ra) :
            abs(length(p) - ra)) - rb;
}

因为 “王” 的结构是 三横一竖,(三横其实是略带弯曲的),所以我决定用 sdArc 来绘制那 “三横”,udSegment 来绘制那 “一竖” 。

shadertoy.png

感觉还差点意思,丰富一下它的条纹

shadertoy.png

以及再给它添加一对灵性的小胡纸

tiger.gif

大功就完成了 !

总结

有了这三种基本图形的绘制之后,其实就只需把它们合理地拼接在一起的工作了,但其实也是最需要耐心的工作,因为有许多细节参数需要调教。。。啊呸!是调校!

最后,完整代码如下所示

完整代码


// 版权声明:转载请附上原文出处及链接。

#define YELLOW vec3(255., 214., 34.)/255.
#define ORANGE vec3(255., 80., 2.)/255.
#define BLACK vec3(29., 21., 22.)/255.
#define WHITE vec3(1.)
#define Pi 3.14159

/// ---------------- SDF ----------------

/// @note 圆形
float sdCircle( in vec2 p, in float r )
{
    return length(p) - r;
}

/// @note (圆头)弧形
/// @param sc 角度、弧长
/// @param ra 弧长
/// @param rb 粗细
float sdArc( in vec2 p, in vec2 sc, in float ra, float rb )
{
    // sc is the sin/cos of the arc's aperture
    p.x = abs(p.x);
    return ((sc.y * p.x > sc.x * p.y) ? length(p - sc * ra) :
            abs(length(p) - ra)) - rb;
}

/// @note (圆头)直线形
/// @param a 起点
/// @param b 终点
float udSegment( in vec2 p, in vec2 a, in vec2 b )
{
    vec2 ba = b - a;
    vec2 pa = p - a;
    float h = clamp( dot(pa, ba) / dot(ba, ba), 0.0, 1.0 );
    return length(pa - h * ba);
}

/// ---------------- SDF ----------------

/// @note 关于 (0, 0) 点的旋转
vec2 rot(vec2 uv, float a)
{
    // [uv.x uv.y] * [cos(a),  sin(a),
    //                -sin(a), cos(a)]
    return vec2(uv.x * cos(a) - uv.y * sin(a), uv.y * cos(a) + uv.x * sin(a));
}

vec2 rotCenter(vec2 uv, float a, vec2 center)
{
    return rot(uv - center, a) + center;
}

/// @note 融合
float smin( float d1, float d2, float k )
{
    float h = clamp( 0.5 + 0.5 * (d2 - d1) / k, 0.0, 1.0 );
    return mix( d2, d1, h ) - k * h * (1.0 - h);
}

void drawStripe(vec2 uv, vec2 offset, float tb, float ra, float rb, inout vec3 col)
{
    float arc_t = sdArc(uv - offset,                 vec2(sin(tb), cos(tb)), ra, rb);
    float arc_m = sdArc(uv - offset + vec2(.0, .13), vec2(sin(tb), cos(tb)), ra, rb);
    float arc_b = sdArc(uv - offset + vec2(.0, .25), vec2(sin(tb), cos(tb)), ra, rb);

    float arc_3 = min(1., min(sign(arc_t), min(sign(arc_m), sign(arc_b))));
    col = mix(BLACK, col, arc_3);
    col = mix( col, BLACK, 1.0 - smoothstep(0.0, AA, abs(arc_t)) ); ///< 边缘平滑 抗锯齿
    col = mix( col, BLACK, 1.0 - smoothstep(0.0, AA, abs(arc_m)) );
    col = mix( col, BLACK, 1.0 - smoothstep(0.0, AA, abs(arc_b)) );
}

const float AA = 0.007;
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    /// @note 将屏幕坐标转为 [-1., 1.]
    /// 以屏幕中心为原点
    vec2 uv = (2.0 * fragCoord - iResolution.xy) / iResolution.y;

    /// @note 构建各个组件的 SDF
    /// @note 脸
    float faceSDF = sdCircle(uv - vec2(0., -.83), 1.02);

    /// @note 耳朵
    float leftOutterEarSDF = sdCircle(uv - vec2(-.61, -.02), .31);
    float rightOutterEarSDF = sdCircle(uv - vec2(+.61, -.02), .31);
    float leftInnerEarSDF = sdCircle(uv - vec2(-.61, -.02), .15);
    float rightInnerEarSDF = sdCircle(uv - vec2(+.61, -.02), .15);

    /// @note 眼睛
    float leftEyeSDF = sdCircle(uv - vec2(-.33, -.61), .06);
    float rightEyeSDF = sdCircle(uv - vec2(+.33, -.61), .06);

    /// @note 嘴
    float leftMouthSDF = sdCircle(uv - vec2(-.21, -1.), .291);
    float rightMouthSDF = sdCircle(uv - vec2(+.21, -1.), .291);

    // 绘制
    vec3 col = WHITE; ///< 背景

    /// @note 耳朵
    col = mix(YELLOW, col, smoothstep(0., .01, leftOutterEarSDF));
    col = mix(YELLOW, col, smoothstep(0., .01, rightOutterEarSDF));
    col = mix(ORANGE, col, smoothstep(0., .01, leftInnerEarSDF));
    col = mix(ORANGE, col, smoothstep(0., .01, rightInnerEarSDF));

    /// @note 脸
    col = mix(YELLOW, col, smoothstep(0., .01, faceSDF));

    /// @note 眼睛
    col = mix(BLACK, col, smoothstep(0., .01, leftEyeSDF));
    col = mix(BLACK, col, smoothstep(0., .01, rightEyeSDF));

    /// @note 嘴
    float mouth = smin(leftMouthSDF, rightMouthSDF, 0.05);
    col = mix(WHITE, col, smoothstep(0., .01, mouth));

    /// @note 鼻子
    float nose = udSegment( uv, vec2(-.038, -0.73), vec2(.038, -.73) ) - .085;
    col = mix(BLACK, col, smoothstep(0., .01, nose));

    /// @note 王
    float d = udSegment( uv, vec2(0., -0.25), vec2(.0, -.06) ) - .03; ///< |
    col = mix( col, BLACK, 1.0 - smoothstep(0.0, AA, abs(d)) );

    float tb = Pi / 12.;
    float arc_t = sdArc(uv - vec2(0., -0.9), vec2(sin(tb), cos(tb)), .84, 0.03); ///< 三
    float arc_m = sdArc(uv - vec2(0., -1.0), vec2(sin(tb), cos(tb)), .84, 0.03);
    float arc_b = sdArc(uv - vec2(0., -1.1), vec2(sin(tb), cos(tb)), .84, 0.03);

    float wang = min(1., min(sign(d), min(sign(arc_t), min(sign(arc_m), sign(arc_b)))));
    col = mix(BLACK, col, wang);

    col = mix( col, BLACK, 1.0 - smoothstep(0.0, AA, abs(arc_t)) ); ///< 边缘平滑 抗锯齿
    col = mix( col, BLACK, 1.0 - smoothstep(0.0, AA, abs(arc_m)) );
    col = mix( col, BLACK, 1.0 - smoothstep(0.0, AA, abs(arc_b)) );

    /// @note 花纹
    float ttb = Pi / 20.;
    drawStripe(uv, vec2(-.82, -1.52), ttb, .84, 0.035, col);
    drawStripe(uv, vec2(.82, -1.52), ttb, .84, 0.035, col);

    /// @note 胡子
    float angle = Pi / 6.*(.5 * sin(iTime * 3.) + .5);

    /// 静态版                           
    float leftBeard = udSegment( uv, vec2(-.4, -0.77), vec2(-.618, -.618)) - .008;
    col = mix(BLACK, col, smoothstep(0., .01, leftBeard));
    
    /// 静态版  
    float rightBeard = udSegment( uv, vec2(.4, -0.77), vec2(.618, -.618) ) - .008;
    col = mix(BLACK, col, smoothstep(0., .01, rightBeard));

    fragColor = vec4(col, 1.0);
}

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论