LibGDX 3D提高了性能

 网吧b国漫救星 发布于 2023-01-11 10:37

我正在制作3D游戏.

游戏需要大约100个立方体才能工作,所有立方体都是动态的.

我真的不知道这样的游戏需要多少性能,但我正在使用带有Mali-400 MP2 GPU,1 GB RAM,1.5 GHz双核的平板电脑进行测试.我知道在一个网格中渲染所有立方体,但是我不能分别移动所有这些立方体.

这个设置给了我一个非常摇摆的fps.跳跃在20到50之间,大多在30以下.(在模拟器10-15中)

当游戏开始时,我构建了一个ModelInstances的arraylist,他们都使用相同的模型.

model = new ModelBuilder().createBox(1f, 1f, 1f, new Material(ColorAttribute.createDiffuse(Color.GREEN)), Usage.Position | Usage.Normal);

// width,height,length = 5, creating a total of 125 cubes

for (int x = 0; x < width; x++) {
    for (int y = 0; y < height; y++) {
        for (int z = 0; z < length; z++) {
            if (this.map[x][y][z] > 0) {
                this.modelInstances.add(instance = new ModelInstance(model));
                instance.transform.translate(x, -(y * 1.5f), -z);
            }
        }
    }
}

渲染:

Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
mb.begin(camera3D);
mb.render(this.modelInstances);
mb.end();

相机初始化:

camera3D = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
camera3D.position.set(0f, 8f, 5f);
camera3D.lookAt(0, 0, 0);
camera3D.near = 1f;
camera3D.far = 300f;
camera3D.update();

我该怎么做才能提高性能?

对于像这样的游戏,平板电脑是否太弱了?或问题是代码?

编辑:

用webGL进行了一些测试,同样的平板电脑,使用铬,渲染125立方体:稳定40-50 fps

1 个回答
  • 您可以将所有多维数据集批量处理为单个Model和ModelInstance,如下所示:

    int width = 5;
    int height = 5;
    int length = 5;
    int numCubes = width*height*length;
    
    ModelBuilder mb = new ModelBuilder();
    mb.begin();
    MeshPartBuilder mpb = mb.part("cubes", GL20.GL_TRIANGLES, (Usage.Position | Usage.Normal), new Material(ColorAttribute.createDiffuse(Color.GREEN)));
    for (int i=0; i<numCubes; i++){
        mpb.box(1, 1, 1); 
    }
    Model model = mb.end();
    mBatchedCubesModelInstance = new ModelInstance(model);
    

    但棘手的部分是能够将每个立方体移动到不同的位置,并能够独立操作它们.

    这是一个Cube类,可以从上面的模型中操作各个多维数据集.我认为理论上它应该适用于您创建的每个立方体使用24个唯一顶点的网格,因此您可以添加纹理坐标.

    这也依赖于position,然后normal始终是mesh的前两个Usage属性,所以希望在libGDX的Mesh类中成立.

    这基本上通过跟踪它在基础网格中的索引号(它是多维数据集编号)来工作,因此它可以选择顶点数组中需要更新的顶点.顶点需要每帧复制到网格中.

    public class Cube {
    
        private int index;
        int vertexFloatSize;
        int posOffset;
        int norOffset;
        boolean hasColor;
        int colOffset;
        private Vector3 position = new Vector3();
        private Matrix4 rotationTransform = new Matrix4().idt();
        private Color color = new Color();
        public float halfWidth, halfHeight, halfDepth;
        private boolean transformDirty = false;
        private boolean colorDirty = false;
    
        static final Vector3 CORNER000 = new Vector3();
        static final Vector3 CORNER010 = new Vector3();
        static final Vector3 CORNER100 = new Vector3();
        static final Vector3 CORNER110 = new Vector3();
        static final Vector3 CORNER001 = new Vector3();
        static final Vector3 CORNER011 = new Vector3();
        static final Vector3 CORNER101 = new Vector3();
        static final Vector3 CORNER111 = new Vector3();
    
        static final Vector3[] FACE0 = {CORNER000, CORNER100, CORNER110, CORNER010};
        static final Vector3[] FACE1 = {CORNER101, CORNER001, CORNER011, CORNER111};
        static final Vector3[] FACE2 = {CORNER000, CORNER010, CORNER011, CORNER001};
        static final Vector3[] FACE3 = {CORNER101, CORNER111, CORNER110, CORNER100};
        static final Vector3[] FACE4 = {CORNER101, CORNER100, CORNER000, CORNER001};
        static final Vector3[] FACE5 = {CORNER110, CORNER111, CORNER011, CORNER010};
        static final Vector3[][] FACES = {FACE0, FACE1, FACE2, FACE3, FACE4, FACE5};
    
        static final Vector3 NORMAL0 = new Vector3();
        static final Vector3 NORMAL1 = new Vector3();
        static final Vector3 NORMAL2 = new Vector3();
        static final Vector3 NORMAL3 = new Vector3();
        static final Vector3 NORMAL4 = new Vector3();
        static final Vector3 NORMAL5 = new Vector3();
        static final Vector3[] NORMALS = {NORMAL0, NORMAL1, NORMAL2, NORMAL3, NORMAL4, NORMAL5};
    
        public Cube(float x, float y, float z, float width, float height, float depth, int index, 
                VertexAttributes vertexAttributes, float[] meshVertices){
            position.set(x,y,z);
            this.halfWidth = width/2;
            this.halfHeight = height/2;
            this.halfDepth = depth/2;
            this.index = index;
    
    
            vertexFloatSize = vertexAttributes.vertexSize/4; //4 bytes per float
            posOffset = getVertexAttribute(Usage.Position, vertexAttributes).offset/4;
            norOffset = getVertexAttribute(Usage.Normal, vertexAttributes).offset/4;
    
            VertexAttribute colorAttribute = getVertexAttribute(Usage.Color, vertexAttributes);
            hasColor = colorAttribute!=null;
            if (hasColor){
                colOffset = colorAttribute.offset/4;
                this.setColor(Color.WHITE, meshVertices);
            }
            transformDirty = true;
        }
    
        public void setIndex(int index){
            this.index = index;
            transformDirty = true;
            colorDirty = true;
        }
    
        /**
         * Call this after moving and/or rotating.
         */
        public void update(float[] meshVertices){
            if (colorDirty && hasColor){
                for (int faceIndex= 0; faceIndex<6; faceIndex++){
                    int baseVertexIndex = (index*24 + faceIndex*4)*vertexFloatSize;//24 unique vertices per cube, 4 unique vertices per face
                    for (int cornerIndex=0; cornerIndex<4; cornerIndex++){
                        int vertexIndex = baseVertexIndex + cornerIndex*vertexFloatSize + colOffset;
                        meshVertices[vertexIndex] = color.r;
                        meshVertices[++vertexIndex] = color.g;
                        meshVertices[++vertexIndex] = color.b;
                        meshVertices[++vertexIndex] = color.a;
                    }
                }
                colorDirty = false;
            }
    
    
            if (!transformDirty){
                return;
            }
            transformDirty = false;
    
            CORNER000.set(-halfWidth,-halfHeight,-halfDepth).rot(rotationTransform).add(position);
            CORNER010.set(-halfWidth,halfHeight,-halfDepth).rot(rotationTransform).add(position);
            CORNER100.set(halfWidth,-halfHeight,-halfDepth).rot(rotationTransform).add(position);
            CORNER110.set(halfWidth,halfHeight,-halfDepth).rot(rotationTransform).add(position);
            CORNER001.set(-halfWidth,-halfHeight,halfDepth).rot(rotationTransform).add(position);
            CORNER011.set(-halfWidth,halfHeight,halfDepth).rot(rotationTransform).add(position);
            CORNER101.set(halfWidth,-halfHeight,halfDepth).rot(rotationTransform).add(position);
            CORNER111.set(halfWidth,halfHeight,halfDepth).rot(rotationTransform).add(position);
    
            NORMAL0.set(0,0,-1).rot(rotationTransform);
            NORMAL1.set(0,0,1).rot(rotationTransform);
            NORMAL2.set(-1,0,0).rot(rotationTransform);
            NORMAL3.set(1,0,0).rot(rotationTransform);
            NORMAL4.set(0,-1,0).rot(rotationTransform);
            NORMAL5.set(0,1,0).rot(rotationTransform);
    
            for (int faceIndex= 0; faceIndex<6; faceIndex++){
                int baseVertexIndex = (index*24 + faceIndex*4)*vertexFloatSize;//24 unique vertices per cube, 4 unique vertices per face
                for (int cornerIndex=0; cornerIndex<4; cornerIndex++){
                    int vertexIndex = baseVertexIndex + cornerIndex*vertexFloatSize + posOffset;
                    meshVertices[vertexIndex] = FACES[faceIndex][cornerIndex].x;
                    meshVertices[++vertexIndex] = FACES[faceIndex][cornerIndex].y;
                    meshVertices[++vertexIndex] = FACES[faceIndex][cornerIndex].z;
    
                    vertexIndex = baseVertexIndex + cornerIndex*vertexFloatSize + norOffset;
                    meshVertices[vertexIndex] = NORMALS[faceIndex].x;
                    meshVertices[++vertexIndex] = NORMALS[faceIndex].y;
                    meshVertices[++vertexIndex] = NORMALS[faceIndex].z;
                }
            }
        }
    
        public Cube setColor(Color color){
            if (hasColor){
                this.color.set(color);
                colorDirty = true;
            }
            return this;
        }
    
        public Cube translate(float x, float y, float z){
            position.add(x,y,z);
            transformDirty = true;
            return this;
        }
    
        public Cube translateTo(float x, float y, float z){
            position.set(x,y,z);
            transformDirty = true;
            return this;
        }
    
        public Cube rotate(float axisX, float axisY, float axisZ, float degrees){
            rotationTransform.rotate(axisX, axisY, axisZ, degrees);
            transformDirty = true;
            return this;
        }
    
        public Cube rotateTo(float axisX, float axisY, float axisZ, float degrees){
            rotationTransform.idt();
            rotationTransform.rotate(axisX, axisY, axisZ, degrees);
            transformDirty = true;
            return this;
        }
    
        public VertexAttribute getVertexAttribute (int usage, VertexAttributes attributes) {
            int len = attributes.size();
            for (int i = 0; i < len; i++)
                if (attributes.get(i).usage == usage) return attributes.get(i);
    
            return null;
        }
    }
    

    要使用它,首先获取网格参考并创建立方体:

        mBatchedCubesMesh = model.meshes.get(0);
        VertexAttributes vertexAttributes = mBatchedCubesMesh.getVertexAttributes();
        int vertexFloatSize = vertexAttributes .vertexSize / 4; //4 bytes per float
        mBatchedCubesVertices = new float[numCubes * 24 * vertexFloatSize]; //24 unique vertices per cube
        mBatchedCubesMesh.getVertices(mBatchedCubesVertices);
    
        mBatchedCubes = new Array<Cube>(numCubes);
        int cubeNum = 0;
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                for (int z = 0; z < length; z++) {
                    mBatchedCubes.add(new Cube((x-(width/2f))*1.5f, -((y-(height/2f)) * 1.5f), -(z-(length/2f))*1.5f, 1,1,1, cubeNum++, vertexAttributes, mBatchedCubesVertices ));
                }
            }
        }
    

    然后在你的render方法中:

    mBatchedCubes.get(0).rotate(1, 1, 1, 180*delta); //example manipulation of a single cube
    
    for (Cube cube : mBatchedCubes){ //must update any changed cubes. 
        cube.update(mBatchedCubesVertices);
    }
    mBatchedCubesMesh.setVertices(mBatchedCubesVertices); //apply changes to mesh
    
    ...
    
    modelBatch.begin(camera);
    modelBatch.render(mBatchedCubesModelInstance);
    modelBatch.end();
    

    现在CPU顶点操作不如着色器顶点操作效率高,因此如果您在每个帧周围移动所有多维数据集,这可能会成为CPU绑定.如果你没有旋转它们,可能有助于为旋转创建一个单独的"脏"变量,并且只在必要时在更新方法中旋转.


    编辑:从这个问题更新

    如果您想要具有透明度,那么立方体必须是可排序的,因此可以从远到远对它们进行排序以进行绘制.它们的index值必须更新为新的顺序,因为它们是按顺序排列到网格中的.这是一个支持排序的Cube类(由于多维数据集可能会移动到网格的不同部分,因此现在必须单独跟踪颜色).

    public class Cube implements Comparable<Cube>{
    
        private int index;
        int vertexFloatSize;
        int posOffset;
        int norOffset;
        boolean hasColor;
        int colOffset;
        private Vector3 position = new Vector3();
        private Matrix4 rotationTransform = new Matrix4().idt();
        public float halfWidth, halfHeight, halfDepth;
        private boolean transformDirty = false;
        private boolean colorDirty = false;
        private Color color = new Color();
        float camDistSquared;
    
        static final Vector3 CORNER000 = new Vector3();
        static final Vector3 CORNER010 = new Vector3();
        static final Vector3 CORNER100 = new Vector3();
        static final Vector3 CORNER110 = new Vector3();
        static final Vector3 CORNER001 = new Vector3();
        static final Vector3 CORNER011 = new Vector3();
        static final Vector3 CORNER101 = new Vector3();
        static final Vector3 CORNER111 = new Vector3();
    
        static final Vector3[] FACE0 = {CORNER000, CORNER100, CORNER110, CORNER010};
        static final Vector3[] FACE1 = {CORNER101, CORNER001, CORNER011, CORNER111};
        static final Vector3[] FACE2 = {CORNER000, CORNER010, CORNER011, CORNER001};
        static final Vector3[] FACE3 = {CORNER101, CORNER111, CORNER110, CORNER100};
        static final Vector3[] FACE4 = {CORNER101, CORNER100, CORNER000, CORNER001};
        static final Vector3[] FACE5 = {CORNER110, CORNER111, CORNER011, CORNER010};
        static final Vector3[][] FACES = {FACE0, FACE1, FACE2, FACE3, FACE4, FACE5};
    
        static final Vector3 NORMAL0 = new Vector3();
        static final Vector3 NORMAL1 = new Vector3();
        static final Vector3 NORMAL2 = new Vector3();
        static final Vector3 NORMAL3 = new Vector3();
        static final Vector3 NORMAL4 = new Vector3();
        static final Vector3 NORMAL5 = new Vector3();
        static final Vector3[] NORMALS = {NORMAL0, NORMAL1, NORMAL2, NORMAL3, NORMAL4, NORMAL5};
    
        public Cube(float x, float y, float z, float width, float height, float depth, int index, 
            VertexAttributes vertexAttributes, float[] meshVertices){
        position.set(x,y,z);
        this.halfWidth = width/2;
        this.halfHeight = height/2;
        this.halfDepth = depth/2;
        this.index = index;
    
    
        vertexFloatSize = vertexAttributes.vertexSize/4; //4 bytes per float
        posOffset = getVertexAttribute(Usage.Position, vertexAttributes).offset/4;
        norOffset = getVertexAttribute(Usage.Normal, vertexAttributes).offset/4;
    
        VertexAttribute colorAttribute = getVertexAttribute(Usage.Color, vertexAttributes);
        hasColor = colorAttribute!=null;
        if (hasColor){
            colOffset = colorAttribute.offset/4;
            this.setColor(Color.WHITE, meshVertices);
        }
        transformDirty = true;
        }
    
        public void updateCameraDistance(Camera cam){
        camDistSquared = cam.position.dst2(position);
        }
    
        /**
         * Call this after moving and/or rotating.
         */
        public void update(float[] meshVertices){
    
        if (transformDirty){
            transformDirty = false;
    
            CORNER000.set(-halfWidth,-halfHeight,-halfDepth).rot(rotationTransform).add(position);
            CORNER010.set(-halfWidth,halfHeight,-halfDepth).rot(rotationTransform).add(position);
            CORNER100.set(halfWidth,-halfHeight,-halfDepth).rot(rotationTransform).add(position);
            CORNER110.set(halfWidth,halfHeight,-halfDepth).rot(rotationTransform).add(position);
            CORNER001.set(-halfWidth,-halfHeight,halfDepth).rot(rotationTransform).add(position);
            CORNER011.set(-halfWidth,halfHeight,halfDepth).rot(rotationTransform).add(position);
            CORNER101.set(halfWidth,-halfHeight,halfDepth).rot(rotationTransform).add(position);
            CORNER111.set(halfWidth,halfHeight,halfDepth).rot(rotationTransform).add(position);
    
            NORMAL0.set(0,0,-1).rot(rotationTransform);
            NORMAL1.set(0,0,1).rot(rotationTransform);
            NORMAL2.set(-1,0,0).rot(rotationTransform);
            NORMAL3.set(1,0,0).rot(rotationTransform);
            NORMAL4.set(0,-1,0).rot(rotationTransform);
            NORMAL5.set(0,1,0).rot(rotationTransform);
    
            for (int faceIndex= 0; faceIndex<6; faceIndex++){
            int baseVertexIndex = (index*24 + faceIndex*4)*vertexFloatSize;//24 unique vertices per cube, 4 unique vertices per face
            for (int cornerIndex=0; cornerIndex<4; cornerIndex++){
                int vertexIndex = baseVertexIndex + cornerIndex*vertexFloatSize + posOffset;
                meshVertices[vertexIndex] = FACES[faceIndex][cornerIndex].x;
                meshVertices[++vertexIndex] = FACES[faceIndex][cornerIndex].y;
                meshVertices[++vertexIndex] = FACES[faceIndex][cornerIndex].z;
    
                vertexIndex = baseVertexIndex + cornerIndex*vertexFloatSize + norOffset;
                meshVertices[vertexIndex] = NORMALS[faceIndex].x;
                meshVertices[++vertexIndex] = NORMALS[faceIndex].y;
                meshVertices[++vertexIndex] = NORMALS[faceIndex].z;
            }
            }
        }
    
        if (colorDirty){
            colorDirty = false;
    
            for (int faceIndex= 0; faceIndex<6; faceIndex++){
            int baseVertexIndex = (index*24 + faceIndex*4)*vertexFloatSize;//24 unique vertices per cube, 4 unique vertices per face
            for (int cornerIndex=0; cornerIndex<4; cornerIndex++){
                int vertexIndex = baseVertexIndex + cornerIndex*vertexFloatSize + colOffset;
                meshVertices[vertexIndex] = color.r;
                meshVertices[++vertexIndex] = color.g;
                meshVertices[++vertexIndex] = color.b;
                meshVertices[++vertexIndex] = color.a;
            }
            }
        }
        }
    
        public Cube setColor(Color color, float[] meshVertices){
        if (hasColor){
            this.color.set(color);
            colorDirty = true;
    
        }
        return this;
        }
    
        public void setIndex(int index){
        if (this.index != index){
            transformDirty = true;
            colorDirty = true;
            this.index = index;
        }
        }
    
        public Cube translate(float x, float y, float z){
        position.add(x,y,z);
        transformDirty = true;
        return this;
        }
    
        public Cube translateTo(float x, float y, float z){
        position.set(x,y,z);
        transformDirty = true;
        return this;
        }
    
        public Cube rotate(float axisX, float axisY, float axisZ, float degrees){
        rotationTransform.rotate(axisX, axisY, axisZ, degrees);
        transformDirty = true;
        return this;
        }
    
        public Cube rotateTo(float axisX, float axisY, float axisZ, float degrees){
        rotationTransform.idt();
        rotationTransform.rotate(axisX, axisY, axisZ, degrees);
        transformDirty = true;
        return this;
        }
    
        public VertexAttribute getVertexAttribute (int usage, VertexAttributes attributes) {
        int len = attributes.size();
        for (int i = 0; i < len; i++)
            if (attributes.get(i).usage == usage) return attributes.get(i);
    
        return null;
        }
    
        @Override
        public int compareTo(Cube other) {
        //This is a simple sort based on center point distance to camera. A more 
        //sophisticated sorting method might be required if the cubes are not all the same 
        //size (such as calculating which of the 8 vertices is closest to the camera
        //and using that instead of the center point).
        if (camDistSquared>other.camDistSquared)
            return -1;
        return camDistSquared<other.camDistSquared ? 1 : 0;
        }
    }
    

    以下是对它们进行排序的方法:

    for (Cube cube : mBatchedCubes){
        cube.updateCameraDistance(camera);
    }
    mBatchedCubes.sort();
    int index = 0;
    for (Cube cube : mBatchedCubes){
        cube.setIndex(index++);
    }
    

    2023-01-11 10:38 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有