当前位置:   article > 正文

unity shader - 毛发渲染,飘逸的毛发_unity 头发shader

unity 头发shader
渲染原理:
第一步:
分层layer,渲染不同长度的毛发。 层越多,绘制的越细节。
因为毛发有长短,所以,在上层,没有毛发的部分alpha设置为0,不显示
多个layer,可以使用shader等多个pass进行绘制
每一个 Pass 即表示一层。 当渲染每一层时, 使用法线将顶点位置挤出模型表面
第二步:
根据噪音贴图, 在随机毛发的长度。控制alpha的值
层次不同,毛发的uv位置偏移应该也有所不同,这样制作的才能更自然,否则uv一直不变,那么毛发的方向不变就变成了刺猬
毛发不同层次的偏移,也可能收到外力的影响,比如,风等,所以外边可以设置一个偏移量
第三步
处理环境光,反射和漫反射光等影响,设置颜色

实现:

1 多pass渲染。 对于皮毛来说,每一层的渲染方式是一样的,所以采用 在每个pass中引用, 避免重复代码。
不同的层次需要传参数控制表示. 用 【 1/总的层数 】 就是每一层毛所对应的长度
  1. //表面皮肤渲染
  2. Pass
  3. {
  4.     CGPROGRAM
  5.                
  6.     #pragma vertex vert_surface//点着色器 , 对应引用cginc中的方法
  7.     #pragma fragment frag_surface// 片段着色器 , 对应引用cginc中的方法
  8.     #define FURSTEP 0.0 
  9.     #include "FurHelper.cginc" // 引用的shader
  10.                
  11.     ENDCG
  12.                
  13. }
  14. // 毛的渲染
  15. Pass
  16. {
  17.     CGPROGRAM
  18.                
  19.     #pragma vertex vert_base //点着色器 , 对应引用cginc中的方法
  20.     #pragma fragment frag_base // 片段着色器 , 对应引用cginc中的方法
  21.     #define FURSTEP 0.05 // 皮毛的长度
  22.     #include "FurHelper.cginc" // 引用的shader
  23.                
  24.     ENDCG
  25.                
  26. }

2 顶点外扩, 实现多层次偏移
思路:在顶点的法线方向,向外扩展一定的距离。 所以在顶点着色器中处理
  1.             v2f vert(a2v v)
  2.             {
  3.                 v2f o;
  4.                 float3 OffetVertex = v.vertex.xyz + v.normal * _LayerOffset *_FurLength;//顶点外扩
  5.                 OffetVertex += mul(unity_WorldToObject, _FurOffset);//顶点受力偏移【加一个方向上的偏移,看起来更自然,例如向下垂】
  6.             }

3 使用噪音贴图,进行透明度处理
思路: 利用uv2 存储噪音贴图, 然后根据噪音贴图随机性,处理显示毛发的透明度 。 
  1. fixed3 noise = tex2D(_FurTex, i.uv.zw ).rgb; 
  2. fixed alpha = saturate(noise - (_LayerOffset * _LayerOffset + _LayerOffset * _FurDensity));

4 优化处理。提炼参数
    · 毛发长度参数。限制最外层的扩展长度
    · 毛发层数
    · 颜色参数
    · 毛发密集参数(噪音贴图中使用,控制uv的tilling)
    · 添加偏移参数 (例如, 长毛宠物的毛会下垂)
5 *优化处理, 添加毛发移动
使用c#代码控制毛发,缓慢飘逸移动 【lerp】
思路:
shader只渲染一层毛发
c#代码控制,克隆多个游戏对象, 都赋值该毛发的shader,通过改变偏移量,生成多个【需要多少层就是生成多少层】
c#代码中,update中lerp控制每一层毛发缓慢移动。 从而实现飘逸的移动
  1. // 生成, material赋值, shader赋值
  2. void CreateShell()
  3. {
  4.     CheckParent();
  5.     layers = new GameObject[LayerCount];
  6.     float furOffset = 1.0f/ LayerCount;
  7.     for (int i = 0; i< LayerCount; i++)
  8.     {
  9.         //复制渲染的原模型一遍
  10.         GameObject layer = Instantiate(Target.gameObject, Target.transform.position, Target.transform.rotation);
  11.         layer.transform.parent = _parent;
  12.         layer.GetComponent<Renderer>().sharedMaterials = new Material[1];
  13.         layer.GetComponent<Renderer>().sharedMaterial = ResetSharedMaterials(i, furOffset);
  14.         layers[i] = layer;
  15.     }
  16. }
  17. // shader 赋值
  18. Material ResetSharedMaterials(int index, float furOffset)
  19. {
  20.     Material special = new Material(ShellShader);
  21.     if (special != null)
  22.     {
  23.         special.SetTexture("_MainTex", Target.sharedMaterial.GetTexture("_MainTex"));
  24.         special.SetColor("_Color", FurColor);
  25.         special.SetColor("_RootColor", FurRootColor);
  26.         special.SetColor("_RimColor", FurRimColor);
  27.         special.SetColor("_Specular", FurSpecularColor);
  28.         special.SetFloat("_Shininess", Shininess);
  29.         special.SetFloat("_RimPower", RimPower);
  30.         special.SetFloat("_FurShadow", FurShadow);
  31.         special.SetTexture("_FurTex",FurPattern);
  32.         special.SetFloat("_FurLength", FurLength);
  33.         special.SetFloat("_FurDensity", FurDensity);
  34.         special.SetFloat("_FurThinness", FurThinness);
  35.         special.SetFloat("_LayerOffset", index * furOffset);            //不同Shell的偏移参数不一样
  36.         special.SetVector("_FurOffset", FurForce* Mathf.Pow(  index*furOffset,FurTenacity));//计算受力、层数和硬度共同影响的Shell偏移
  37.         special.renderQueue = 3000 + index;//由于在单通道渲染半透明材质,进行了深度写入,为了防止被深度剔除,所以要手动更改渲染队列
  38.     }
  39.     return special;
  40. }
  41. // 移动
  42. void UpdateShellTrans()
  43. {
  44.     if (layers == null || layers.Length == 0)
  45.     {
  46.         return;
  47.     }
  48.     for (int i = 0; i < layers.Length; i++)
  49.     {
  50.         //位置和旋转Lerp到目标模型
  51.         layers[i].gameObject.transform.position = Vector3.Lerp(layers[i].gameObject.transform.position, Target.transform.position, lerpSpeed * Time.deltaTime *20);
  52.         layers[i].gameObject.transform.rotation = Quaternion.Lerp(layers[i].gameObject.transform.rotation, Target.transform.rotation, lerpSpeed * Time.deltaTime *10);
  53.     }
  54. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/108537?site
推荐阅读
相关标签
  

闽ICP备14008679号