Почему текстуры отображаются неправильно при использовании индексированного рендеринга (glDrawElements) при попытке загрузить их из файла wavefront .obj? - PullRequest
0 голосов
/ 12 июня 2018

Введение

Я создаю простой анализатор файлов .obj wavefront.Мне удалось заставить его читать файл, хранить его содержимое (позиции вершин, координаты вершин, нормали вершин (пока их не используют) и информацию о многоугольных элементах лица (например, 5/2/3)).Эти данные затем передаются в класс (называемый GameEntity), и оттуда данные используются для визуализации этой конкретной сущности (в данном случае куба) на экране внутри цикла рендеринга, используя glDrawElements в режиме GL_TRIANGLES .Однако текстуры отображаются неправильно .

Исходный код

OBJLoader.java

public class OBJLoader {    
    /**
     * This method loads a model represented by a wavefront .obj file from resources/models using
     * the specified name of the file (without the .obj extension) and a full path to the texture used
     * by the model. It passes the information (vertex positions, texture coordinates, indices)
     * obtained from the .obj file to the GameEntity constructor.
     * @param fileName
     * @param texturePath
     * @return
     * @throws Exception
     */
    public static GameEntity loadObjModel(String fileName, String texturePath) throws Exception {
        double start = System.nanoTime();

        List<Vector3f> vertices = null;
        List<Vector2f> textures = null;
        List<Vector3f> normals = null;
        List<Integer> indices = null;

        String line;

        float[] vertexPosArray = null;
        float[] texturesArray = null;
        float[] normalsArray = null;
        int[] indicesArray = null;

        try {
            FileReader fr = new FileReader(new File("resources/models/" + fileName + ".obj"));
            BufferedReader br = new BufferedReader(fr);
            vertices = new ArrayList<>();
            textures = new ArrayList<>();
            normals = new ArrayList<>();
            indices = new ArrayList<>();

            while((line = br.readLine()) != null) {

                if (!line.equals("") || !line.startsWith("#")) {
                    String[] splitLine = line.split(" ");

                    switch(splitLine[0]) {
                    case "v":
                        Vector3f vertex = new Vector3f(Float.parseFloat(splitLine[1]), Float.parseFloat(splitLine[2]), Float.parseFloat(splitLine[3]));
                        vertices.add(vertex);
                        System.out.println("[OBJLoader.loadObjModel]: Vertex " + vertex.toString() + " has been added to vertices from " + fileName);
                        break;
                    case "vt":
                        Vector2f texture = new Vector2f(Float.parseFloat(splitLine[1]), Float.parseFloat(splitLine[2]));
                        textures.add(texture);
                        System.out.println("[OBJLoader.loadObjModel]: Texture coordinate [" + texture.x +  ", " + texture.y  + "] has been added to textures from " + fileName);
                        break;
                    case "vn":
                        Vector3f normal = new Vector3f(Float.parseFloat(splitLine[1]), Float.parseFloat(splitLine[2]), Float.parseFloat(splitLine[3]));
                        normals.add(normal);
                        System.out.println("[OBJLoader.loadObjModel]: Normal " + normal + " has been added to normals from " + fileName);
                        break;
                    }
                }
            }

            int numVertices = vertices.size();
            System.out.println("[OBJLoader.loadObjModel]: numVertices = " + numVertices);
            texturesArray = new float[numVertices*2];
            System.out.println("[OBJLoader.loadObjModel]: length of texturesArray = " + texturesArray.length);
            normalsArray = new float[numVertices*3];

            br.close(); //find a better way to start a file again
            br = new BufferedReader(new FileReader("resources/models/" + fileName + ".obj"));

            while((line = br.readLine()) != null) {
                if (line.startsWith("f")) {
                    System.out.println("    [OBJLoader.loadObjModel]: Found line starting with f!"); 
                    String[] splitLine = line.split(" ");

                    //f should be omitted, therefore not starting at index 0
                    String[] v1 = splitLine[1].split("/");
                    String[] v2 = splitLine[2].split("/");
                    String[] v3 = splitLine[3].split("/");

                    System.out.println("        v1 | " + v1[0] + ", " + v1[1] + ", " + v1[2]);
                    System.out.println("        v2 | " + v2[0] + ", " + v2[1] + ", " + v2[2]);
                    System.out.println("        v3 | " + v3[0] + ", " + v3[1] + ", " + v3[2]);

                    processVertex(v1, indices, textures, normals, texturesArray, normalsArray);
                    processVertex(v2, indices, textures, normals, texturesArray, normalsArray);
                    processVertex(v3, indices, textures, normals, texturesArray, normalsArray);
                }
            }
            br.close();

        } catch (Exception e) {
            System.err.println("[OBJLoader.loadObjModel]: Error loading obj model!");
            e.printStackTrace();
        }

        vertexPosArray = new float[vertices.size()*3];
        indicesArray = new int[indices.size()];

        int i = 0;
        for(Vector3f vertex : vertices) {
            vertexPosArray[i++] = vertex.x;
            vertexPosArray[i++] = vertex.y;
            vertexPosArray[i++] = vertex.z;
        }

        for(int j = 0; j<indices.size(); j++) {
            indicesArray[j] = indices.get(j);
        }

        double end = System.nanoTime();
        double delta = (end - start) / 1000_000;
        System.out.println("[OBJLoader.loadObjModel]: Vertices array of " + fileName + ": ");
        System.out.println("[OBJLoader.loadObjModel]: It took " + delta + " milliseconds to load " + fileName);

        System.out.println("[OBJLoader.loadObjModel]: Ordered vertex position array: " + ArrayUtils.getFloatArray(vertexPosArray));
        System.out.println("[OBJLoader.loadObjModel]: Ordererd texture coordinates array: " + ArrayUtils.getFloatArray(texturesArray));
        System.out.println("[OBJLoader.loadObjModel]: Ordererd indices array: " + ArrayUtils.getIntArray(indicesArray));

        return new GameEntity(vertexPosArray, indicesArray, texturesArray, texturePath);
    }

    /**
     * The input to this method is vertex data as a String array, which is used to determine how to
     * arrange texture coordinate and normal vector data (this data is associated with each vertex position)
     * into the correct order in the texture and normals array
     * @param vertexData
     * @param indices
     * @param textrues
     * @param normals
     * @param textureArray
     * @param normalsArray
     */
    private static void processVertex(String[] vertexData, List<Integer> indices, List<Vector2f> textures,
            List<Vector3f> normals, float[] textureArray, float[] normalsArray) {
        int currentVertexPointer = Integer.parseInt(vertexData[0]) - 1;
        System.out.println("[OBJLoader.processVertex]: currentVertexPointer = " + currentVertexPointer);
        indices.add(currentVertexPointer);
        System.out.println("[OBJLoader.processVertex]: Adding " + currentVertexPointer + " to indices!");

        Vector2f currentTex = textures.get(Integer.parseInt(vertexData[1]) - 1);
        textureArray[currentVertexPointer*2] = currentTex.x;
        textureArray[currentVertexPointer*2 + 1] = 1.0f - currentTex.y;
        System.out.println("[OBJLoader.processVertex]: Added vt " + currentTex.x + " to index " + currentVertexPointer*2 + 
                " and vt " + (1.0f - currentTex.y) + " to index " + (currentVertexPointer*2+1) + " of the textureArray");

        Vector3f currentNorm = normals.get(Integer.parseInt(vertexData[2]) - 1);
        normalsArray[currentVertexPointer*3] = currentNorm.x;
        normalsArray[currentVertexPointer*3 + 1] = currentNorm.y;
        normalsArray[currentVertexPointer*3 + 2] = currentNorm.z;
    }
}

Конструктор GameEntity:

    /**
 * Creates a new textured GameEntity
 * @param vPositions The vertex coordinates of a model
 * @param indices The indices of a model (in which order should the vertices be bound by OpenGL?)
 * @param textureCoordinates The coordinates of a texture (which texture coordinate should be applied to which vertex?)
 * @param texturePath The path of the texture 
 * @throws Exception
 */
public GameEntity(float[] vPositions, int[] indices, float[] textureCoordinates, String texturePath) throws Exception{
    System.out.println("[GameEntity.GameEntity]: Creating a new model texture...");
    modelTexture = new Texture(texturePath);
    System.out.println("[GameEntity.GameEntity]: Creating new mesh based on parameters... ");
    mesh = new Mesh(vPositions, indices, textureCoordinates, modelTexture);

    System.out.println("[GameEntity.GameEntity]: Initializing position, scale and rotation instance fields... ");
    position = new Vector3f(0, 0, 0);
    scale = 1;
    rotation = new Vector3f(0, 0, 0);
}

Обратите внимание только на тот факт, что позиции вершин, индексы и координаты текстуры (вместе с созданной текстурой) отправляются в конструктор Mesh:

Конструктор сетки

/**
     * This constructor creates a renderable object (instance of Mesh with its texture) out of input parameters by storing them
     * in the vao of that Mesh instance
     * @param vertices The vertex positions of a model
     * @param indices The indices to tell OpenGL how to connect the vertices
     * @param texCoords Texture coordinates (used for texture mapping)
     * @param texture A Texture object
     */
    public Mesh(float[] vertices, int[] indices, float[] texCoords, renderEngine.Texture texture) {
        System.out.println("[Mesh.Mesh]: Creating a new textured Mesh instance... ");

        verticesBuffer = null;
        textureBuffer = null;
        indicesBuffer = null;

        try {
            this.texture = texture;
            vertexCount = indices.length;

            vbos = new ArrayList<>();
            vaos = new ArrayList<>();
            textures = new ArrayList<>();

            System.out.println("[Mesh] Creating and binding the vao (vaoID)");
            vaoID = glGenVertexArrays();
            vaos.add(vaoID);
            glBindVertexArray(vaoID);

            setupVerticesVbo(vertices);

            setupIndicesBuffer(indices);

            setupTextureVbo(texCoords);

            textures.add(texture);

            glBindBuffer(GL_ARRAY_BUFFER, 0);
            glBindVertexArray(0);
        }
    }

Соответствующие методы дляКласс сетки: setupIndicesBuffer и setupTextureVbo:

    private void setupIndicesBuffer(int[] indices)  {
        indicesVboID = glGenBuffers();
        vbos.add(indicesVboID);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesVboID);
        indicesBuffer = BufferUtilities.storeDataInIntBuffer(indices);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicesBuffer, GL_STATIC_DRAW);
    }

    /**
 * This method sets up the texture vbo for a mesh object (buffers data to it and assigns it to attribute list
 * index 1 of the vao)
 * 
 * @param colours - an array of colours of the vertices of a model
 */
private void setupTextureVbo(float[] textures) {
    System.out.println("[Mesh] Creating texture vbo (textureVboID)...");
    textureVboID = glGenBuffers();
    vbos.add(textureVboID);

    System.out.println("   - [Mesh] Creating texture buffer (textureBuffer)...");
    textureBuffer = BufferUtilities.storeDataInFloatBuffer(textures);

    System.out.println("   - [Mesh] Binding textureVboID to GL_ARRAY_BUFER...");
    glBindBuffer(GL_ARRAY_BUFFER, textureVboID);

    System.out.println("   - [Mesh] Buffering data from textureBuffer to GL_ARRAY_BUFFER...");
    glBufferData(GL_ARRAY_BUFFER, textureBuffer, GL_STATIC_DRAW);

    System.out.println("   - [Mesh] Sending texture vbo to index 1 of the active vao...");
    glVertexAttribPointer(1, 2, GL_FLOAT, false, 0, 0);
}

cube.obj

# Blender v2.78 (sub 0) OBJ File: 'cube.blend'
# www.blender.org
o Cube
v 1.000000 -1.000000 -1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 -1.000000 1.000000
v -1.000000 -1.000000 -1.000000
v 1.000000 1.000000 -0.999999
v 0.999999 1.000000 1.000001
v -1.000000 1.000000 1.000000
v -1.000000 1.000000 -1.000000
vt 0.2766 0.2633
vt 0.5000 0.4867
vt 0.2766 0.4867
vt 0.7234 0.4867
vt 0.9467 0.2633
vt 0.9467 0.4867
vt 0.0533 0.4867
vt 0.0533 0.2633
vt 0.2766 0.0400
vt 0.5000 0.2633
vt 0.0533 0.7100
vt 0.7234 0.2633
vt 0.0533 0.0400
vt 0.2766 0.7100
vn 0.0000 -1.0000 0.0000
vn 0.0000 1.0000 0.0000
vn 1.0000 -0.0000 0.0000
vn 0.0000 -0.0000 1.0000
vn -1.0000 -0.0000 -0.0000
vn 0.0000 0.0000 -1.0000
s off
f 2/1/1 4/2/1 1/3/1
f 8/4/2 6/5/2 5/6/2
f 5/7/3 2/1/3 1/3/3
f 6/8/4 3/9/4 2/1/4
f 3/10/5 8/4/5 4/2/5
f 1/3/6 8/11/6 5/7/6
f 2/1/1 3/10/1 4/2/1
f 8/4/2 7/12/2 6/5/2
f 5/7/3 6/8/3 2/1/3
f 6/8/4 7/13/4 3/9/4
f 3/10/5 7/12/5 8/4/5
f 1/3/6 4/14/6 8/11/6

Что я достиг

  • взгляните на это видео
  • посмотрите на эту страницу на GitHub для объяснения OBJLoader
  • посмотрите на это хранилище дляисходный код (OBJLoader еще не включен, но вы можете взглянуть на другие классы, такие как GameEntity или Mesh, поскольку эти два класса являются классами, в которые отправляются данные вершин после их извлечения из файла .obj).

Видео сначала отображает исходный кодOBJLoader класс.Затем он отображает, как текстуры на кубе отображаются неправильно (за исключением задней и левой грани куба).Затем он показывает файл, в котором я проанализировал, сколько раз записывается каждый индекс в массиве, который содержит информацию о координатах текстуры.В конце отображается текстура, которая должна быть отображена.

Проблема

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

Я знаю, что: - координаты текстуры правильно считываются из файла .obj и сохраняются в текстурах ArrayList .Код для этого можно найти в разделе switch:

case "vt":
        Vector2f texture = new Vector2f(Float.parseFloat(splitLine[1]), Float.parseFloat(splitLine[2]));
        textures.add(texture);
        break;

. Я уверен, что 50/50: - индексы определяются правильно из файла .obj, так как, если они не были определены правильно,куб не был бы нарисован вообще.Код, соответствующий этому, можно найти в методе processVertex :

int currentVertexPointer = Integer.parseInt(vertexData[0]) - 1;
indices.add(currentVertexPointer);

Я не уверен, правильно ли: - текстуры упорядочены в конечном массиве с плавающей точкой, называемом texturesArray.Код, относящийся к этому шагу, можно найти в методе processVertex :

Vector2f currentTex = textures.get(Integer.parseInt(vertexData[1]) - 1);
textureArray[currentVertexPointer*2] = currentTex.x;
textureArray[currentVertexPointer*2 + 1] = 1.0f - currentTex.y;

Как это должно было работать:

Сначала нужно прочитать координаты текстурыиз файла .obj и сохраняется как Vector2f (2-мерные векторы, в основном просто хранилище для значений x и y) в ArrayList с именем textures .

Для правильной работы OpenGL, однако, эти координаты текстуры следует переставить так, чтобы они соответствовали соответствующей вершине (по крайней мере, так я читал в нескольких уроках).Это делается путем чтения так называемых полигональных элементов лица.

Эти полигональные элементы лица описываются каждой строкой, начинающейся с f.Каждая такая строка описывает три вершины.Каждая вершина представлена ​​положением вершины, текстурной координатой и вектором нормали.Пример такой строки: f 8/4/2 6/5/2 5/6/2.Присмотритесь к представлению вершины 8/4/2.Это говорит о том, что эта вершина имеет позицию, равную восьмой указанной позиции вершины в файле .obj (-1.000000 1.000000 -1.000000), координату текстуры, равную 4-й указанной координате текстуры в файле (0.7234 0.4867), и второй вектор нормали (0,0000 1,0000 0,0000).

processVertex метод вызывается три раза за каждый раз, когда найдена линия, начинающаяся с f, для обработки каждой из вершин, описывающих этот многоугольный элемент лица (один раз для 8/4/2, один раз для 6/5/2 иодин раз за 5/6/2).Каждый раз один набор данных передается методу в виде массива String (который разделяется в месте прямой косой черты), за которым следуют List textures , List normals, float [] textureArray и float []normalsArray.

private static void processVertex(String[] vertexData, List<Integer> indices, List<Vector2f> textures,
        List<Vector3f> normals, float[] textureArray, float[] normalsArray) {
    int currentVertexPointer = Integer.parseInt(vertexData[0]) - 1;
    System.out.println("[OBJLoader.processVertex]: currentVertexPointer = " + currentVertexPointer);
    indices.add(currentVertexPointer);
    System.out.println("[OBJLoader.processVertex]: Adding " + currentVertexPointer + " to indices!");

    //something probably wrong here 
    Vector2f currentTex = textures.get(Integer.parseInt(vertexData[1]) - 1);
    textureArray[currentVertexPointer*2] = currentTex.x;
    textureArray[currentVertexPointer*2 + 1] = 1.0f - currentTex.y;
    System.out.println("[OBJLoader.processVertex]: Added vt " + currentTex.x + " to index " + currentVertexPointer*2 + 
            " and vt " + (1.0f - currentTex.y) + " to index " + (currentVertexPointer*2+1) + " of the textureArray");

    Vector3f currentNorm = normals.get(Integer.parseInt(vertexData[2]) - 1);
    normalsArray[currentVertexPointer*3] = currentNorm.x;
    normalsArray[currentVertexPointer*3 + 1] = currentNorm.y;
    normalsArray[currentVertexPointer*3 + 2] = currentNorm.z;
}

Обратите внимание, что нормальные данные вершины могут быть проигнорированы, поскольку они не имеют значения.

Текущий индекс вершины сначала определяется путем вычитания одного из первого числа в переданном массиве String.(например, если 8/4/2 был передан в качестве параметра, 7 будет присвоено currentVertexPointer).Причина для того, чтобы вычесть одно, состоит в том, что изображения в файлах .obj wavefront начинаются с единицы, тогда как индексы в Java начинаются с 0. Затем это число добавляется в список indexices .

Затем,соответствующие текстурные координаты (представленные в виде Vector2f) получаются из textures List путем чтения второго числа в массиве строк, переданного как параметр, и вычитания единицы (например, если 8/4/2 был передан как параметр, Vector3f с индексом 3 из текстур список будет получен): Vector2f currentTex = textures.get(Integer.parseInt(vertexData[1]) - 1);

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

Для хранения координат текстуры соответственно, первая координата текстуры ( u или s , как их называют некоторые) хранится с индексом textureArray , который в два раза большебольшой как currentVertexPointer, так как каждая вершина имеет две текстурные координаты: textureArray[currentVertexPointer*2] = currentTex.x;.Вторая координата текстуры ( v или t , как некоторые предпочитают) сохраняется в индексе textureArray, который на единицу больше, чем индекс первой координаты текстуры: textureArray[currentVertexPointer*2 + 1] = 1.0f - currentTex.y;.

Обратите внимание, что вторая координата текстуры вычитается из 1.0f из-за различий между пространствами координат текстуры файлов волнового фронта и OpenGL.

Мои наблюдения

Я отслеживал каждый раз новыйтекстура присваивается новому (или существующему) индексу в textureArray и обнаруживает, что некоторые индексы перезаписываются, что может быть причиной проблем

После анализа данных я закончил стакой файл, который отображает заполненные индексы в textureArray справа и различные элементы, которые назначаются этим индексам во время выполнения:

 Index | values that get assigned to the index during execution of the program
    0 | 0.2766 ... 0.2766 (third f-call) ... 0.2766 (sixth f-call) ... 0.2766 (14th f-call)
    1 | 0.5133 ... 0.5133 (third f-call) ... 0.5133 (sixth f-call) ... 0.5133 (14th f-call)
    2 | 0.2766 ... 0.2766 (third f-call) ... 0.2766 (fourth f-call) ... 0.2766 (seventh f-call) ... 0.2766 (ninth f-call)
    3 | 0.7367 ... 0.7367 (third f-call) ... 0.7367 (fourth f-call) ... 0.7367 (seventh f-call) ... 0.7367 (ninth f-call)
    4 | 0.2766 ... 0.5 (fifth f-call) ... 0.5 (seventh f-call) ... 0.2766 (twelveth f-call) ... 0.5 (13th f-call)
    5 | 0.96 ... 0.7367 (fifth f-call) ... 0.7367 (seventh f-call) ... 0.96 (twelveth f-call) ... 0.7367 (13th f-call)
    6 | 0.5 ... 0.5 (fifth f-call) ... 0.5 (seventh f-call) ... 0.2766 (14th f-call)
    7 | 0.5133 ... 0.5133 (fifth f-call) ... 0.5133 (seventh f-call) ... 0.29000002 (14th f-call)
    8 | 0.9467 ... 0.0533 (third f-call) ... 0.0533 (sixth f-call) ... 0.0533 (ninth f-call)
    9 | 0.5133 ... 0.5133 (third f-call) ... 0.5133 (sixth f-call) ... 0.5133 (ninth f-call)
    10 | 0.9467 ... 0.0533 (fourth f-call) ... 0.9467 (eighth f-call) ... 0.0533 (ninth f-call) ... 0.0533 (twelveth f-call)
    11 | 0.7367 ... 0.7367 (fourth f-call) ... 0.7367 (eighth f-call) ... 0.7367 (ninth f-call) ... 0.7367 (twelveth f-call)
    12 | 0.7234 ... 0.0533 (twelveth f-call) ... 0.7234 (13th f-call)
    13 | 0.7367 ... 0.96 (twelveth f-call) ... 0.7367 (13th f-call)
    14 | 0.7234 ... 0.7234 (fifth f-call) ... 0.0533 (sixth f-call) ... 0.7234 (eighth f-call) ... 0.7234 (13th f-call) ... 0.0533 (14th f-call)
    15 | 0.5133 ... 0.5133 (fifth f-call) ... 0.29000002 (sixth f-call) ... 0.5133 (eighth f-call) ... 0.5133 (13th f-call) ... 0.29000002 (14th f-call)

    All of the indexes in the texturesArray have been accessed and assigned values several time.

    Indexes with unchanged values (ie, indexes which have been assigned the same value every time):
    0, 1, 2, 3, 9, 11

    Indexes with changed value (ie, indexes which have been assigned different values):
    4, 5, 6, 7, 8, 10, 12, 13, 14

Очевидно, что большинство индексов перезаписываются различными данными координат текстуры,Индексы, которые перезаписываются одними и теми же данными координат текстуры, равны 0, 1, 2, 3, 9 и 11. Поэтому я предполагаю, что задняя и левая грани отображаются правильно из-за того, что эти индексы перезаписываются одинаковыми значениями,тогда как другие индексы перезаписываются другими значениями.

Как решить проблему?

Да, это оказалось довольно долго, не так ли?Спасибо за потраченное время, я действительно ценю это.

Редактировать # 1

После первого ответа от @florentt мне удалось интегрировать следующий код в processVertex метод:

    private static void processVertex(String[] vertexData, List<Integer> indices, List<Vector2f> textures,
        List<Vector3f> normals, float[] textureArray, float[] normalsArray) {

    int currentVertexPointer = Integer.parseInt(vertexData[0]) - 1;
    System.out.println("[OBJLoader.processVertex]: currentVertexPointer = " + currentVertexPointer);
    indices.add(currentVertexPointer);

    //THIS IS NEW
    Vector2f currentTex = textures.get(Integer.parseInt(vertexData[1]) - 1);
    if ((textureArray[currentVertexPointer*2] + textureArray[currentVertexPointer*2+1])== 0 ) { //if the index hasn't been populated yet, store it
        textureArray[currentVertexPointer*2] = currentTex.x;
        textureArray[currentVertexPointer*2+1] = 1.0f - currentTex.y;
    } else {
        //create a new vertex (index?) and associate it with second coordinate u
        //create a new vertex (index?) and associate it with texture coordinate v
    }
    //END OF NEW CODE

    Vector3f currentNorm = normals.get(Integer.parseInt(vertexData[2]) - 1);
    normalsArray[currentVertexPointer*3] = currentNorm.x;
    normalsArray[currentVertexPointer*3 + 1] = currentNorm.y;
    normalsArray[currentVertexPointer*3 + 2] = currentNorm.z;
}

Он сообщил, что проблема вызвана тем, что одна вершина связана с несколькими разными координатами вершины (которые принадлежат разным граням).Каждая такая вершина должна быть продублирована и ей присвоена соответствующая координата текстуры.Я добавил условие if в метод processVertex , который проверяет, являются ли индексы для определенного набора координат текстуры пустыми или нет внутри texturesArray .Если индекс в массиве с плавающей запятой пуст, он содержит значение 0. Чтобы вычислить, являются ли этот и последующие индексы пустыми (каждая вершина имеет две текстурные координаты), тогда суммы значений в этих индексах должны быть 0, если ониоба пустые.Если эти два индекса еще не были заполнены текстурными координатами, тогда присвойте им текстурные координаты, которые можно получить из обрабатываемого в настоящее время элемента полигональной грани (т. Е. 8/4/2).

Однако, у меня нет 'Я не получил ни малейшего представления о том, что делать, когда индекс уже заполнен.Я знаю, что вектор положения должен быть продублирован и ему назначены соответствующие текстурные координаты (доступ к ним осуществляется так же, как указано выше), но разве это не изменит весь ArrayList оригинальных позиционных векторов, которые были считаны из файла .obj?В случае метода processVertex(String[] vertexData, List<Integer> indices, List<Vector2f> textures, List<Vector3f> normals, float[] textureArray, float[] normalsArray) где должны храниться эти дублированные векторы?Стоит ли просто дублировать индекс этого позиционного вектора, а затем присвоить текстурную координату этому индексу?Как тогда сохранять координаты текстуры в этом новом индексе?

Моя первая попытка была ввести следующее выражение if-else:

if ((textureArray[currentVertexPointer*2] + textureArray[currentVertexPointer*2+1])== 0 ) { //if the index hasn't been populated yet, store it
        textureArray[currentVertexPointer*2] = currentTex.x;
        textureArray[currentVertexPointer*2+1] = 1.0f - currentTex.y;
    } else {
        int duplicateVertexPointer = currentVertexPointer;
        indices.add(duplicateVertexPointer);
        textureArray[duplicateVertexPointer*2] = currentTex.x;
        textureArray[duplicateVertexPointer*2+1] = currentTex.y;
    }

Выше, однако, работает еще хужекак и раньше.Теперь куб даже не представлен как куб, а скорее как отдельный треугольник и граничит с пустотой между ними.Пожалуйста, помогите :(

Ответы [ 2 ]

0 голосов
/ 21 июня 2018

Проблема вызвана дублированием текстурных координат для одной вершины.Это связано с характером текстурных швов в Blender, где была создана модель.Одним из быстрых решений этой проблемы является выбор всех ребер модели, где отмечен текстовый шов, а затем разделение (дублирование) краев (Сетка -> Край -> Разделение краев).

0 голосов
/ 12 июня 2018

Кажется, вы очень близки к выяснению проблемы.Да, некоторые данные перезаписываются, потому что вершина obj может иметь несколько нормалей или векторов текстуры на разных гранях.Проблема заключается в том, что это не относится к атрибутам вершин в openGL.

Что вам нужно сделать, это проверить, есть ли у вашей вершины уже координата текстуры, и если вы создаете новую вершину и связываете ее свторая текстура.Будьте осторожны, это может быть немного грязно.Допустим, у вас есть вершина v1.на разных гранях у него несколько текстурных координат.

V1 -> t1;v1 -> t2;v1 -> t2.Так вы говорите, V1 -> T1, легко.Но затем вы видите другую координату в той же вершине.Таким образом, вы клонируете v1 и получаете v2 -> t2 (с v1 == v2).Теперь перейдите v1-> t2, вам не нужно создавать новую вершину v3, потому что v2 уже существует и идеально подходит.

Так что, чтобы сделать это правильно, вам нужно отслеживать клоны и посмотреть, есть ли какие-либо из них.Подберите комбинацию индекс / координата.

Это становится еще более запутанным, когда у вас есть и нормаль, и координата текстуры, потому что две грани могут иметь общие координаты текстуры, но не нормали или наоборот, все комбинации существуют.Так что иногда у вас будет клон, который подходит для определенной комбинации, но не для всех.

Я сделал obj-парсер, который работает (иш), но он немного грязный, поэтому предоставление вам кода будет плохой услугой.Я надеюсь, что, по крайней мере, отправил вас на правильный путь.

РЕДАКТИРОВАТЬ вот как бы я это сделал, если бы мне снова пришлось создавать парсер.Каждый раз, когда я добавляю вершину к своему лицу, я вызываю следующую функцию:

vector<vertex> vertices; //All the vertices in my object, this includes the position, texture coordinate and normal
vector<vector<int>> synonyms; // For each vertex, the array of vertices which are the same (cloned vertices)
vector<int> normalIndex;
vector<int> UV index;
int normalIn;
int textureIn;
int vertexIn; //each elements in one point of my face declaration in the obj file vertexIn/textureIn/normalIn

funtion(all of the above)
{

    vector<int> synonymsVertex = synonyms[vertexIn]; //We get the synonyms of the vertex we want to add
    for(int vertexClone : synonymsVertex)
    {
        vertex vertexObj = vertices[vertexClone];
        //In the case the combination doesn't exist, we clone the vertex and add it to the list
        if(vertexObj.normal != normalIn || vertexObj.UV != textureIn)
        {
             vertex newVertex(vertexObj, normalIn, textureIn);
             vertices.push_back(newVertex);
             synonymsVertex.push_back(vertices.size - 1);
        }
    }
}
...