ios - 多重纹理的最有效方法-iOS,OpenGL ES2,优化

标签 ios objective-c opengl-es-2.0 shader texture-mapping

我正在尝试找到在iOS上的OpenGL ES2中处理多重纹理的最有效方法。 “高效”是指即使在较旧的iOS设备(iPhone 4及更高版本)上最快的渲染速度,但也兼顾了便利性。

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

方法1 -我的基本和正常值为rgb,没有ALPHA。对于这些对象,我不需要透明度。我的发射和镜面信息分别只是一个 channel 。为了减少texture2D()调用,我认为我可以将发射存储为基准的Alpha channel ,将镜面反射存储为法线的Alpha。每个文件都保存在自己的文件中,如下所示:

到目前为止,我的问题是找到一种文件格式,该格式将支持完整的非预乘alpha channel 。 PNG只是没有为我工作。我尝试将其保存为PNG的每种方式都会将.alpha与文件保存.rgb预乘(通过Photoshop),从而基本上破坏了.rgb。重新加载文件时,任何具有0.0 alpha值的像素都具有黑色rgb。我没有事件就发布了这个问题here

我知道,如果我能找到一种方法来保存和加载此独立的第4 channel ,则此方法将产生更快的渲染。但是到目前为止,我还没有能够继续前进。

方法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种不同的较小纹理更好?

我确实遇到的一个问题是在不同的 map 之间流血。由于线性纹理映射,一些蓝色普通像素的texcoord平均为1.0。这在接缝附近的对象上添加了蓝色边缘。为了避免这种情况,我必须更改UV贴图,以使其不会太靠近边缘。这对于很多对象来说是一件痛苦的事情。

方法3 将结合方法1和2,并在一侧具有base.rgb +发射.a,在另一侧具有normal.rgb +镜面。但是我仍然遇到这个问题,要获得一个独立的Alpha来保存在文件中。

也许我可以将它们保存为两个文件,但是在加载期间将它们合并,然后再将其发送到openGL。我必须尝试一下。

方法4 最后,在3D世界中,如果我有20种不同的墙面板纹理,这些文件应该是单个文件还是全部打包在一个纹理 map 集中?我最近注意到,在某个时候,我的世界已经从 map 集移到了单独的纹理上-尽管每个纹理都是16x16。

使用单个模型并修改纹理坐标(我已经在上面的方法2和3中进行了此操作),您可以轻松地将偏移量发送到着色器,以在 map 集中选择特定的 map :
v_fragmentTexCoord0 = u_texOffset + a_vertexTexCoord0 * u_texScale;

这提供了很大的灵活性,并减少了纹理绑定(bind)的数量。基本上,这就是我现在在游戏中的做法。但是在访问较大纹理的一小部分并在顶点着色器中具有上述数学运算的速度是吗?还是一遍又一遍地重复绑定(bind)较小的纹理更快?特别是如果您不按纹理对对象进行排序。

我知道很多。但是这里的主要问题是,考虑到速度+便利性,最有效的方法是什么?方法4对于多个纹理会更快还是对多个重新绑定(bind)会更快?还是我有其他忽略的方法。我看到所有这些3d游戏都有很多图形和区域覆盖。如何提高帧速率,尤其是在iphone4等较旧的设备上?

**** 更新 ****

由于最近几天我突然有2个答案,所以我会这样说。基本上我确实找到了答案。或一个答案。问题是哪种方法更有效?意味着哪种方法将产生最佳的帧速率。我已经尝试了上面的各种方法,在iPhone 5上,它们的速度都差不多。 iPhone5/5S的GPU速度非常快。重要的是在旧设备(如iPhone4/4S)或大型设备(如视网膜iPad)上。我的测试不科学,我没有MS报告速度。但是,对4个RGBA纹理进行4次texture2D()调用实际上与对具有偏移量的单个纹理进行4次texture2d()调用一样快,甚至更快。当然,我在顶点着色器中而不是在片段着色器中执行偏移量计算(在片段着色器中都不执行)。

因此,也许有一天我会进行测试,并创建一个包含一些要报告的数字的网格。但是我现在没有时间这样做,自己写一个正确的答案。而且,我真的不能选中其他任何未回答问题的答案,原因不是SO的工作原理。

但是要感谢那些回答的人。并检查我的另一个问题,该问题也回答了其中的一些问题:Load an RGBA image from two jpegs on iOS - OpenGL ES 2.0

最佳答案

在内容管道中有一个后期处理步骤,将rgb与alpha纹理合并并存储在a中。打包游戏时将其转换为Ktx文件,或者在编译时将其转换为构建后事件。

这是相当琐碎的格式,编写这样的命令行工具很简单,该工具可以加载2个png并将它们合并为一个Ktx,rgb + alpha。

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

现在,当您将数据存储在纹理对象中时,您确实要最小化纹理采样,因为这意味着大量的gpu操作和内存访问(取决于过滤模式)。

我建议您有2个纹理,每个纹理2个层,因为如果您将所有纹理都添加到同一纹理中,则存在问题,当您使用双线性或mipmapped采样时,这是潜在的伪像,因为它可能包括靠近边缘的相邻像素(其中一个纹理层结束处),第二个开始,或者如果您决定生成mipmap。

作为一项额外的改进,我建议不要在Ktx中使用原始的rgba 32位数据,而实际上将其压缩为dxt或pvrtc格式。这将使用更少的内存,这意味着由于内存带宽有限,因此对于gpu来说加载速度更快,内存传输也更少。
当然,将压缩器添加到后处理工具中会稍微复杂一些。
请注意,取决于算法和实现,压缩纹理确实会降低质量。

关于ios - 多重纹理的最有效方法-iOS,OpenGL ES2,优化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22110519/

相关文章:

ios - UINavigationController 隐藏单个 View Controller 的导航栏

ios - React Native 应用程序在 Release模式下崩溃但在 Debug模式下运行良好

ios - 有趣的内存错误 - NSMutableArray 被一致替换

ios - 在 iOS 中用另一个实际图像掩盖透明图像

ios - 新手 : Drawing an OpenGL ES shape using an ordered vertex array

ios - 是否可以通过 iOS 纹理缓存 API 从 OpenGL ES 帧缓冲区读取 float ?

java - ARCore : should I add "uses-feature android:glEsVersion="0x0002000 0""?

ios - 在 CoreData (Swift) 中为实体创建默认数据

objective-c - LaunchAgent - 有类似 RunAtScreenSaver 的东西吗?

iOS计算重力