Слияние мешей opengl и libgdx

Я использую следующий код для объединения нескольких сеток:

import java.util.ArrayList;
import java.util.Arrays;

import org.obsgolem.crystalia.gfx.Renderer;

import com.badlogic.gdx.graphics.Mesh;
import com.badlogic.gdx.graphics.VertexAttribute;
import com.badlogic.gdx.graphics.VertexAttributes.Usage;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.Array;
import java.util.*;

public class MeshBatch
{

    private final static VertexAttribute[] attributeConfig = new VertexAttribute[]{
        new VertexAttribute(Usage.Position, 3, "a_position"),
        new VertexAttribute(Usage.ColorPacked, 4, "a_color"),
        new VertexAttribute(Usage.Normal, 3, "a_normal")};
    private final static int VERTEX_SIZE = 3 + 1 + 3;

    private Mesh m;
    private List<Float> vertices = new ArrayList<Float>();
    private List<Short> indices = new ArrayList<Short>();

    public void addMesh(float[] vert, short[] ind)
    {
        int offset = (vertices.size() / VERTEX_SIZE);

        //You have to throw an exception when you get over the limit of short indices
        if (offset + vert.length / VERTEX_SIZE > Short.MAX_VALUE) {
            throw new RuntimeException("blablabla");
        }

        for (short i : addOffset(ind, offset)) {
            indices.add(i);
        }

        for (float v : vert) {
            vertices.add(v);
        }
    }

    public short[] addOffset(short[] ind, int offset)
    {
        short[] indarr = new short[ind.length];
        for (int i = 0; i < ind.length; ++i) {
            //Do you really need this check? You are the only one using this code
            //so make sure that you never provide a null value. If you really want to have a chekc throw an exception instead
            short value = ind[i];//ind[i] == null ? 0 : ind[i];
            indarr[i] = (short) (value + offset);
        }
        return indarr;
    }


    public void end()
    {
        m = new Mesh(false, vertices.size(), indices.size(), attributeConfig);
        m.setVertices(Renderer.makeFloatArray(vertices));
        m.setIndices(Renderer.makeShortArray(indices));
    }

    public void render()
    {
        Renderer.getInstance().render(m);
    }
}

Однако когда я рисую с помощью этого класса, я получаю странные эффекты освещения. С этим мешем свет ярче для всех других объектов, в то время как тот, который визуализируется, имеет освещение, которое выглядит плоским. Обычным способом (отдельные сетки для каждого объекта) я получаю приятное гладкое освещение. Вот два скриншота, которые я сделал:

С объединенной сеткой:
Освещение объединенной сетки

Без объединенной сетки:
Гладкое попиксельное освещение без объединенной сетки

Что вызывает эту проблему и как она может повлиять на освещение другого меша? Мои вершины отправляются в правильном формате (3 поплавка для вершин, 1 для цвета и 3 для нормалей). Мои индексы тоже работают. Проблема возникает только тогда, когда сетка фактически визуализируется. Без него освещение работает отлично. Я думаю, что проблема как-то связана с нормалями, но я не могу понять, что это может быть за проблема.

Редактировать: кажется, проблема решена. Когда я переключил плитки с использования отдельных сеток на сетку, освещение исправилось само. Как такое могло произойти?


person jbills    schedule 23.05.2012    source источник
comment
Я понятия не имею о libgdx, поэтому не хочу отвечать. Тем не менее, несколько вещей, которые могут помочь: (1) Плоское затенение означает, что все нормали лица указывают в одном направлении. Так что попробуйте распечатать их и посмотреть, так ли это. (2) Также может возникнуть проблема с расчетом расстояния до источника света. Возможно, все ваши нормали направлены в одном направлении, но вы вычисляете расстояние до источника света, чтобы оно было одинаковым для всех вершин. Я понятия не имею, какую модель освещения вы используете, поэтому я не могу больше помочь.   -  person Max    schedule 23.05.2012
comment
@Max Я использую попиксельное освещение, только рассеянное. Каждая сетка представляет собой куб. Разве грани куба не должны иметь нормали, указывающие в одном направлении?   -  person jbills    schedule 23.05.2012
comment
Здесь недостаточно информации, чтобы сказать что-то полезное. Хотя кажется странным, что вы отправляете «4» в VertexAttribute для цвета, если вы утверждаете, что он использует одно число с плавающей запятой, но я понятия не имею, что делает этот класс.   -  person Tim    schedule 23.05.2012
comment
Я исправил проблему с объединенной сеткой, влияющей на другое освещение. Вместо того, чтобы нормализовать расстояние от источника света, я теперь делю расстояние от источника света на sqrt длины этой переменной. Кажется, это помогло с этой проблемой, но не с проблемой освещения meshbatch.   -  person jbills    schedule 23.05.2012
comment
@tim Colorpacked - это 4 поплавка, упакованных в один поплавок. Этот кусок кода работает, я это знаю.   -  person jbills    schedule 23.05.2012
comment
Не обращайте внимания на мой комментарий по поводу исправления. Это было неправильное решение.   -  person jbills    schedule 23.05.2012
comment
@jbills: Мне показалось, что это два квадроцикла. Что касается вашего вопроса о нормалях кубических граней: это действительно зависит. Вы можете создать куб, в котором все нормали грани указывают перпендикулярно грани. Это сделало бы куб очень плоско заштрихованным. Обычно люди делают нормали куба радиально направленными наружу от центра куба. Это делает более приятное затенение. В любом случае, для общей сетки (которую вы могли бы нарисовать, например, в Blender), нормали рассчитываются как средние значения всех граней, примыкающих к нормали. Это делает лучшее освещение. Надеюсь, это поможет.   -  person Max    schedule 23.05.2012
comment
Хорошо, но это не помогает с моей первоначальной проблемой. Только комбинированная сетка имеет эту проблему.   -  person jbills    schedule 23.05.2012
comment
@jbills: проверьте (распечатайте) комбинированные нормали сетки. Описание модели освещения у вас странное, расстояние до источника света должно быть разностью положения источника света и координат положения вершины.   -  person Max    schedule 24.05.2012
comment
Вот распечатка вершин объединенного куба: pastebin.com/bLzfTRu1 Кажется, я исправил проблема однако. При объединении тайлов в пакет сетки освещение возвращается к гладкому. Что мне сейчас нужно, так это объяснение того, как это может работать.   -  person jbills    schedule 24.05.2012
comment
@jbills: я точно не знаю, что вы там печатаете, но это очень подозрительно (-1.4545402E38). Это очень очень большое число. Если нормали напечатаны правильно, значит, что-то не так с вашими нормалями...   -  person Max    schedule 25.05.2012
comment
Я не могу легко распечатать нормали отдельных мешей. Я распечатываю чередующиеся вершины, цвета и нормали. Вершины идут первыми, за ними идет упакованный цвет (большой поплавок), за которым следуют нормали. Я просмотрел, и они кажутся правильными.   -  person jbills    schedule 25.05.2012


Ответы (1)


Кажется, это хорошо работает для спрашивающего этого вопроса:

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;
}
person Lestat    schedule 09.01.2014