项目开发笔记(十三)
最近使用Shader的时候需要给Shader传递参数,后面发现参数过多(30+个颜色值和10+个纹理),Shader在PC上运行得好好的,但是到某些移动设备上就尿崩,突然发现:
- 为什么shader无法往其传递数组呢?
- 为什么使用了多个纹理后(例如三星T560),在设备上无法运行正常?
Unity允许通过Material往Shader里传递int,Texture,Color, Vector4等参数,而且不同的移动设备可能支持不同的 Shader Model(SM 1 2 3 4 5),不同的SM版本会限制纹理指令和算术指令的数量使用。参考:High-Level_Shading_Languag。经过测试设备三星T560只能传入8个纹理指令,再增加就会工作不正常。我们不能改变设备显卡对SM的支持,只能通过减少纹理指令和算术指令的方式来兼容它。顺带把如何传入数组的问题也解决了。那怎么做呢?
0x00. 首先怎么往Shader传递参数?
举个例子 倘若你在Shader中定义了如下变量:
//------------------------------------------------------------------------- int _nCount = 0; // 容差 float _fTolerance; // 纹理平铺数量 int _nUVTileCount; sampler2D _MainTex; //-------------------------------------------------------------------------
则可通过Material来传输给它:
// m_sShader 为Shader变量 // m_txtMain 为Texture变量 Material m = null; if (null != m_sShader) { m = new Material(m_sShader); m.SetInt("_nCount", 6); m.SetFloat("_fTolerance", 0.4f); m.SetInt("_nUVTileCount", 3); m.SetTexture("_MainTex", m_txtMain); }
0x01. 怎么给Shader传递N个参数呢?
(!!警告!!这种方式会造成多次纹理采样
)
当然可以使用0x00的方式一个一个传,但是在没有严格的性能把控的情况下,我们可以把N个参数写入纹理中,然后传输纹理对象给Shader。举个例子,下面的代码我通过一个texture对象把10个颜色值(或者说是 10×4 个浮点数指)传给了Shader。
Texture2D colorTxt = new Texture2D(10, 10); Color[] colors = new Color[10]{ Color.black, Color.white, Color.yellow , Color.gray , Color.green , Color.grey , Color.blue,Color.red, Color.magenta,Color.cyan}; for (int j = 0; j != 10; j++) { for (int i = 0; i != 10; ++i) { colorTxt.SetPixel(i, j, colors[j]); } } colorTxt.Apply(); // m_sShader 为Shader变量 Material m = new Material(m_sShader); m.SetTexture("_ColorTxt", colorTxt);
在Shader中使用的时候可以如下:
// 获取第0个参数 fixed4 argv000 = tex2D(_TagTex002, ((0.5,0.05))) // 获取第1个参数 fixed4 argv001 = tex2D(_TagTex002, ((0.5,0.15))) // ....
上图纹理放大后如下图:
通过这种方式就可以把无数个数值参数传递给了Shader,注意在几个或者少于10个参数的情况下不推荐这种方式。
0x02. 怎么给Shader传递更多的纹理对象?
拿SMT560来说,如果我定义超过8个纹理对象并传给Shader的时候 Shader就会显示不正常。那么怎么解决这个问题呢?
答案就在0x01,简单来说就是合并纹理。把传入的纹理们 几个合并成一个,或者如果传输的都是小纹理 则可考虑全部纹理合并成一个。然后使用的时候再根据UV值换算。这样就解决了文章开头的两个小问题。
PS:如各位朋友有更好的方案欢迎留言告知,感激不尽
参考文献
纹理其实就是数组。
最大纹理数量和纹理填充率都是受GPU硬件限制的,这个我怀疑和GPU AGP总线通道数量有关。不过用8个纹理,真的太恐怖了,我不可想象你用的是多复杂的shader。
0x01的方式并不推荐,看上文中你的多次纹理采样,我心里就微微发寒。我要是BOSS,一定要你命三千。
-_- 好,立马去优化一下