我试图在iOS上找到处理OpenGL ES2中多纹理的最有效方法。通过有效的'我的意思是即使在较旧的iOS设备(iPhone 4及更高版本)上也能实现最快的渲染效果 - 而且还可以平衡便利性。
我已经考虑(并试过)了几种不同的方法。但是遇到了一些问题和疑问。
方法1 - 我的基数和正常值是rgb,没有ALPHA。对于这些物品,我不需要透明度。我的发射和镜面反射信息都只是一个通道。为了减少texture2D()
次调用,我想我可以将发射存储为基数的alpha通道,将镜面反射存储为法线的alpha。每个都在他们自己的文件中,它看起来像这样:
到目前为止,我的问题是找到一种支持完整的非预乘alpha通道的文件格式。 PNG对我来说没有用。我尝试将此作为PNG保存的每种方式都将.alpha与.rgb文件保存(通过photoshop)基本上摧毁.rgb。当我重新加载文件时,任何具有0.0 alpha的像素都有黑色rgb。我发布了这个问题here没有任何活动。
我知道如果我能找到一种方法来保存和加载这个独立的第4通道,这种方法会产生更快的渲染效果。但到目前为止,我还没有能够继续前进。
方法2 - 当它没有工作时,我转到了单个4向纹理,其中每个象限都有不同的地图。这不会减少texture2D()
次调用,但会减少着色器中正在访问的纹理数量。
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 texture lookups(感谢Brad Larson),我将这些偏移移动到顶点着色器并将它们保留在片段着色器之外。
但我的问题是:减少着色器中使用的纹理采样器的数量是否重要?或者我最好在这里使用4种不同的较小纹理?
我遇到的一个问题是在不同的地图之间流血了。由于线性纹理映射,1.0的texcoord在一些蓝色正常像素中进行平均。这在接缝附近的物体上添加了蓝色边缘。为了避免这种情况,我不得不将我的UV贴图更改为不太靠近边缘。对很多物体来说,这很痛苦。
方法3 将组合方法1和2.并且在一侧具有base.rgb + emission.a而在另一侧具有normal.rgb + specular.a。但是我仍然有这个问题获得一个独立的alpha保存在文件中。
也许我可以将它们保存为两个文件,但在加载过程中将它们合并后再发送到openGL。我必须尝试一下。
方法4 最后,在3D世界中,如果我有20种不同的墙面板纹理,它们应该是单个文件还是全部打包在单个纹理图集中?我最近注意到,在某些时候,我的飞船从一个地图集转移到了单独的纹理 - 尽管它们每个都是16x16。
使用单个模型并修改纹理坐标(我在上面的方法2和3中已经做过),您可以轻松地向着色器发送偏移量以选择地图集中的特定地图:
v_fragmentTexCoord0 = u_texOffset + a_vertexTexCoord0 * u_texScale;
这提供了很大的灵活性并减少了纹理绑定的数量。这基本上就是我现在在游戏中的表现。但是 IS IT 能够更快地访问较大纹理的一小部分,并在顶点着色器中具有上述数学运算?或者反复绑定较小的纹理是否更快?特别是如果你不按纹理对对象进行排序。
我知道这很多。但这里的主要问题是考虑速度+便利性的最有效方法是什么?对于多个纹理,方法4会更快还是多个rebinds会更快?或者还有其他一些我忽视的方式。我看到所有这些3D游戏都有很多图形和区域覆盖。他们如何保持帧速率,尤其是在像iphone4这样的旧设备上?
****更新****
由于我在最近几天突然有2个答案,我会这样说。基本上我确实找到了答案。或者 AN 回答。问题是哪种方法更有效?意味着哪种方法将产生最佳帧速率。我已经尝试过上面的各种方法,而且在iPhone 5上他们的速度一样快。 iPhone5 / 5S拥有极快的GPU。重要的是iPhone4 / 4S等旧设备,或视网膜iPad等大型设备。我的测试不科学,我没有速度报告。但是对4个RGBA纹理的4 texture2D()
调用实际上与对具有偏移的单个纹理的4 texture2d()
调用一样快或甚至更快。当然,我在顶点着色器中进行偏移计算而不是片段着色器(从不在片段着色器中)。
所以也许有一天我会做测试并制作一个带有一些数字的网格来报告。但我现在没有时间做这件事并自己写一个正确的答案。而且,我无法真正勾选任何其他没有回答问题的答案,因为这不是SO的工作方式。
但感谢有回答的人。并查看我的另一个问题,该问题也回答了这个问题:Load an RGBA image from two jpegs on iOS - OpenGL ES 2.0
答案 0 :(得分:1)
在您的内容管道中有一个后期处理步骤,您将rgb与alpha纹理合并并将其存储在一个。打包游戏时的Ktx文件或编译时的后期构建事件。
这是相当简单的格式,编写这样的命令行工具很简单,它可以加载2个png并将它们合并为一个Ktx,rgb + alpha。
这样做的一些好处是 - 在游戏启动时加载文件时减少了cpu开销,因此游戏启动更快。 - 某些GPUso本身不支持rgb 24bit格式,这会强制驱动程序在内部将其转换为rgba 32bit。这会增加加载阶段和临时内存使用的时间。
现在,当您获得纹理对象中的数据时,您确实希望最小化纹理采样,因为它意味着很多gpu操作和内存访问,具体取决于过滤模式。
我建议有2个纹理,每个2层,因为如果你用双线性或mipmap样本将所有这些纹理添加到同一个纹理是潜在的工件,因为它可能包括靠近边缘的邻近像素,其中一个纹理图层结束,第二层开始,或者如果您决定生成mipmap。
作为额外的改进,我建议不要在Ktx中使用原始的rgba 32位数据,而是将其压缩为dxt或pvrtc格式。这将使用更少的内存,这意味着更快的加载时间和更少的内存传输,因为内存带宽有限。 当然,将压缩器添加到后处理工具稍微复杂一些。 请注意,压缩纹理会根据算法和实现而略微降低质量。
答案 1 :(得分:0)
愚蠢的问题,但你肯定你是采样器有限吗?在我看来,用你的"两个双向纹理"你可能会吸收很多纹理数据,而你可能而是带宽有限。
如果您使用3个纹理[BaseRGB,NormalRBG和组合Emission + Specular]并使用PVRTC压缩怎么办?根据细节,您甚至可以使用2bpp(而不是4bpp)作为BaseRGB和/或Emission + Specular。
对于法线,我可能会坚持使用4bpp。此外,如果你能负担着色器指令,只存储R& G通道(在蓝色通道中加0)并用一些数学重新导出蓝色通道。这应该会提供更好的质量。