代码画一个阿里云吉祥物云小宝

817 阅读4分钟

单位的吉祥物是云小宝。还是挺喜欢这个图像的,家里还有云小宝的抱枕。基于Shader的一些函数,实现了以下的效果

2406161226

shader地址: www.shadertoy.com/view/XXtXRn

SDF构建基本形状

在iconfront找了下SVG,发现是通过三阶贝塞尔曲线画出来的,感觉有点太复杂了。。 不如直接用用几个圆组合组合. 这里的函数在之前绝大部分都推导过了, GPU优化的函数可以在IQ的文章 iquilezles.org/articles/di… 找。 2406160358 这里通过圆角矩形,圆形,胶囊,圆弧四个SDF函数画出脸蛋,头,眼睛和嘴巴

2406162416

代码就是很简单的SDF函数加上一些距离和宽高

float face = sdRoundedBox    (uv - vec2(0.0, 0.0), vec2(1.0, .5), vec4(.5));
float head = sdCircle        (uv - vec2(0.0, 0.15), .65);
float eyer = sdUnevenCapsule (uv - vec2(0.33, 0.0), 0.06, 0.06, 0.15 );
float eyel = sdUnevenCapsule (uv - vec2(-0.33, 0.0), 0.06, 0.06, 0.15 );
float month= sdArc           (rotate(uv, PI), vec2(sin(0.22 * PI), cos(0.22 * PI)), 0.25,  0.04);

Mix函数产生过渡

云小宝本身就是嘴角往上笑的,但是我想如果把眼睛弯弯的应该会更加可爱吧。 这里介绍mix函数,最简单的可以讲一个图像转化成为另外一个图像, 其函数定义为

mix(a, b, h) = h * a + (1 - h) * b

例如我们需要像下面将一个眼睛转化成另外一个眼睛,只需要mix以下两个眼睛的distance

float animate2Eye(float eye1, float eye2, float eye3, float progress) {
    return max(progress, 0.0) < 0.5 ? 
        mix(eye1, eye2, progress * 2.0):
        mix(eye2, eye3, min((progress - .5 ) * 2.0, 1.0));

}

2406162825

鼠标Uniform让眼睛跟随

shaderToy提供了鼠标的位置信息,在shader中称为uniform变量。 通过IMouse.xy获取,同样我们将其转化到[-1, 1]的空间中去

    vec2 mouse = (2.0 * iMouse.xy - iResolution.xy) / iResolution.xy;

让眼睛看向鼠标,其实就是眼睛沿着鼠标的位置挪动一些距离。最简单的做法

    float eyeR = Eye1(uv - vec2(0.33, 0.0) - mouse * .1);
    float eyeL = Eye1(uv - vec2(-0.33, 0.0) - mouse * .1);

2406164021

Fract函数产生重复

一个云小宝太单调了,给他找同类。这里的方法是通过操作坐标系来达到效果,关键是两个函数,fractfloor。 这个技术在之前的tile图像中经常用到, 通过放大然后fract 2406160920 id 是不同格子的坐标,例如我们要让不同云小宝都看向鼠标,就需要id计算格子与鼠标的相对坐标

    
    float scale = 1.717;
    vec2 uv = fract(uv2 * scale);
    vec2 id = floor(uv2 * scale);
    uv = 2. * uv - 1.0;

	mouse =  mouse * scale - id  - uv;

2406161124

Sin函数产生波纹

shadertoy除了提供mouse的坐标uniform之外,还提供了时间的uniform. 通过时间与sin函数的组合,便可以产生波纹的效果。 这里我们将波纹放在云小宝的外面. 首先找到外轮廓,其实很简单就是使用min函数把头发和脸结合起来

        float border = min(face, hair);

通过sin函数构建波纹

if (border > 0. ) {
	col = mix(col, vec3(1.0), sin(border*10.0));
}

2406162831 为sin函数增加偏移,让其运动

col = mix(col, vec3(1.0), sin(border*10.0  - iTime * 5.));

2406163118

1x\frac1{x}函数产生光

2406163219 光有一个现象是,距离光源越远,光强度越低。 可以用一个1x\frac1{x}函数来模拟这个现象,x距离0越近,y值越大, 当然有时候我们要调节一下曲线的陡峭程度来达到合适的效果, 例如下面我使用了0.18x\frac{0.18}{x}

 if (border > 0. ) {
		float d =  sin(border*10.0  - iTime * 2. * PI);
		d = 0.18 / d;
		d = abs(d);
		col = vec3(d);
	}

2406164000

三角函数产生丝滑颜色

如果只是需要一个颜色, 那么我们可以将上面的函数改为

            col = vec3(1.0, 2.0, 3.0) * d;

2406164258

但是如果我们要颜色丰富一些,可以使用IQ这篇博客中iquilezles.org/articles/pa… 介绍的通过三角函数产生美妙的色盘

// cosine based palette, 4 vec3 params
vec3 palette( in float t, in vec3 a, in vec3 b, in vec3 c, in vec3 d )
{
    return a + b*cos( 6.28318*(c*t+d) );
}

为了获取自己喜欢的颜色,也可以到 dev.thi.ng/gradients/ 这个网站去调配颜色, 这里我随便需选了一些颜色

vec3 palette( in float t )
{
    vec3 a = vec3(0.318, 0.848, 0.798);
    vec3 b = vec3(0.500, 0.500, 0.500);
    vec3 c = vec3(1.000, 1.000, 1.000); 
    vec3 d = vec3(0.000, 0.333, -0.362);
    return a + b*cos( 6.28318*(c*t+d) );
}

并且让颜色随着时间沿着distance移动

            col = palette(border + iTime);

2406165202

reference