欢迎光临散文网 会员登陆 & 注册

拆一下漫画风Shader的流程

2023-01-06 19:48 作者:Nevrwind  | 我要投稿

本来想录个视频的,但是录完才发现没录声音,遂作罢,把自己公众号的文章搬过来发了。

图片


图片


这玩意的使用价值目前不是特别大,因为摩尔纹特别重,回头加个LOD会好很多。

拆效果 |

按例先拆一手效果,看看漫画长啥样。这边选的是JOJO的漫画,因为jojo里面光影的刻画比较细节,不同的光影有不同的处理手法,有些漫画阴影都是网点纸处理,可能细节就不太够。

图片
下一秒就是刀了


可以基本拆出来就是几个部分:

1. 光到暗的部分,光的部分是纯白,浅色阴影的部分是浅色的网点纸过渡

2. 深色阴影是单排斜线或着双排斜线,最黑的地方是纯黑

3. 本色纹理部分,深色衣服以网点纸打底,褶皱和更深的部分是纯黑色

4. 阴影部分,以阴影轮廓线勾勒,以网点纸或者斜线涂色

拆出来效果之后就可以着手制作了。


原理 |

本质就是在屏幕UV上玩花样而已,没有太多新的东西。屏幕UV的部分可以参考庄佬的1。在我们获取屏幕UV之后,就可以着手进行网点纸和斜线的制作了,做法有两种:程序化生成或者用纹理,我这里是用程序化的方法做的,只能说吃力不讨好,理论上拿纹理做会简单很多,也省内存,也不用写很多代码,你说我这图啥呢。

网点纸的做法:

取得了屏幕UV之后,因为UV都是连续的数,所以只需要对屏幕UV取个小数,就可以取得以下的效果:

图片

有这玩意就简单了,对每个像素取一下到中心的距离再step一手:

fixed spot = smoothstep(spotScale-0.1, spotScale+0.1,distance(fixed2(0.5,0.5), fragCoord));

具体原理就是,到顶点的固定距离是一个圆,没step之前在屏幕上是这个样子的:

图片

取完Step就可以得到最基本的网点图了,而这个step的数值就可以作为网点的大小取值:

图片

如果用lambert作为step值,就能得到一个这样的图,这个就可以作为网点纸的基础了:

图片


//波点部分

half2 fragCoord = scrUV * originDist * 0.001 * _SpotDensity; 

fragCoord.x = frac(fragCoord.x);

fragCoord.y = frac(fragCoord.y);fixed spot = smoothstep(spotScale-0.1, spotScale+0.1,distance(fixed2(0.5,0.5), fragCoord));spot *= _ToonScale;


斜排线的做法:

和网点纸的思路差不多,不过斜排线需要一个角度去控制排线的方向,角度的思路就是我在屏幕上随便定一个向量,然后求屏幕上UV到这个向量的投影距离,之后取个小数,就可以取到斜排的0到1的取值了,之后对这个取值step就可以得到排线了,同样的,这个step的值也可以作为粗细来使用。

图片

half2 dir2D = half2(cos(radians(_Angle)),sin(radians(_Angle))); float dist = dot(dir2D, fragCoord);                            

图片
取余的结果
图片
Step的结果


(其实这些一张图就解决了,何必呢)

和得到网点纸不同,斜排线我们还需要添加一些手绘的效果,比如手绘的线条会有一定的扭曲,断开,排线会不整齐,同时会有细小的碎线来表示粗糙的部分等等。先放一张没有添加效果的纯排线:

图片


手绘线条的扭曲,断开和粗细变化:这个很简单,用一张噪声去干扰角度,最终颜色和粗细的取值就可以了,其实也看不出来,所以写成了#if,不需要的时候可以不勾选:

//线的扭曲

float2 toonDistortUV = fragCoord.xy *_DistortScale * 0.03;

fixed DistortionL = saturate(tex2D(_ToonNoiseTex, toonDistortUV).r);

fixed DistortionS = saturate(tex2D(_ToonNoiseTex, toonDistortUV*6).g);

DistortionL = Remap(0,1,-0.5,0.5, DistortionL);

DistortionS = Remap(0,1,-0.5,0.5, DistortionS);

fixed Distortion = DistortionL * 4 + DistortionS;

_Angle -= Distortion * _DistortionIntensity;

图片


//线的断开float2 toonBreakUV = fragCoord.xy * _Density * 0.07;

fixed BreakTex = saturate(tex2D(_ToonNoiseTex, toonBreakUV).r);

Break = step(_BreakAmount, BreakTex);

图片


//粗细随机

float2 thicknessRUV = fragCoord.xy * 0.8;

Thicken = tex2D(_ToonNoiseTex, thicknessRUV).r;

Thicken = Remap(0,1,-0.1,0.1,Thicken);

Thicken = lerp(0.0,Thicken,_ThicknessRIntensity);

图片

手绘线条的排线随机

这个需要在光影上做手脚,把兰伯特的光照部分和噪声图除一下,就可以得到一张这样的mask:

图片

这个边缘就有很好的随机性,别问我为什么是除以,拿DS一个个试试出来的。

用这个mask去叠排线,就可以得到这样的效果:



细小的碎线:这里需要一张cell噪声图,随便在DS做一张:

图片

然后用这张图去干扰角度值,来得到不同方向的排线:

图片

最后用噪声贴图去mask一下就行了,不是特别的复杂:

图片


最后得到的排线Shader的效果:

叠加上网点纸就能得到一个基本的漫画Shader了:



阴影的做法:阴影的原理就是先通过SHADOW_ATTENUATION(i)取得最基础的阴影范围,之后就可以用两个step卡出阴影线然后给一个粗细随机:


然后可以根据阴影的范围给一层网点纸,就能得到阴影了。

texture的部分是基于原有的texture,把荒木线提出来之后,对衣服的部分用step卡出阴影给纯黑,其余部分给一个网点就行了。


综合一下以上的所有乱七八糟的效果,就能得到最终的效果了:

再下个街道模型,给上shader,给个后处理的速度线:

承太郎可tm太帅了。



拆一下漫画风Shader的流程的评论 (共 条)

分享到微博请遵守国家法律