基於計算著色器實現BlendShape三維人臉表情動畫驅動
阿新 • • 發佈:2018-12-18
為什麼要用計算著色器呢? 因為人臉表情基模型頂點數實在是太多了,而且有不止一個模型,動畫越精細,表情基模型越多,這個時候僅僅用CPU計算顯然是不夠的,所以就開始上GPU了,平行計算。
直接上計算著色器的程式碼:
1、ComputeShader.compute
#pragma kernel Cul
RWStructuredBuffer<float3> vertBuffer;
StructuredBuffer<float3> blendshapeBuffer;
StructuredBuffer<float3> naturalBuffer;
float weight; //表情基權重
[numthreads(32,32,1)]
void Cul(uint3 id : SV_DispatchThreadID) {
int index = id.x * 32 * 4 + id.y;
vertBuffer[index] = vertBuffer[index] + (blendshapeBuffer[index] - naturalBuffer[index]) * weight;
}
2、ComputeShaderCS.cs
[RequireComponent(typeof(MeshFilter))]
public class ComputeShaderCS : MonoBehaviour {
//表情基的Mesh網格 0為natural模型
public Mesh[] meshs;
//每個表情基的權重值陣列(限制在0-1)
[Range(0,1)]
public float[] weights;
//計算著色器
public ComputeShader shader;
ComputeBuffer vertBuffer; //頂點緩衝
ComputeBuffer blendshapeBuffer; //表情基模型緩衝
ComputeBuffer naturalBuffer; //natural模型緩衝
//模型網格資訊
Mesh mesh;
private void Start() {
//獲取模型的網格資訊
mesh = GetComponent<MeshFilter>().mesh;
//分配緩衝空間(每個float佔4個位元組,一個Vector3包括3個float,所佔大小為4*3)
vertBuffer = new ComputeBuffer(meshs[0].vertices.Length, 4 * 3);
blendshapeBuffer = new ComputeBuffer(meshs[0].vertices.Length, 4 * 3);
naturalBuffer = new ComputeBuffer(meshs[0].vertices.Length, 4 * 3);
}
private void Update() {
//不必每一幀執行,此處為便於測試
RunShader();
}
//執行計算著色器
void RunShader() {
int kernel = shader.FindKernel("Cul");
Vector3[] vertices = new Vector3[meshs[0].vertices.Length];
//初始化緩衝
vertBuffer.SetData(meshs[0].vertices);
naturalBuffer.SetData(meshs[0].vertices);
//填充緩衝
shader.SetBuffer(kernel, "vertBuffer", vertBuffer);
shader.SetBuffer(kernel, "naturalBuffer", naturalBuffer);
//根據權重值與表情基計算新的頂點位置
for (int w = 1; w < weights.Length; w++) {
if (weights[w] <= 0) continue;
//初始化緩衝
blendshapeBuffer.SetData(meshs[w].vertices);
//填充緩衝
shader.SetBuffer(kernel, "blendshapeBuffer", blendshapeBuffer);
//設定權重
shader.SetFloat("weight", weights[w]);
//執行Compute Shader
shader.Dispatch(kernel, 4, 4, 1);
}
//獲取計算結果
vertBuffer.GetData(vertices);
//更改網格結構
mesh.vertices = vertices;
}
void OnDestroy() {
if (vertBuffer != null) vertBuffer.Release();
if (blendshapeBuffer != null) blendshapeBuffer.Release();
if (naturalBuffer != null) naturalBuffer.Release();
}
}
效果