带有法线渲染的 OpenGL ES 多边形(注意“ES!”)
好吧...假设我有一个相对简单的实体,它有 6 个不同的法线,但实际上有接近 48 个面(每个方向 8 个面),并且面之间有很多共享顶点。在 OpenGL 中渲染它最有效的方法是什么?
我知道我可以将顶点放入数组中,然后使用索引数组来渲染它们,但我必须不断打破渲染步骤来更改法线(即设置法线 1...渲染 8 个面...设置法线2...渲染 8 个面等)因此,我必须维护一个索引数组数组...每个法线一个!不好!
我可以做到的另一种方法是使用单独的法线和顶点数组(甚至交错它们),但这意味着我需要法线与顶点具有一对一的比率,这意味着法线将被复制 8 倍以上比他们需要的更好!在具有球形甚至弯曲表面的物体上,每个法线很可能都是不同的,但对于这一点来说,这确实看起来像是浪费内存。
在一个完美的世界中,我希望我的顶点和法线数组具有不同的长度,然后当我去绘制三角形或四边形时指定该顶点的每个数组的索引。
现在,OBJ 文件格式允许您准确指定...不同长度的顶点数组和法线数组,然后当您指定要渲染的面时,您可以指定顶点和法线索引(以及 UV 坐标,如果你也在使用纹理)这似乎是完美的解决方案! 48 个顶点,但只有 8 个法线,然后是定义形状面的索引对。但我不确定如何在 OpenGL ES 中渲染它(再次注意“ES”。)目前我必须“非规范化”(抱歉这里的 SQL 双关语)法线回到 1 比 1 的值顶点数组,然后渲染。只是浪费我的记忆。
有人帮忙吗?我希望我在这里遗漏了一些非常简单的东西。
标记
Ok... imagine I have a relatively simple solid that has six distinct normals but actually has close to 48 faces (8 faces per direction) and there are a LOT of shared vertices between faces. What's the most efficient way to render that in OpenGL?
I know I can place the vertices in an array, then use an index array to render them, but I have to keep breaking my rendering steps down to change the normals (i.e. set normal 1... render 8 faces... set normal 2... render 8 faces, etc.) Because of that I have to maintain an array of index arrays... one for each normal! Not good!
The other way I can do it is to use separate normal and vertex arrays (or even interleave them) but that means I need to have a one-to-one ratio for normals to vertices and that means the normals would be duplicated 8 times more than they need to be! On something with a spherical or even curved surface, every normal most likely is different, but for this, it really seems like a waste of memory.
In a perfect world I'd like to have my vertex and normal arrays have different lengths, then when I go to draw my triangles or quads To specify the index to each array for that vertex.
Now the OBJ file format lets you specify exactly that... a vertex array and a normal array of different lengths, then when you specify the face you are rendering, you specify a vertex and a normal index (as well as a UV coord if you are using textures too) which seems like the perfect solution! 48 vertices but only 8 normals, then pairs of indexes defining the shapes' faces. But I'm not sure how to render that in OpenGL ES (again, note the 'ES'.) Currently I have to 'denormalize' (sorry for the SQL pun there) the normals back to a 1-to-1 with the vertex array, then render. Just wastes memory to me.
Anyone help? I hope I'm missing something very simple here.
Mark
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
你没有遗漏任何东西。这就是规范的工作方式,因为这就是大多数硬件的工作方式(也就是说,你的完美并不是硬件完美)。
我不会讨论实现支持索引数组的硬件的复杂性,但我会指出您可能会失去的一个优化:
GL 可能会使用单个索引作为顶点后变换缓存的索引,从而不必在下一次迭代时重新变换顶点。通过一组索引,您可以使优化变得更加复杂。
关于内存节省:在您的情况下,您正在谈论大约一个立方体,每个面使用 4 个四边形、8 个三角形。所以我们讨论的是 9*6=54 个唯一顶点。如果只有位置和法线,则为 54 * 4 * 3 * 2 = 1296 B 顶点数据 + 2 * 48 * 3 = 288 B 索引数据(假设属性基本类型为 4 字节,索引为 GLushort)。总计1584B。这也假设位置和法线的数据格式不是最佳的。替代方案大约是 26*4*3(pos)+8*4*3(norm)+2*48*3*2=312+96+576=984B。因此,在这个人为的案例中您节省了大约 0.5kB。
将其传递给更节省内存的属性类型,您将得到:648+288=936 vs 156+48+576=780...差异开始可以忽略不计。
我为什么要提出这个?因为如果您想要优化内存消耗,您应该查看属性数据类型。
最后,正如您自己所注意到的,在实际的 3D 世界中(即不是在盒子的世界中),这种机制的节省会很低:很少有属性可以共享。
You're not missing anything. This is how the spec works because this is how most hardware works (aka your perfect is not hardware perfect).
I won't go into the complexities of implementing hardware that would support an array of indices, but I will point out one optimization you'd likely lose:
the GL potentially uses the single index as an index into a post-vertex transform cache, to not have to re-transform the vertex on the next iteration. You make that optimization significantly more complex with a set of indices.
Regarding memory savings: in your case, you're talking about roughly a cube with each face using 4 quads, 8 triangles. So we're talking about 9*6=54 unique vertices. if you only have position and normals, that's 54 * 4 * 3 * 2 = 1296 B of vertex data + 2 * 48 * 3 = 288 B of index data (assuming 4-byte for the attribute base types and GLushort for indices). Grand total of 1584B. That's assuming a non-optimal data format for positions and normals too. The alternative is roughly 26*4*3(pos)+8*4*3(norm)+2*48*3*2=312+96+576=984B. So you saved about 0.5kB on this contrived case.
Pass this to more memory-saving types for the attributes, and you get to: 648+288=936 vs 156+48+576=780... The difference starts to be negligeable.
Why am I bringing this up ? because you should look at your attribute data types if you want memory consumption optimizations.
Last, as you noticed yourself, in practical 3d worlds (i.e. not in the world of boxes), the savings of such a mechanism would be low: very few attributes can get shared.