因爲項目中有換裝系統,換裝系統中有大量的可替換的臉部數據,比如眉毛,睫毛,腮紅,眼影,口紅等,需要十多個貼圖通道。所以我們的shader中需要有十多張貼圖通道
跑再android機器上時,一切都正常顯示。但跑再iphone8時,就會發現材質丟失了
然後查到的報錯信息是採樣次數過多,機器不支持這個採樣次數。iphone8是9個
基於這個問題,有兩種解決方案:
第一種:
把多個貼圖合併,也就是再到gpu之前先把所有貼圖合併成一個貼圖,然後給gpu渲染一次。
var pixels = tex.GetPixels32();
for (int j = 0; j < pixels.Length; j++)
{
if (pixels[j].a != 0)
{
if (j < colors.Length)
{
var realJ = j;
var newPixel = pixels[realJ] * (color * 255);
var curColors = colors[realJ];
var dstColors = Color32.Lerp(curColors, newPixel, (float)newPixel.a / 255.0f);
colors[realJ] = new Color32((byte)(curColors.r > 0 ? (dstColors.r) : newPixel.r),
(byte)(curColors.g > 0 ? (dstColors.g) : newPixel.g),
(byte)(curColors.b > 0 ? (dstColors.b) : newPixel.b),
(byte)(curColors.a > 0 ? (dstColors.a) : newPixel.a));
}
}
}
寫入shader:
var faceOtherShader = Shader.PropertyToID("_FaceOtherMap");
bodymat.SetTexture(faceOtherShader, texture2d);
優點:這樣減少了貼圖數量,消耗也更少了,帶寬消耗自然也更小了。
缺點:但這樣做的問題是每次換裝時都需要合併一次貼圖,會有明顯的卡頓,效果也並不是很好。
第二種:
分兩個材質放貼圖
也就是第一個做光照模型的渲染,第二個做貼圖渲染
儘量讓兩個材質的貼圖採樣個數都小於9個。讓光照模型材質(AvatarMat)先渲染,貼圖渲染(AvatarFaceTexture)後渲染。
然後跑到iphone8上,確實不會出現材質丟失的紫色了。
但是出現了一個問題:
就是腮紅,嘴脣等都會閃爍。
然後看了下這張rt的alpha通道
越白說明alpha越高,可以看到眼睛旁邊和嘴巴旁邊都有半透明的效果。
查找原因:
看了下shader發現
Blend SrcAlpha OneMinusSrcAlpha
混合模式只用了這個。因爲這個blend方式是也會把alpha通道做混合的。
然後我們進行分析
先渲染的avatarmat中是不透明的
但後面處理的avatarfacetexture裏是有透明
然後混合用Blend SrcAlpha OneMinusSrcAlpha混合公式後得到的是一個帶透明的效果,因爲後面在處理的face時,因爲alpha是不爲1的,所以frontcolor*alpha+backcolor*(1-alpha)肯定得到的是一個半透明的值。
比如:
facetexture當前像素是(0.8,0.8,0.8,0.8)avatarmat的像素是(1,1,1,1)
計算過程:(0.8,0.8,0.8,0.8)*0.8+(1,1,1,1)(1-0.8)=(0.84,0.84,0.84,0.84)
也就是最後的alpha值是0.84,所以是透明的,所以會顯示背景而導致半透明區域是閃爍的。
那麼解決辦法也很簡單,就是區分color的混合和alpha的混合。
因爲blend有一種模式可以區分color和alpha的混合。
Blend SrcFactor DstFactor, SrcFactorA DstFactorA
也就是前面兩個是顏色混合,後面兩個是顏色混合。正是我們想要的。所以最後的混合方式是
Blend SrcAlpha OneMinusSrcAlpha,One One
問題就解決了
當然這裏還要注意FaceTextureshader要做小量的深度偏移,不然會跟身體重疊比較嚴重,導致閃爍發生
優點:切換貼圖不會卡頓,順暢過度
缺點:貼圖採樣數量還是一樣多,所以會產生帶寬過高的問題
以後的優化點可以考慮部分貼圖合併減少貼圖數量。