前言

玩过ShaderToy的肯定都听过iq这个名字,即使没有听过,肯定也见过他的一些作品,比如随便截两张:

千万不要误会,上图中的两个效果可不是建模渲染的展示,而是由纯GLSL语言(说纯语言不准确,倒是有用到一些纹理图片)生成的哦,并且还是有动态效果的?

有兴趣的可以去他的个人主页观摩下,同时iq也是ShaderToy的创始人,在上面创作了非常多很有启发性的作品。

好了,让我们回到残酷的现实中来,先从iq大神的一篇极其简单的作品开始吧?


核心实现

为了更准确的表达此篇文章的内容,我会在移植到Unity中时做一些删减与修改,以便读者能更好的理解与阅读。

先放上这次的最终效果视频:

视频封面

00:08

UV准备

顶点著色器中与以往一样,没有什么特别的,我们重点来关注下片断著色器。

i.uv 表示的是屏幕上0-1的归一化坐标, ScreenParams.xy表示的是屏幕的长宽像素,而i.uv*ScreenParams即可得到屏幕上的每个像素点的坐标。

所得到值再除以_ScreenParams.x(注意这里是.x),即可得到以宽像素为基准的UV分布图。

细观察得到的uv值,在x的值上是0-1的值,但是在y值上却不是0-1,看效果感觉是0-0.45左右,这就是我们除以_ScreenParams.x得到的效果,目的是为了使画面不变形,以实际像素为基准进行表现。

二维向量p根据式子拆分后可以看出是为了得到以下的动画效果:

其中p.x在后面会表示为方格到细胞格的变化因子。

p.y则表示为清晰到模糊的变化因子。

return iqnoise(24*uv,p.x,p.y);

是我们最终返回的颜色值,其中iqnosie是我们自定义的一个函数,是整个效果的核心实现,代码如下:

按照执行顺序,我们先来分析下iqnoise函数。

float2 p = floor(x);

floor(x),表示对x值进行向下取整。

这里的x值就是frag中调用iqnoise方法的第一个参数24*uv,也就是将现有的uv乘以24倍。

float2 f = frac(x);

frac(x),返回x值的小数部分

对24倍的uv取小数,其实就是将其分成24份独立的小uv块,y轴上由于上面做了特珠处理,所以不会是24份,同时保证了不变形。

方格的清晰与模糊

由于p.x与p.y分别表示了格子的变化与模糊的变化,那么为了更直观的分析,让我们假设p.x=0时,p.y保持原有不变,则效果如下:

视频封面

00:04

iqnoise同步修改后部分代码如下:

k值先忽略,继续往下看。。。

分别定义了va与wt,都初始化为0

然后开始一个5x5的嵌套循环,值域如下:

定义二维向量g,值为当前循环中的坐标点

定义三维向量o,默认初始化为0

o的z值等于hash3(p2+g).z;

也就是当计算当前像素点时,会对此像素周围的24个像素点分别取值采样(是不是感觉和我们平时做图像模糊时用到的卷积演算法很相似?)。

好,现在让我们来看下自定义的随机函数hash3.

随机马赛克

hash3函数,它的主要作用是为了返回随机的方格效果

在之前的案例中我们有知道随机噪波的常用公式为:

frac(sin(dot(i.uv.xy, float2(12.9898, 78.233))) * 43758.5453);

其中dot()的部分在我们这里是用q来表示,q是三维向量,分别代表著RGB三个不同的通道。

以R通道为例,可拆解为:

q.r=frac(sin(dot(p2,float2(127.1,311.7)))*43758.5453);

效果如下:

可以看出其实就是通过p2的UV特性来实现类似这种马赛克的随机效果。

通过在dot中指定不同的数值来实现三个通道不一致的效果,最终RGB效果如下:

不错的彩色马赛克效果,是不是又学会了一招~

不得不说,ShaderToy中处处有惊喜!

其实hash3中我们也可以改成这样的简化版:

不过只有单通道,并且颜色在取值上会偏暗些。

继续循环

好了,hash3了解完毕后,让我们继续回到上面的循环嵌套中

定义二维向量r,值为g-f,并对其做点积运算,同时利用smoothstep来实现柔和过渡效果,而这里的k就是决定模糊程度的。

float k = 1.0+63.0*pow(1.0-p.y,4.0);

可以得出k的取值在(1-64)之间,随著时间变化的曲线如下图所示:

峰谷时为模糊效果,持续一段模糊后会向清晰过渡,然后再过渡回来,就是这样的一个循环。

va与wt就是利用卷积演算法来实现加权平均后最终的像素结果。

由此可见iqnoise中的u与v值不同时可以得到不同的效果,有以下几种情况:

return iqnoise( 24.0*uv, 0,0 );

return iqnoise( 24.0*uv, 0,1 );

return iqnoise( 24.0*uv, 1,0 );

return iqnoise( 24.0*uv, 1,1 );

也就是说如果u值是0-1间的动画,那么结果就是方格到细胞格的过渡动画效果

同样v值如果是0-1的动画,那么结果就是清晰到模糊的过渡动画

同时两者也可以互相组合,最终得出本例的最终效果。


最后

欢迎大家关注更多干货的公众号:Unity技术美术 ( ID:gh_8b69cca044dc )

Unity技术美术QQ交流分享群:19470667

推荐阅读:

相关文章