写在前面
若干年前,当妲己的尾巴还没长毛的时候,我还在TA的门外观望,直到有一天,一顿解压不消耗流量的操作之后,我看到了这个摇着毛茸茸尾巴的妲己,我忍不住了,我要尽情吩咐吩咐她!来把你的尾巴取下来给我玩儿玩儿。
关于多pass毛发的实现思路已经有很多大佬在各个平台分享过了,也不在乎我再来一篇,写这篇教程的初衷就是我这个基佬(只会基础的佬)想尽微薄之力丰富一下各位的网络生活,刚刚接触Unity Shader的同学也能够以此为实例来练习一下。
本文中有部分借鉴了梁家斌大佬的文章内容,读者可以先阅读这篇文章来了解毛发的特征及一些制作思路,再来通过这篇文章来实践一下。
先让毛长出来
所谓多pass毛发,如图所示,就是通过多次渲染让毛发从模型表面长出来,长的方向是模型表面法线方向,毛发的粗细就是当前pass中渲染像素的大小,当然每一个pass都是没有厚度的,所以多pass毛发其实都是一层层的“切片”,想要毛发更加真实,自然是需要这些“切片”之间的距离近一些,层数多一些,这必然会导致渲染压力,如何在保证效率的同时实现较好的效果则需要我们在制作过程中做一些尝试。
为了让我们的shader简洁且易于维护,先创建一个FurShaderInclude.cginc来定义结构体,变量和函数。
首先需要在毛发的顶点函数vert_fur中实现一次顶点的挤出。
float3 p = v.vertex + v.normal * _FurLength * FURSTEP;
其中FURSTEP为相对皮肤挤出的距离,_Furlength是控制挤出的系数。
回到我们的shader文件,通过category{}让各个pass来继承相同的渲染状态。
category
{
Tags{"LightMode"="ForwardBase" "RenderType" = "Transparent" "IgnoreProjector" = "True" "Queue" = "Transparent"}
Blend SrcAlpha OneMinusSrcAlpha
SubShader{
pass
{
CGPROGRAM
#pragma vertex vert_fur
#pragma fragment frag_fur
#define FURSTEP 0.05 //定义宏
#include "FurShaderInclude.cginc"
ENDCG
}
pass
{
CGPROGRAM
#pragma vertex vert_fur
#pragma fragment frag_fur
#define FURSTEP 0.1 //逐渐变大
#include "FurShaderInclude.cginc"
ENDCG
}
pass
{
CGPROGRAM
#pragma vertex vert_fur
#pragma fragment frag_fur
#define FURSTEP 0.15
#include "FurShaderInclude.cginc"
ENDCG
}
/*...
重复若干次
...*/
pass
{
CGPROGRAM
#pragma vertex vert_fur
#pragma fragment frag_fur
#define FURSTEP 0.6
#include "FurShaderInclude.cginc"
ENDCG
}
}
}
接下来用SubstanceDesign来制作一张Noise实现“毛孔”,为了让毛发能够由粗变细,Noise的单个元素要有灰度的变化。
在毛发的片元函数frag_fur中,把noise的灰度输出到alpha,并且让alpha值随