unity shader - fur rendering, flowing fur

Rendering principle:
Step 1:
Layered layer, rendering fur of different lengths. The more layers, the more detail you draw.
Because the hair is long and short, in the upper layer, the alpha of the part without hair is set to 0 and will not be displayed
Multiple layer s can be drawn using multiple pass es such as shader s
Each Pass represents a layer. When rendering each layer, Extrudes vertex positions out of the model surface using normals
Step 2:
According to the noise map, the length of the hair at random. Controls the value of alpha
For different layers, the uv position offset of hair should also be different, so that it can be made more naturally. Otherwise, if the uv remains unchanged, the direction of hair will become a hedgehog
The offset of different layers of hair may also be affected by external forces, such as wind, so an offset can be set outside
Step 3
Deal with the effects of ambient light, reflected and diffuse light, and set the color


1 multi pass rendering. For fur, the rendering method of each layer is the same, so it is used Reference in each pass to avoid duplicate code.
Different levels need to pass parameter control representation[ 1 / total number of floors ]Is the length of each layer of hair
//Surface skin rendering
    #pragma vertex vert_surface / / point shader, corresponding to the method in cginc
    #pragma fragment frag_surface / / fragment shader, corresponding to the method in cginc
    #define FURSTEP 0.0 
    #include "FurHelper.cginc" / / referenced shader
// Rendering of fur
    #pragma vertex vert_base / / point shader, corresponding to the method in cginc
    #pragma fragment frag_base / / fragment shader,   Corresponding reference to the method in cginc
    #define FURSTEP 0.05 / / fur length
    #include "FurHelper.cginc" / / referenced shader

2 vertex extension to realize multi-level migration
Idea: extend a certain distance outward in the normal direction of the vertex. So it is handled in the vertex shader
            v2f vert(a2v v)
                v2f o;
                float3 OffetVertex = v.vertex.xyz + v.normal * _LayerOffset *_FurLength;//Vertex extension
                OffetVertex += mul(unity_WorldToObject, _FurOffset);//Vertex force offset [add an offset in one direction, which looks more natural, such as sagging]

3 use noise map for transparency
Idea: use uv2 to store the noise map, and then process and display the transparency of hair according to the randomness of the noise map.  
fixed3 noise = tex2D(_FurTex, i.uv.zw ).rgb; 
fixed alpha = saturate(noise - (_LayerOffset * _LayerOffset + _LayerOffset * _FurDensity));

4. Optimization treatment. Refining parameters
    · Fur length parameter. Limit the extension length of the outermost layer
    · Hair layers
    · Color parameters
    · Fur density parameter (used in noise map to control uv tiling)
    · Add an offset parameter (for example, the fur of a long haired pet droops)
5 * optimize processing and add hair movement
Use c# code to control hair and move slowly [lerp]
The shader renders only one layer of fur
c# code control, clone multiple game objects, assign the shader of the hair, and generate multiple layers by changing the offset [the number of layers required is the number of layers generated]
In c# code, lerp in update controls each layer of hair to move slowly. So as to realize elegant movement
// Generation, material assignment, shader assignment
void CreateShell()
    layers = new GameObject[LayerCount];
    float furOffset = 1.0f/ LayerCount;
    for (int i = 0; i< LayerCount; i++)
        //Copy the rendered original model once
        GameObject layer = Instantiate(Target.gameObject, Target.transform.position, Target.transform.rotation);
        layer.transform.parent = _parent;
        layer.GetComponent<Renderer>().sharedMaterials = new Material[1];
        layer.GetComponent<Renderer>().sharedMaterial = ResetSharedMaterials(i, furOffset);
        layers[i] = layer;

// shader assignment
Material ResetSharedMaterials(int index, float furOffset)
    Material special = new Material(ShellShader);
    if (special != null)
        special.SetTexture("_MainTex", Target.sharedMaterial.GetTexture("_MainTex"));
        special.SetColor("_Color", FurColor);
        special.SetColor("_RootColor", FurRootColor);
        special.SetColor("_RimColor", FurRimColor);
        special.SetColor("_Specular", FurSpecularColor);
        special.SetFloat("_Shininess", Shininess);
        special.SetFloat("_RimPower", RimPower);
        special.SetFloat("_FurShadow", FurShadow);
        special.SetFloat("_FurLength", FurLength);
        special.SetFloat("_FurDensity", FurDensity);
        special.SetFloat("_FurThinness", FurThinness);
        special.SetFloat("_LayerOffset", index * furOffset);            //The offset parameters of different shells are different
        special.SetVector("_FurOffset", FurForce* Mathf.Pow(  index*furOffset,FurTenacity));//Calculate the Shell offset affected by force, number of layers and hardness
        special.renderQueue = 3000 + index;//Since the translucent material is rendered in a single channel and depth is written, in order to prevent it from being eliminated by depth, the rendering queue should be changed manually
    return special;

// move
void UpdateShellTrans()
    if (layers == null || layers.Length == 0)
    for (int i = 0; i < layers.Length; i++)
        //Position and rotate Lerp to target model
        layers[i].gameObject.transform.position = Vector3.Lerp(layers[i].gameObject.transform.position, Target.transform.position, lerpSpeed * Time.deltaTime *20);
        layers[i].gameObject.transform.rotation = Quaternion.Lerp(layers[i].gameObject.transform.rotation, Target.transform.rotation, lerpSpeed * Time.deltaTime *10);

reference resources:

How sweet! Production and sharing of the new version of "the first beauty of the canyon" Daji tail hair


Fur Tool Unity Asset for 3D Character Modelling Artists | Coldface Interactive



Keywords: Unity lua Shader

Added by Cooper94 on Sat, 20 Nov 2021 10:19:54 +0200