最有效的多纹理方式 – iOS,OpenGL ES2,优化

我试图find在iOS上的OpenGL ES2中处理多纹理的最有效的方法。 “高效率”是指即使在较老的iOS设备(iPhone 4及更高版本)上也是最快的渲染 – 同时也是平衡方便。

我考虑过(并尝试过)几种不同的方法。 但遇到了一些问题和疑问。

方法1 – 我的基础和正常值是没有ALPHA rgb。 对于这些对象,我不需要透明度。 我的发射和镜面reflection信息只有一个频道。 为了减lesstexture2D()调用我想我可以存储发射作为基地的alpha通道,镜面作为正常的alpha。 每个人都在他们自己的文件中看起来像这样:

两个双向纹理

到目前为止,我的问题一直在find一个文件格式,将支持完整的非预乘alpha通道。 PNG只是没有为我工作。 每一个我试图把这个保存为PNG的方法都是将.alg与.rgb保存在一起(通过photoshop),基本上破坏了.rgb。 当我重新加载文件时,任何具有0.0阿尔法的像素都有黑色的rgb。 我在这里发布了这个问题没有任何活动。

我知道这种方法会产生更快的渲染,如果我能find一种方法来保存和加载这个独立的第四频道。 但到目前为止,我还没有能够,只好继续前进。

方法2 – 当没有工作,我转向一个单一的4路纹理,每个象限有不同的地图。 这不会减lesstexture2D()调用,但会减less在着色器中访问的纹理数量。

一个4路纹理

4路纹理确实需要修改着色器中的纹理坐标。 对于模型的灵活性,我将texcoords放在模型的结构中,并在着色器中修改它们,如下所示:

 v_fragmentTexCoord0 = a_vertexTexCoord0 * 0.5; v_fragmentTexCoord1 = v_fragmentTexCoord0 + vec2(0.0, 0.5); // illumination frag is up half v_fragmentTexCoord2 = v_fragmentTexCoord0 + vec2(0.5, 0.5); // shininess frag is up and over v_fragmentTexCoord3 = v_fragmentTexCoord0 + vec2(0.5, 0.0); // normal frag is over half 

为了避免dynamic纹理查找 (感谢Brad Larson ),我将这些偏移量移至顶点着色器,并将它们保留在片段着色器之外。

但是我的问题是: 减less着色器中使用的纹理采样器的数量? 或者我会在这里使用4个不同的小纹理更好?

我遇到的一个问题是在不同的地图之间stream血。 1.0的texcoord是由于线性纹理映射而在一些蓝色正常像素上平均的。 这在接缝附近的物体上增加了一个蓝色的边缘。 为了避免它,我不得不改变我的紫外线贴图,不要太靠近边缘。 对于很多对象来说这是一个很痛苦的事情。

方法3将结合方法1和方法2,并且一方面具有base.rgb + emission.a,另一方面具有normal.rgb + specular.a。 但是,我仍然有这个问题得到一个独立的alpha保存在一个文件中。

也许我可以将它们保存为两个文件,但在加载过程中结合它们,然后将它们发送到OpenGL。 我必须尝试。

方法4最后,在3D世界中,如果我有20个不同的面板纹理的墙壁,这些应该是单个文件或全包装在一个纹理地图集? 我最近注意到,在某些时候,我的世界从一个地图集转移到了个人纹理 – 尽pipe它们每个都是16×16。

使用单个模型并通过修改纹理坐标(我已经在上面的方法2和3中完成了),您可以轻松地向着色器发送偏移量以在地图集中select特定的地图:

 v_fragmentTexCoord0 = u_texOffset + a_vertexTexCoord0 * u_texScale; 

这提供了很大的灵活性并减less了纹理绑定的数量。 这基本上是我现在在我的游戏中做的。 但是,访问一个较大的纹理的一小部分,并在顶点着色器中有上述的math更快? 还是反复快速地反复绑定更小的纹理? 特别是如果你不按纹理sorting对象。

我知道这是很多。 但是这里的主要问题是考虑速度+便利性的最有效的方法是什么? 方法4对于多个纹理会更快吗?还是多个重新绑定会更快? 还是有其他的方式,我俯瞰。 我看到所有这些3D游戏都有很多graphics和区域覆盖。 他们如何保持帧率,特别是在像iphone4这样的老设备上?

****更新****

由于我在最近几天突然有两个答案,我会说这个。 基本上我find了答案。 或者答案。 问题是哪种方法更有效率 ? 意味着哪种方法会导致最佳的帧速率。 我已经尝试了上述各种方法,并在iPhone 5上,他们都快速。 iPhone5 / 5S有一个非常快的GPU。 重要的是在像iPhone4 / 4S这样的老式设备上,或者在像视网膜iPad这样的大型设备上。 我的testing不科学,我没有ms速度报告。 但是4个texture2D()对4个RGBA纹理的调用实际上与4个texture2d()调用具有偏移量的单个纹理一样快或甚至更快。 当然,我在顶点着色器中进行偏移计算,而不是在片段着色器中(从不在片段着色器中)。

所以也许有一天我会做testing,并用一些数字做网格来报告。 但是我现在没时间去做,自己写一个正确的答案。 而且我不能真的勾选任何其他答案,而不是回答问题的原因,这不是如何工作。

不过感谢那些已经回答的人。 并检查了我的这个也回答了这个问题的其他问题: 从iOS上的两个JPEG加载RGBA图像 – OpenGL ES 2.0

在您的内容pipe道中有一个后处理步骤,您将rgb与alpha纹理合并并将其存储在。 包装游戏时的Ktx文件或编译时的后期构build事件。

这是相当简单的格式,编写这样的命令行工具很容易,加载2个PNG,并合并成一个KTX,RGB +阿尔法。

这样做的一些好处是 – 在游戏启动时加载文件时,CPU的开销较小,因此游戏开始更快。 – 某些GPUso本身不支持rgb 24bit格式,这会迫使驱动程序在内部将其转换为rgba 32bit。 这为加载阶段和临时内存使用增加了更多的时间。

现在,当您获得纹理对象中的数据时,您确实希望最小化纹理采样,因为它意味着许多GPU操作和内存访问取决于过滤模式。

我会build议有2个纹理每个2层,因为有问题,如果您将所有这些添加到相同的问题是潜在的人为因素,当您采样双线性或mipmapped,因为它可能包括邻近像素接近边缘,其中一个纹理层结束和第二个开始,或者如果你决定生成mipmap。

作为一个额外的改进,我build议不要在Ktx中使用原始的rgba 32位数据,而是实际将其压缩为dxt或pvrtc格式。 这将使用更less的内存,这意味着更快的加载时间和更less的内存传输的GPU,作为内存带宽是有限的。 当然,将压缩机添加到后处理工具稍微复杂一些。 请注意,压缩纹理会根据algorithm和实现的不同而有所不同。

愚蠢的问题,但你确定你是取样限制? 在我看来,使用“两种双向纹理”可能会拉动很多纹理数据,而且可能会受到带宽的限制。

如果你要使用3种纹理[BaseRGB,NormalRBG和组合的Emission + Specular]并使用PVRTC压缩? 根据具体情况,你甚至可以使用2bpp(而不是4bpp)作为BaseRGB和/或Emission + Specular。

对于法线我可能会坚持4bpp。 此外,如果您能够承受着色器指令,则只存储R&G通道(将0置于蓝色通道中),并用一些math运算重新获取蓝色通道。 这应该会提高质量。