找回密码
 立即注册
查看: 248|回复: 0

[Unity/Shader] DNA效果实现

[复制链接]
发表于 2022-8-24 10:55 | 显示全部楼层 |阅读模式
先上效果

防止观众跑掉先上效果,另外温馨提醒大量图片。



最终效果

理论部分

这次的理论部分还是挺基础的,只用了简单的Billboard和序列帧,不过新颖的是这次的使用方式。
序列帧

说到序列帧的话,你不会在想这个效果就是序列图一张一张播放的吧,不会吧,不会吧。



序列图

如上图可知,这是个圆的模糊过程(好像是句废话),但这次并不是想播放这个过程,所以与Time没有任何关系。
都说到这个份上,聪明的你应该已经发现这张图和深度有着莫大的联系了吧。
其实它是更近一步的东西——焦距。
这下你应该就明白了吧,这张序列图是在模拟景深来代替传统后处理的方案来实现。
Billboard

序列帧部分就先说这么多,让我们继续往下看。
想必考虑比较全面的同学应该已经被新的一道坎难住了,
他应该在想如果每个点都是一个面片的话,那要实现效果岂不是需要上百个面片,
那就要考虑性能方面的问题了,要减少DrawCall就得使用GPUInstance,
但这么大量的模型,也势必会占用不少的显存吧。
所以如果我说这百个面片合并成一个模型,是不是就不需要考虑这个方面的问题了,
对的,确实如此,但可想而知事情并没有这么简单。
好了,压力来到了Billboard这边,众所周知,传统的Billboard是对面片来使用的,
这是因为Billboard需要基于模型原点来形成新的空间坐标系,
模型原点只有一个,而每个面片需要的坐标系都不一样。
是不是觉得此路不通了,让我们想想别的办法,
还记得Blender(或者3D Max)中的各自原点么,
是的,如果能得到”各自原点”的话,就能从根本解决我们的问题。
那我们从哪里入手呢,最容易想到当然是顶点啦,毕竟是空间变化嘛,
这次效果比较特殊,用的是最简单的方形面片,所以只需考虑将四个顶点偏移到各自原点的位置即可,
每个顶点到有各自原点的偏移距离都是一样的,现在不知道的只是方向罢了。
而模型中作为方向的依据最佳选择当然是UV啦,
只要简单地减去0.5,就能看到它完美符合我们指向原点的需求(的反方向,加个负号就好)。
half2 offset = (v.uv.xy - 0.5) * _BillboardOffset;
half4 vertex = mul(UNITY_MATRIX_MV, float4(v.vertex.xy - offset, v.vertex.z, 1.0));


随便画来示例一下

得到各自原点后就能按之前一样进行空间变化啦(可能需要自己慢慢试下)
以上,理论部分就结束了,挺简单的不是么。
代码部分

代码方面的话,序列帧和Billboard在《Shader入门精要》都是有完整的模板,这里根据需要在上面上小改下。
序列帧

//vert
VertexPositionInputs vertexPos = GetVertexPositionInputs(half4(0, 0, v.vertex.z, 1));
half depth = vertexPos.positionWS.z - _WorldSpaceCameraPos.z;
o.uv.z = abs(abs(depth) - _TextureXY.z);//Z为焦距

//frag
half tile = saturate(i.uv.z / (max(_TextureXY.w, 0) + 0.001));// W为聚焦程度
tile = floor(tile * (_TextureXY.x * _TextureXY.y - 1));

half row = floor(tile / _TextureXY.x);
half column = tile - row * _TextureXY.x;

half2 uv = i.uv.xy + half2(column, -row - 1);
uv.x /= _TextureXY.x;
uv.y /= _TextureXY.y;

half4 col = 1;
col.a = tex2D(_MainTex, uv).r;在Unity里试摆出这样的几个面片。



xz等差的面片

上个材质,效果如下,焦点处最清晰,靠近或者远离都会模糊透明



基于焦距模拟景深的序列图

当然只要旋转下就发现是个面片了,所以我们需要Billboard。


Billboard

广告牌这块我用的是视空间下的像素偏移去做的(你要是想用冯姐姐那套叉乘去做也可)。
对了,值得一提的是我这里用的是正交相机,如果你用叉乘的话要小改一下,除非你还是用透视的。
o.vertex = mul(UNITY_MATRIX_P,
  mul(UNITY_MATRIX_MV, float4(0.0, 0.0, 0.0, 1.0))
  + float4(v.vertex.x, v.vertex.y, 0.0, 0.0));小改了下之后是这样的,uv - 0.5就是顶点偏移到原点的方向依据,_BillboardOffset是距离。
顶点色后面文章后面再详细说明,_Scale是一些焦距,远近,总体有关的大小控制。
//vert
half2 offset = (v.uv.xy - 0.5) * _BillboardOffset * v.color.r;
half4 vertex = mul(UNITY_MATRIX_MV, float4(v.vertex.xy - offset, v.vertex.z, 1.0));

offset *= max((_Scale1 + (1 - _Scale1) * (abs((depth - _TextureXY.z)))), 0);//焦距大小
offset *= max(_Scale2 + (1 - _Scale2) * depth, 0);//远近大小
offset *= _Scale3;//总体大小

vertex += float4(offset, 0.0, 0.0);
o.vertex = mul(UNITY_MATRIX_P, vertex);把上面用的面片导出到Blender合并一下吧。


再扔到Unity里中把_BillboardOffset调整好,就可以快乐地旋转啦。


模型部分

好了,到这功能上已经基本实现了,让我们整点好玩的吧,先整一截出来,如下图。


还记得上面的顶点色么,没错,这里面需要用到。
因为大的和小的面片顶点到各自原点的距离(也就是_BillboardOffset)不一样。
但其成正比关系,所以这里直接用顶点色的明度表示。


然后复制偏移旋转n次,如下图。


但是这时候合并导入Unity后,你会发现自己的Billboard失效了。


还记得我们的各自原点是怎么来的么,half3(v.vertex.xy - offset,v.vertex.z),
而offset是根据uv-0.5得到,面片被旋转的顶点已经和uv不重叠了,
也就是说vertex-offset,已经不是我们想要的各自原点,所以Billboard条件并不成立。


解决这个问题的办法还是挺多的,我这里还是选择让面片旋转回来,毕竟只用Alt+R很快的。


再次导入Unity中结果就正确了。


到这里模型制作的坑也踩完了,我们接着按上述流程把DNA建出来吧。


导入Unity中,加个好看的背景,再来个z轴自转,就能得到下图的效果了。


工程源码

写在最后

哎,萌心的文笔依旧不好,这篇就这么简单带过吧。
最后来唠嗑点别的吧,像上述咱们用的技术都是非常基础的,但是却有巧妙的用法。
我认为TA不仅仅只是临摹大佬的作品,还应该有自己的创意和想法(又不是莫的感情的机器人)。
做的时候多想为什么,这样不仅能更深刻还说不定能发现些前人漏掉的某些东西。
(保持好奇心的学习才是最有效率的)
陷入了迷茫期的时候,可以看看Free Bird大佬这篇文章。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-9-22 03:30 , Processed in 0.090475 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表