当前位置:   article > 正文

绒毛/短毛渲染_骨骼网格体短毛渲染

骨骼网格体短毛渲染

refer:
腾讯游戏学堂
主要来自这里,感谢讲解!

  • 首先说一点,很多文章都把毛发和头发弄混,根本就不是一回事好吧,为了区分,我的两篇文章分别用了长毛短毛来区分

多层毛发模型

1

  • 关于这个模型,大部分文章都会引用这张图,图很好,但是原论文中单独的一张图放到这里,完全没有语境,让人看着非常费解,并且和Unity的实现有一点区别;
  • 这里我把原图的注释翻译并且重写了,希望能帮助你理解这个模型:

当然直接一张图是看不明白的,这里我来用通俗的话来讲明白这个多层毛发模型


2

首先,真实世界的毛发是这样的:

  1. 圆柱形,越靠近末端越细,直到尖尖
  2. 透光,越细越透光(不是因为其有间隙才透光,而是毛发本身就是半透明的)
  3. 虽然透光,但是不是完全透光,因此毛发的根部收到的光照更少

也就是如下这样:
在这里插入图片描述

现实中的毛发就是这样,但是我们不可能去真的一根根建模
于是讨巧的模拟毛发的算法便被提出:
纹理很好用,我们想把所有的毛发信息记录在纹理中,但是纹理只是2维的,如何记录三维的毛发信息呢?
不卖关子,做法就是用很多层纹理叠起来,把毛发分段记录下来:
在这里插入图片描述

  • 只不过这毛发不是实体的,而是分层的面片,侧面看会露馅,不过好解决,只要层数足够多,就可以掩盖这一点
    在这里插入图片描述

  • 将没有毛发的空袭部分alpha值记为0,就像是用透明度这个工具一点点雕刻,这样便可以雕刻出毛皮的样子

  • 至于具体怎么去雕刻毛发,去看下面的代码实现

  • 另外前面还有说到毛发是会透光的,那么我们在渲染的时候就可以修改其透明度,越靠近末梢越透明

  • 我还说到毛发根部受到的光更少,这个现象我们用环境光遮蔽来实现



3

这样一来,便将渲染毛发这种听起来不可思议的事情,转化成了渲染一堆叠起来的纹理了,这听起来就实际多了,不过这仍然会消耗大量的算力

尽管开销很大,但这也是当前渲染毛发最好的方法了(实时),别的开销更大
感兴趣可以看一下这位UP的案例,个人觉得超棒

因为开销很大,所有在性能调优方面就极为重要

  • 王者荣耀中妲己的角色展示界面,其尾巴就是用的这个模型,而其在绝大部分移动平台上都可以流程运行,效果还很棒,说明优化很到位
  • 如果每一层都调用一个pass去绘制的话太浪费了,应该使用实例化(GPU instance)来减少调用,实例化可以看learnopengl,讲的很好
  • 通过改变毛发的形状,可以在较少的层级下接近更多层级的效果:
  • 环境光遮蔽,添加与否的差别,个人觉得差距还挺大的

                 


代码实现

先放出本代码的最终效果

微观细节:

  • 可以看到是一片一片的组成的毛发
    在这里插入图片描述

一点补充说明:

  • 我用了很笨的方法,一个Pass挤出一层,所以我调用了非常多的Pass,性能开销很大,应该用实例化,但是还没学到,学到了来补
  • 由于大量的重复Pass,因此将着色器的主体写在了cginc中,调用即可
  • 另外上图是没有进行任何光照运算的结果(除了AO),本人在尝试用phong进行光照时,效果非常奇怪,推测是多层透明的影响,毕竟本文是讲毛发的,光照不再讨论了,这部分我以后再尝试不同的光照模型(主要试试各向异性的模型,如kajiya)




cginc

就不逐行写注释了,主要的要点如下:

  • v2f vert_fur(appdata v, float layer_offset)
    注意这个,cginc中的vert和frag可以传入参数,这也就是毛发模型能够在每一层细微调整的关键(每个pass的参数不一样,直接properties导入太多太复杂)
  • alpha = step(layer_offset, alpha);
    这个是雕刻毛发的关键,step函数是:step(a,x); x<a 返回0 x>=0 返回1;而step中的alpha是从噪声图中读取的
  • alpha *= (1-layer_offset);
    逐层进行透明度衰减
  • col.xyz *= pow(layer_offset, _AO ); 进行环境光遮蔽,AO具体可以看我的这篇文章(还没写,新坑)
#ifndef FUR_INCLUDE
#define FUR_INCLUDE

#include "UnityCG.cginc"
#include "Lighting.cginc"

struct appdata
{
    float3 normal : NORMAL;
    float4 vertex : POSITION;
    float2 uv : TEXCOORD0;
};

struct v2f
{
    float2 uv : TEXCOORD0;
    float2 uv_layer : TEXCOORD1;
    float4 vertex : SV_POSITION;
};

float _Length;
sampler2D _MainTex;
sampler2D _LayerMap;
float4 _MainTex_ST;
float4 _LayerMap_ST;
float _AO;

v2f vert_fur(appdata v, float layer_offset)
{
    v2f o;
    v.vertex.xyz += v.normal * _Length * layer_offset;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    o.uv_layer = TRANSFORM_TEX(v.uv, _LayerMap);

    return o;
}

fixed4 frag_fur(v2f i, float layer_offset) 
{
    float alpha = tex2D(_LayerMap, i.uv_layer).r;//读取layer纹理
    
    alpha = step(layer_offset, alpha); //雕刻毛发
    alpha *= 1-layer_offset; //透明度衰减计算
    fixed4 col = fixed4(tex2D(_MainTex, i.uv).rgb, alpha);//应用上述得到的透明度
    
    col.xyz *= pow(layer_offset, _AO ); //AO计算

    return col;
}

#endif

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53




shader

Shader "Unlit/fur"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _LayerMap ("Layer map", 2D) = "white"{}
        _Length ("fur length", range(0,1)) = 0.5
        _AO ("AO", range(0,1)) = 0.5
    }
    SubShader
    {
        Tags { "RenderType"="Transparent" "Queue" = "Transparent"}
        Blend SrcAlpha OneMinusSrcAlpha


        Cull off



        Pass{
            CGPROGRAM
            #pragma vertex vert0
            #pragma fragment frag0
            #include "layers.cginc"

            v2f vert0(appdata v){return vert_fur(v,0);}
            fixed4 frag0(v2f i):SV_TARGET{return frag_fur(i,0);}

            ENDCG
        }
        Pass{
            CGPROGRAM
            #pragma vertex vert1
            #pragma fragment frag1
            #include "layers.cginc"

            v2f vert1(appdata v){return vert_fur(v,0.01);}
            fixed4 frag1(v2f i):SV_TARGET{return frag_fur(i,0.01);}

            ENDCG
        }
        Pass{
            CGPROGRAM
            #pragma vertex vert2
            #pragma fragment frag2
            #include "layers.cginc"

            v2f vert2(appdata v){return vert_fur(v,0.02);}
            fixed4 frag2(v2f i):SV_TARGET{return frag_fur(i,0.02);}

            ENDCG
        }
        Pass{
            CGPROGRAM
            #pragma vertex vert3
            #pragma fragment frag3
            #include "layers.cginc"

            v2f vert3(appdata v){return vert_fur(v,0.03);}
            fixed4 frag3(v2f i):SV_TARGET{return frag_fur(i,0.03);}

            ENDCG
        }
        Pass{
            CGPROGRAM
            #pragma vertex vert4
            #pragma fragment frag4
            #include "layers.cginc"

            v2f vert4(appdata v){return vert_fur(v,0.04);}
            fixed4 frag4(v2f i):SV_TARGET{return frag_fur(i,0.04);}

            ENDCG
        }
        Pass{
            CGPROGRAM
            #pragma vertex vert5
            #pragma fragment frag5
            #include "layers.cginc"

            v2f vert5(appdata v){return vert_fur(v,0.05);}
            fixed4 frag5(v2f i):SV_TARGET{return frag_fur(i,0.05);}

            ENDCG
        }
        Pass{
            CGPROGRAM
            #pragma vertex vert6
            #pragma fragment frag6
            #include "layers.cginc"

            v2f vert6(appdata v){return vert_fur(v,0.06);}
            fixed4 frag6(v2f i):SV_TARGET{return frag_fur(i,0.06);}

            ENDCG
        }
        Pass{
            CGPROGRAM
            #pragma vertex vert7
            #pragma fragment frag7
            #include "layers.cginc"

            v2f vert7(appdata v){return vert_fur(v,0.07);}
            fixed4 frag7(v2f i):SV_TARGET{return frag_fur(i,0.07);}

            ENDCG
        }
        Pass{
            CGPROGRAM
            #pragma vertex vert8
            #pragma fragment frag8
            #include "layers.cginc"

            v2f vert8(appdata v){return vert_fur(v,0.08);}
            fixed4 frag8(v2f i):SV_TARGET{return frag_fur(i,0.08);}

            ENDCG
        }
        Pass{
            CGPROGRAM
            #pragma vertex vert9
            #pragma fragment frag9
            #include "layers.cginc"

            v2f vert9(appdata v){return vert_fur(v,0.09);}
            fixed4 frag9(v2f i):SV_TARGET{return frag_fur(i,0.09);}

            ENDCG
        }
       Pass{
            CGPROGRAM
            #pragma vertex vert10
            #pragma fragment frag10
            #include "layers.cginc"

            v2f vert10(appdata v){return vert_fur(v,0.10);}
            fixed4 frag10(v2f i):SV_TARGET{return frag_fur(i,0.1);}

            ENDCG
        }
        Pass{
            CGPROGRAM
            #pragma vertex vert11
            #pragma fragment frag11
            #include "layers.cginc"

            v2f vert11(appdata v){return vert_fur(v,0.11);}
            fixed4 frag11(v2f i):SV_TARGET{return frag_fur(i,0.11);}

            ENDCG
        }
        Pass{
            CGPROGRAM
            #pragma vertex vert12
            #pragma fragment frag12
            #include "layers.cginc"

            v2f vert12(appdata v){return vert_fur(v,0.12);}
            fixed4 frag12(v2f i):SV_TARGET{return frag_fur(i,0.12);}

            ENDCG
        }
        Pass{
            CGPROGRAM
            #pragma vertex vert13
            #pragma fragment frag13
            #include "layers.cginc"

            v2f vert13(appdata v){return vert_fur(v,0.13);}
            fixed4 frag13(v2f i):SV_TARGET{return frag_fur(i,0.13);}

            ENDCG
        }
        Pass{
            CGPROGRAM
            #pragma vertex vert14
            #pragma fragment frag14
            #include "layers.cginc"

            v2f vert14(appdata v){return vert_fur(v,0.14);}
            fixed4 frag14(v2f i):SV_TARGET{return frag_fur(i,0.14);}

            ENDCG
        }
        Pass{
            CGPROGRAM
            #pragma vertex vert15
            #pragma fragment frag15
            #include "layers.cginc"

            v2f vert15(appdata v){return vert_fur(v,0.15);}
            fixed4 frag15(v2f i):SV_TARGET{return frag_fur(i,0.15);}

            ENDCG
        }
        Pass{
            CGPROGRAM
            #pragma vertex vert16
            #pragma fragment frag16
            #include "layers.cginc"

            v2f vert16(appdata v){return vert_fur(v,0.16);}
            fixed4 frag16(v2f i):SV_TARGET{return frag_fur(i,0.16);}

            ENDCG
        }
        Pass{
            CGPROGRAM
            #pragma vertex vert17
            #pragma fragment frag17
            #include "layers.cginc"

            v2f vert17(appdata v){return vert_fur(v,0.17);}
            fixed4 frag17(v2f i):SV_TARGET{return frag_fur(i,0.17);}

            ENDCG
        }
        Pass{
            CGPROGRAM
            #pragma vertex vert18
            #pragma fragment frag18
            #include "layers.cginc"

            v2f vert18(appdata v){return vert_fur(v,0.18);}
            fixed4 frag18(v2f i):SV_TARGET{return frag_fur(i,0.18);}

            ENDCG
        }
        Pass{
            CGPROGRAM
            #pragma vertex vert19
            #pragma fragment frag19
            #include "layers.cginc"

            v2f vert19(appdata v){return vert_fur(v,0.19);}
            fixed4 frag19(v2f i):SV_TARGET{return frag_fur(i,0.19);}

            ENDCG
        }
        Pass{
            CGPROGRAM
            #pragma vertex vert20
            #pragma fragment frag20
            #include "layers.cginc"

            v2f vert20(appdata v){return vert_fur(v,0.20);}
            fixed4 frag20(v2f i):SV_TARGET{return frag_fur(i,0.20);}

            ENDCG
        }
        Pass{
            CGPROGRAM
            #pragma vertex vert21
            #pragma fragment frag21
            #include "layers.cginc"

            v2f vert21(appdata v){return vert_fur(v,0.21);}
            fixed4 frag21(v2f i):SV_TARGET{return frag_fur(i,0.21);}

            ENDCG
        }
        Pass{
            CGPROGRAM
            #pragma vertex vert22
            #pragma fragment frag22
            #include "layers.cginc"

            v2f vert22(appdata v){return vert_fur(v,0.22);}
            fixed4 frag22(v2f i):SV_TARGET{return frag_fur(i,0.22);}

            ENDCG
        }
        Pass{
            CGPROGRAM
            #pragma vertex vert23
            #pragma fragment frag23
            #include "layers.cginc"

            v2f vert23(appdata v){return vert_fur(v,0.23);}
            fixed4 frag23(v2f i):SV_TARGET{return frag_fur(i,0.23);}

            ENDCG
        }
        Pass{
            CGPROGRAM
            #pragma vertex vert24
            #pragma fragment frag24
            #include "layers.cginc"

            v2f vert24(appdata v){return vert_fur(v,0.24);}
            fixed4 frag24(v2f i):SV_TARGET{return frag_fur(i,0.24);}

            ENDCG
        }
        Pass{
            CGPROGRAM
            #pragma vertex vert25
            #pragma fragment frag25
            #include "layers.cginc"

            v2f vert25(appdata v){return vert_fur(v,0.25);}
            fixed4 frag25(v2f i):SV_TARGET{return frag_fur(i,0.25);}

            ENDCG
        }
        Pass{
            CGPROGRAM
            #pragma vertex vert26
            #pragma fragment frag26
            #include "layers.cginc"

            v2f vert26(appdata v){return vert_fur(v,0.26);}
            fixed4 frag26(v2f i):SV_TARGET{return frag_fur(i,0.26);}

            ENDCG
        }
        Pass{
            CGPROGRAM
            #pragma vertex vert27
            #pragma fragment frag27
            #include "layers.cginc"

            v2f vert27(appdata v){return vert_fur(v,0.27);}
            fixed4 frag27(v2f i):SV_TARGET{return frag_fur(i,0.27);}

            ENDCG
        }
        Pass{
            CGPROGRAM
            #pragma vertex vert28
            #pragma fragment frag28
            #include "layers.cginc"

            v2f vert28(appdata v){return vert_fur(v,0.28);}
            fixed4 frag28(v2f i):SV_TARGET{return frag_fur(i,0.28);}

            ENDCG
        }
        Pass{
            CGPROGRAM
            #pragma vertex vert29
            #pragma fragment frag29
            #include "layers.cginc"

            v2f vert29(appdata v){return vert_fur(v,0.29);}
            fixed4 frag29(v2f i):SV_TARGET{return frag_fur(i,0.29);}

            ENDCG
        }
        Pass{
            CGPROGRAM
            #pragma vertex vert30
            #pragma fragment frag30
            #include "layers.cginc"

            v2f vert30(appdata v){return vert_fur(v,0.3);}
            fixed4 frag30(v2f i):SV_TARGET{return frag_fur(i,0.3);}

            ENDCG
        }
    }

}


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/108548
推荐阅读
相关标签
  

闽ICP备14008679号