用 Shader 繪制一只可愛的 “小撓斧” 【GLSL】
完整效果圖
設(shè)計(jì)思路
我設(shè)計(jì)的這只 “小撓斧” 的造型十分簡單,只用到了三種基本圖形
圓形
(圓頭)弧線
(圓頭)直線
那么在 shader 當(dāng)中我們?cè)撊绾卫L制這三種基本圖形呢?答案是通過 SDF !【想進(jìn)一步了解的同學(xué)可以搜索我的相關(guān)專欄文章 ShaderJoy 】
限于篇幅,我這里就直接列出了本文所用到的三種 SDF 的函數(shù)
sdCircle
udSegment
sdArc
sdCircle 的使用
sdCircle 具體代碼分別如下所示
/// @note 圓形
float sdCircle( in vec2 p, in float r )
{
return length(p) - r;
}
通過圓形我們可以首先繪制出兩只耳朵(外耳郭)的基本造型
然后同樣,繪制出內(nèi)耳的造型
進(jìn)而繪制出 大臉盤子
以及眼睛和嘴
udSegment 的使用
udSegment 具體代碼分別如下所示
/// @note (圓頭)直線形
/// @param a 起點(diǎn)
/// @param b 終點(diǎn)
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);
}
通過它,我們可以給 “小撓斧” 繪制一個(gè)鼻子
是不是已經(jīng)有點(diǎn)可可愛愛了~ 但是這樣還是不行的,因?yàn)榭雌饋碛悬c(diǎn)像小熊。。。畢竟是 “小撓斧” ,應(yīng)該給它添加一個(gè) 王 的印記!那就要配合使用我們下一個(gè) SDF 函數(shù)了。
sdArc 的使用
sdArc 具體代碼分別如下所示
/// @note (圓頭)弧形
/// @param sc 角度、弧長
/// @param ra 弧長
/// @param rb 粗細(xì)
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;
}
因?yàn)?“王” 的結(jié)構(gòu)是 三橫一豎,(三橫其實(shí)是略帶彎曲的),所以我決定用 sdArc 來繪制那 “三橫”,udSegment 來繪制那 “一豎” 。
感覺還差點(diǎn)意思,豐富一下它的條紋
以及再給它添加一對(duì)靈性的小胡紙
大功就完成了 !
總結(jié)
有了這三種基本圖形的繪制之后,其實(shí)就只需把它們合理地拼接在一起的工作了,但其實(shí)也是最需要耐心的工作,因?yàn)橛性S多細(xì)節(jié)參數(shù)需要調(diào)教。。。啊呸!是調(diào)校!
最后,完整代碼如下所示
完整代碼
// 版權(quán)聲明:轉(zhuǎn)載請(qǐng)附上原文出處及鏈接。
#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
const float AA = 0.007;
/// ---------------- SDF ----------------
/// @note 圓形
float sdCircle( in vec2 p, in float r )
{
return length(p) - r;
}
/// @note (圓頭)弧形
/// @param sc 角度、弧長
/// @param ra 弧長
/// @param rb 粗細(xì)
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 起點(diǎn)
/// @param b 終點(diǎn)
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 關(guān)于 (0, 0) 點(diǎn)的旋轉(zhuǎn)
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)) );
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
/// @note 將屏幕坐標(biāo)轉(zhuǎn)為 [-1., 1.]
/// 以屏幕中心為原點(diǎn)
vec2 uv = (2.0 * fragCoord - iResolution.xy) / iResolution.y;
/// @note 構(gòu)建各個(gè)組件的 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);
/// 靜態(tài)版
float leftBeard = udSegment( uv, vec2(-.4, -0.77), vec2(-.618, -.618)) - .008;
col = mix(BLACK, col, smoothstep(0., .01, leftBeard));
/// 靜態(tài)版
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);
}
原文鏈接: https://juejin.cn/post/7056666793003188261
-- END --
進(jìn)技術(shù)交流群,掃碼添加我的微信:Byte-Flow
獲取相關(guān)資料和源碼
推薦:
全網(wǎng)最全的 Android 音視頻和 OpenGL ES 干貨,都在這了
面試官:如何利用 Shader 實(shí)現(xiàn) RGBA 到 NV21 圖像格式轉(zhuǎn)換?
