libGDX 中的慢速模型批量渲染 [英] slow model batch rendering in libGDX

查看:14
本文介绍了libGDX 中的慢速模型批量渲染的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个问题,在 libGDX 中是否有用于分组模型的特殊模式.我创建了一个实现 ApplicationListener 的简单类,它显示了我的问题.我正在使用 libGDX 的夜间版本.

I've got a question whether there is a special mode for grouping models in libGDX. I created a simple class that implements ApplicationListener which shows my problem. I am using the nightly build of libGDX.

我阅读了两个使用相同纹理的不同模型.该应用程序分别呈现每种类型的 250 个模型.这是渲染代码部分的样子:

I read two different models which use the same texture. The application renders respectively 250 models of every type. This is how the part of rendering code looks like:

  mModelBatch.begin(camera);
  for(int y=0; y<50; y++)
  {
     for(int x=-5; x<5; x++)
     {
        ModelInstance instance;
        if(x%2 == 0) instance = modelInstance1;
        else instance = modelInstance2;

        instance.transform.setToTranslation(x, 0, -y);
        mModelBatch.render(instance);
     }

  }

  mModelBatch.end(); 

我试图找到一个好的解决方案,所以我写了另一个测试代码:

I was trying to find a good solution so I wrote another test code:

public void getRenderables(Array<Renderable> renderables, Pool<Renderable> pool)
{
  for(int y=0; y<50; y++)
  {
     for(int x=-5; x<5; x++)
     {
        ModelInstance instance;
        if(x%2 == 0) instance = modelInstance1;
        else instance = modelInstance2;

        instance.transform.setToTranslation(x, y%3, -y);

        Renderable renderable = pool.obtain();
        renderable = instance.getRenderable(renderable);
        renderables.add(renderable);
     }
  }

}

如下图所示:

mModelBatch.begin(camera);      
mModelBatch.render(testRenderProvider);
mModelBatch.end();

但是它仍然给了我 13 FPS.同时,为了进行另一个测试,我在搅拌机中创建了与之前程序中相同的地图.接下来,我将所有内容归为一个对象(没有任何附加版本).通过这种方式,我创建了一个大小接近 1MB 的 BIG 对象,它可以在搅拌机的屏幕截图中看到.

However it still gave me 13 FPS. Meanwhile to make another test I created in blender the same map as it was in the previous program. Next I grouped everything in just one object (without any additional editions). This way I created one BIG object of size almost 1MB, it can be seen on the screenshot from blender.

我改变了测试程序,让它只绘制一个 BIG 对象:

I changed the test program in a way that it draws only this one BIG object:

mModelBatch.begin(camera);
      modelInstance1.transform.setToTranslation(0, 0, 0);
      mModelBatch.render(modelInstance1);
mModelBatch.end();

接下来我在手机(Sony XPeria Mini Pro - 与之前相同)和 iPod 5g 上启动了该程序,我已经获得了……60 FPS!

Next thing I did was that I launched the program on my phone (Sony XPeria Mini Pro - the same as previously) and iPod 5g and I've got... 60 FPS!

是否可以在一次绘制调用中渲染所有内容?

Is it possible to render everything in just one draw call?

推荐答案

问题解决了!我在一个非常低端的移动设备上达到了 60 FPS.游戏运行顺利.我发现了如何将多个网格合并到一个网格中,以便可以使用 VBO 机制.libGDX 中存在一个错误,导致网格复制方法无法用于多个网格.更改后,地图被划分为小扇区.每个扇区由具有相同 z 轴值的网格组成,如下图所示:

Problem solved! I achieved 60 FPS on a ver low-end mobile device. The game runs smootly. I found out how to merge multiple Meshes into one mesh so that VBO mechanizms can be used. There was a bug in libGDX which caused the Mesh-copying method unusable with multiple meshes. After the changes, the map is divided in small sectors. Each sector consists of meshes with the same z-axis value as can be seen on the following image:

VBO 机制非常有限,因此一次不能绘制很多顶点,这就是扇区必须相当小的原因.必须编写新的渲染器才能正确处理渲染.渲染器的各个部分正在动态合并网格(没有任何单独的工具,例如搅拌机).

The VBO mechanisms are very limitated, so not many vertices can be drawn at one-time that is why the sectors have to be rather small. New renderer had to be written to handle the rendering properly. And the parts of the renderer are merging the meshes dynamically (without any seperate tool, like eg. blender).

public static Mesh mergeMeshes(AbstractList<Mesh> meshes, AbstractList<Matrix4> transformations)
{
    if(meshes.size() == 0) return null;

    int vertexArrayTotalSize = 0;
    int indexArrayTotalSize = 0;

    VertexAttributes va = meshes.get(0).getVertexAttributes();
    int vaA[] = new int [va.size()];
    for(int i=0; i<va.size(); i++)
    {
        vaA[i] = va.get(i).usage;
    }

    for(int i=0; i<meshes.size(); i++)
    {
        Mesh mesh = meshes.get(i);
        if(mesh.getVertexAttributes().size() != va.size()) 
        {
            meshes.set(i, copyMesh(mesh, true, false, vaA));
        }

        vertexArrayTotalSize += mesh.getNumVertices() * mesh.getVertexSize() / 4;
        indexArrayTotalSize += mesh.getNumIndices();
    }

    final float vertices[] = new float[vertexArrayTotalSize];
    final short indices[] = new short[indexArrayTotalSize];

    int indexOffset = 0;
    int vertexOffset = 0;
    int vertexSizeOffset = 0;
    int vertexSize = 0;

    for(int i=0; i<meshes.size(); i++)
    {
        Mesh mesh = meshes.get(i);

        int numIndices = mesh.getNumIndices();
        int numVertices = mesh.getNumVertices();
        vertexSize = mesh.getVertexSize() / 4;
        int baseSize = numVertices * vertexSize;
        VertexAttribute posAttr = mesh.getVertexAttribute(Usage.Position);
        int offset = posAttr.offset / 4;
        int numComponents = posAttr.numComponents;

        { //uzupelnianie tablicy indeksow
            mesh.getIndices(indices, indexOffset);
            for(int c = indexOffset; c < (indexOffset + numIndices); c++)
            {
                indices[c] += vertexOffset;
            }
            indexOffset += numIndices;
        }

        mesh.getVertices(0, baseSize, vertices, vertexSizeOffset);
        Mesh.transform(transformations.get(i), vertices, vertexSize, offset, numComponents, vertexOffset, numVertices);
        vertexOffset += numVertices;
        vertexSizeOffset += baseSize;
    }

    Mesh result = new Mesh(true, vertexOffset, indices.length, meshes.get(0).getVertexAttributes());
    result.setVertices(vertices);
    result.setIndices(indices);
    return result;
} 

    public static Mesh copyMesh(Mesh meshToCopy, boolean isStatic, boolean removeDuplicates, final int[] usage) {
    // TODO move this to a copy constructor?
    // TODO duplicate the buffers without double copying the data if possible.
    // TODO perhaps move this code to JNI if it turns out being too slow.
    final int vertexSize = meshToCopy.getVertexSize() / 4;
    int numVertices = meshToCopy.getNumVertices();
    float[] vertices = new float[numVertices * vertexSize];
    meshToCopy.getVertices(0, vertices.length, vertices);
    short[] checks = null;
    VertexAttribute[] attrs = null;
    int newVertexSize = 0;
    if (usage != null) {
        int size = 0;
        int as = 0;
        for (int i = 0; i < usage.length; i++)
            if (meshToCopy.getVertexAttribute(usage[i]) != null) {
                size += meshToCopy.getVertexAttribute(usage[i]).numComponents;
                as++;
            }
        if (size > 0) {
            attrs = new VertexAttribute[as];
            checks = new short[size];
            int idx = -1;
            int ai = -1;
            for (int i = 0; i < usage.length; i++) {
                VertexAttribute a = meshToCopy.getVertexAttribute(usage[i]);
                if (a == null)
                    continue;
                for (int j = 0; j < a.numComponents; j++)
                    checks[++idx] = (short)(a.offset/4 + j);
                attrs[++ai] = new VertexAttribute(a.usage, a.numComponents, a.alias);
                newVertexSize += a.numComponents;
            }
        }
    }
    if (checks == null) {
        checks = new short[vertexSize];
        for (short i = 0; i < vertexSize; i++)
            checks[i] = i;
        newVertexSize = vertexSize;
    }

    int numIndices = meshToCopy.getNumIndices();
    short[] indices = null; 
    if (numIndices > 0) {
        indices = new short[numIndices];
        meshToCopy.getIndices(indices);
        if (removeDuplicates || newVertexSize != vertexSize) {
            float[] tmp = new float[vertices.length];
            int size = 0;
            for (int i = 0; i < numIndices; i++) {
                final int idx1 = indices[i] * vertexSize;
                short newIndex = -1;
                if (removeDuplicates) {
                    for (short j = 0; j < size && newIndex < 0; j++) {
                        final int idx2 = j*newVertexSize;
                        boolean found = true;
                        for (int k = 0; k < checks.length && found; k++) {
                            if (tmp[idx2+k] != vertices[idx1+checks[k]])
                                found = false;
                        }
                        if (found)
                            newIndex = j;
                    }
                }
                if (newIndex > 0)
                    indices[i] = newIndex;
                else {
                    final int idx = size * newVertexSize;
                    for (int j = 0; j < checks.length; j++)
                        tmp[idx+j] = vertices[idx1+checks[j]];
                    indices[i] = (short)size;
                    size++;
                }
            }
            vertices = tmp;
            numVertices = size;
        }
    }

    Mesh result;
    if (attrs == null)
        result = new Mesh(isStatic, numVertices, indices == null ? 0 : indices.length, meshToCopy.getVertexAttributes());
    else
        result = new Mesh(isStatic, numVertices, indices == null ? 0 : indices.length, attrs);
    result.setVertices(vertices, 0, numVertices * newVertexSize);
    result.setIndices(indices);
    return result;
}

这对于尝试在 libGDX 中编写自己的 3D 游戏的人来说非常有用.如果没有这种机制,就不可能写出比几个模型更复杂的东西.

This can be very useful for the people trying to write their own 3D games in libGDX. Without this mechanizm it is rather impossible to write anything more compilcated than a few models.

这篇关于libGDX 中的慢速模型批量渲染的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆