---
# System prepended metadata

title: 髮片模型_進階
tags: [髮片model]

---

# 髮片模型_進階
###### tags: `髮片model`

### 髮片模型生成研究步驟
(一)髮片模型構想
(二)Unity Mesh實作(架構)
(三)三種髮片樣式演算公式
(四)髮片模型生成與VR手把


### (一)髮片模型構想
   預期結果，(一)頭髮模型風格，以VRoid的動漫式風格為目標，(二) 髮片模型有三種樣式，分別是直髮、波浪捲髮及螺旋捲髮，髮片會依不同長度、寬度、厚度等參數挑整呈現不一樣的效果，(三)模型即時更新計算，讓使用者可即時看到效果，以利創作。

### (二)Unity Mesh實作
**Mesh設定步驟**

**step1:** 建立MeshFilter以及MeshRenderer組件，並將mesh網格加入進MeshFilter中

```
GetComponent<MeshFilter>().mesh = mesh = new Mesh();
GetComponent<MeshRenderer>().material = hairColor; //加入material
```

**step2:** 設定vertice、uv、nomal、tangent、triangle

**mesh.vertice陣列:** 
將在PosGenerate.cs程式中收集的座標給到mesh.vertice
```C#
Vector3[] vertice;
for (int i = 0,j=0;i<GetPointPos.Count;i++,j++) 
{
    vertice[i] = GetPointPos[j];
}
mesh.vertices = vertice;
```

**mesh.uv陣列:** 
貼圖設定(Vector2座標，範圍在0~1)
```C#
uv = new Vector2[GetPointPos.Count];
int len = GetPointPos.Count / 4;
float TexWidth = 0.8f;
for (int i = 0, x = 0; i < len; i++)
{
    for (int j = 1; j <= 4; j++)
    {
        uv[x] = new Vector2(TexWidth / 4 * j, 1.0f / len * i);
        x++;
    }
}
mesh.uv = uv;
```
**mesh.tangent陣列:** 
```C#
tangents = new Vector4[GetPointPos.Count];
for (int i = 0,j=0;i<GetPointPos.Count;i++,j++) 
{
    tangents[i] = new Vector4(1f, 0f, 0f, -1f);
}
mesh.tangents = tangents;
```
**mesh.triangle陣列:** 
以int型態指定存取vertice[某index]中的資料(座標)

```C#
int point = GetPointPos.Count - 2;
triangle = new int[point * 6];

int t = 0;
for (int i = 1, vi = 0; i <= point - 2; i++, vi++)
{
    if (i % 4 != 0) t = SetQuad(triangle, t, vi, vi + 1, vi + 4, vi + 5);
    else t = SetQuad(triangle, t, vi, vi - 3, vi + 4, vi + 1);
}
int vii = 0;
t = SetQuad(triangle, t, vii + 2, vii + 1, vii + 3, vii);
vii = GetPointPos.Count - 1;
t = SetQuad(triangle, t, vii - 1, vii, vii - 2, vii - 3);

mesh.triangles = triangle;

```


**mesh.nomal用mesh.RecalculateNormals()函式自動計算代替**

完整 MeshGenerate.cs 程式碼
```C#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class MeshGenerate : MonoBehaviour
{
    private Mesh mesh;
    public static Material GethairColor;
    Shader HairShader;

    Vector3[] vertice;
    Vector2[] uv;
    Vector4[] tangents;
    int[] triangle;

    public void GenerateMesh(List<Vector3> GetPointPos,int Getwidth) 
    {
        GethairColor = GetComponent<Renderer>().material;
        HairShader = Shader.Find("Diffuse Fast");
        GethairColor.shader = HairShader;

        GethairColor.color = new Color(222f/255,184f/255,135f/255);
        GetComponent<MeshFilter>().mesh = mesh = new Mesh();
        GetComponent<MeshRenderer>().material = GethairColor;
        mesh.name = "HairModel";

        vertice = new Vector3[GetPointPos.Count];
        uv = new Vector2[GetPointPos.Count];
        tangents = new Vector4[GetPointPos.Count];

        for (int i = 0,j=0;i<GetPointPos.Count;i++,j++) 
        {
            vertice[i] = GetPointPos[j];
            tangents[i] = new Vector4(1f, 0f, 0f, -1f);
        }

        //加厚度
        int len = GetPointPos.Count / 4;
        float TexWidth = 0.8f;
        for (int i = 0, x = 0; i < len; i++)
        {
            for (int j = 1; j <= 4; j++)
            {
                uv[x] = new Vector2(TexWidth / 4 * j, 1.0f / len * i);
                x++;
            }
        }

        mesh.vertices = vertice;
        mesh.uv = uv;
        mesh.tangents = tangents;
        
        int point = GetPointPos.Count - 2;
        triangle = new int[point * 6];

        int t = 0;
        for (int i = 1, vi = 0; i <= point - 2; i++, vi++)
        {
            if (i % 4 != 0) t = SetQuad(triangle, t, vi, vi + 1, vi + 4, vi + 5);
            else t = SetQuad(triangle, t, vi, vi - 3, vi + 4, vi + 1);
        }
        int vii = 0;
        t = SetQuad(triangle, t, vii + 2, vii + 1, vii + 3, vii);
        vii = GetPointPos.Count - 1;
        t = SetQuad(triangle, t, vii - 1, vii, vii - 2, vii - 3);

        mesh.triangles = triangle;
        mesh.RecalculateBounds();
        mesh.RecalculateNormals();
    }

    private static int SetQuad(int[] triangles, int i, int v0, int v1, int v2, int v3)
    {
        triangles[i] = v0;
        triangles[i + 1] = v1;
        triangles[i + 2] = v2;
        triangles[i + 3] = v2;
        triangles[i + 4] = v1;
        triangles[i + 5] = v3;
        return i + 6;
    }

}

```
### (三)三種髮片樣式演算公式

**三種髮片樣式:直髮、波浪捲髮、螺旋捲髮**
目的:設計三種髮片風格演算公式，來調配整個Mesh.vertice頂點位置

### 實作步驟:
**(一)抓取髮片生成座標，配合刷子物件以及VR手把**
1. 抓取刷具(UI道具)座標點，為生成點座標值。
2. 每隔length=0.05f的距離抓一次點
3. 以正規化糾正移動速度造成的誤差距離

**以下是抓取座標的程式架構**
```C#
if (TriggerDown == 0 && Gather1.icon == 1) //沒被按下
{
    if (TriggerClick.GetStateDown(Pose.inputSource)) //偵測被按下的瞬間
    {

        OldPos = NewPos = HairPos.transform.position;
        PosCreater.VectorCross(HairPos.transform.up, HairPos.transform.forward, HairPos.transform.right);
        PointPos.Add(OldPos);
        TriggerDown = 1;
    }
}
if (TriggerDown == 1) //被按下
{
    NewPos = HairPos.transform.position;
    float dist = Vector3.Distance(OldPos, NewPos); //計算舊點到新點，位置的距離
    if (dist > length) //距離大於設定的長度
    {
        //正規化
        Vector3 NormaizelVec = NewPos - OldPos;
        NormaizelVec = Vector3.Normalize(NormaizelVec);
        NormaizelVec = new Vector3(NormaizelVec.x * length, NormaizelVec.y * length, NormaizelVec.z * length);
        NewPos = NormaizelVec + OldPos;
        PointPos.Add(NewPos);

        ''''

        OldPos = NewPos;
    }

   ''''

    if (TriggerClick.GetStateUp(Pose.inputSource)) //放開
    {
        ''''
        PointPos.Clear();
        direction.Clear();
        Gather1.GridState = false;
        TriggerDown = 0;
    }

```
**(二)配合VR手把，處理髮片生成方向性問題**

在3D空間中分別有世界座標的X、Y、Z三個方向，而每個物件本身也有自己LocalPosition的X、Y、Z軸，我們利用外積公式抓出物件X軸與Z軸在世界座標的位置。並將每個座標的方向性座標存入List保存，用於之後的變形計算。

```C#
public Vector3 cross1, cross2;
public List<Vector3> directionA = CreateHair.direction;
public void VectorCross(Vector3 up, Vector3 forward, Vector3 right)
{
    cross1 = Vector3.Cross(up, forward);//x
    cross1.Normalize();
    cross2 = Vector3.Cross(up, right);//z
    cross2.Normalize();
    directionA.Add(cross1); //每個座標的方向性座標存入List
    directionA.Add(cross2);
}
```
**(三)髮片即時變形程式架構**

設定三個List資料
GetPointPos →  在CreateHair抓取物件座標
TempPoint → 在變形計算過程中的暫存結構
GetUpdatePointPos → 儲存變形後的座標，並傳遞給MeshGenerate.cs中的Mesh.vertice


直髮參數:髮尾平、髮尾尖、寬度、厚度


波浪捲髮參數:髮尾平、髮尾尖、寬度、捲度、厚度


螺旋捲髮參數:髮尾平、髮尾尖、寬度、捲度、厚度
 



