有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java为什么在尝试从波前加载纹理时,使用索引渲染(GLDraweElements)时纹理显示不正确。obj文件?

简介

我正在建立一个简单的波前。obj文件解析器。我设法让它读取文件,存储文件内容(顶点位置、顶点坐标、顶点法线(尚未使用它们)和多边形面元素信息(例如5/2/3))。然后,该数据被传递到一个类(称为GameEntity),并从该类中使用GLDraweElements在模式GL_三角形中,使用该数据将该特定实体(在本例中为立方体)渲染到渲染循环内的屏幕上。但是,纹理渲染不正确

源代码

装载机。爪哇

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;
    }
}

游戏实体构造函数:

    /**
 * 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);
}

请注意,顶点位置、索引和纹理坐标(以及创建的纹理)将发送到网格构造函数:

网格构造器

/**
     * 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);
        }
    }

网格类的相关方法有setupIndicesBuffersetupTextureVbo

    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);
}

立方体。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

我所取得的成绩

  • 请看this视频
  • 查看GitHub上的this页面,了解OBJLoader的解释
  • 查看源代码的this存储库(OBJLoader尚未包括在内,但您可以查看其他类,如GameEntity或Mesh,因为这两个类是顶点数据从.obj文件提取后发送到的类)

视频首先显示OBJLoader类的源代码。然后,它将显示立方体上的纹理如何被错误映射(立方体的背面和左侧除外)。然后,它显示了一个文件,我在其中分析了数组中保存纹理坐标信息的每个索引被写入的次数。最后,将显示应映射的纹理

问题

如视频中所示,立方体的六个面中有四个面与纹理映射不正确

我知道: -纹理坐标是从图像中读取的。obj文件正确并存储在纹理中阵列列表。此代码可在switch子句中找到:

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

我确信: -指数是根据实际情况正确确定的。obj文件,因为如果没有正确地确定它们,则根本不会绘制立方体。与此相关的代码可以在processVertex方法中找到:

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

我不确定是否: -纹理在名为纹理阵列的最终浮点数组中正确排序。与此步骤相关的代码可以在processVertex方法中找到:

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

它应该如何工作:

首先,纹理坐标将从一个图像中读取。obj文件,并作为矢量2F(二维矢量,基本上只存储x和y值)存储在名为纹理的ArrayList中

但是,为了让OpenGL正常工作,这些纹理坐标应该重新排列,以便与相应的顶点匹配(至少我从多个教程中读到了这一点)。这是通过读取所谓的多边形面元素来完成的

这些多边形面元素由以^{开头的每一行描述。每条这样的线描述三个顶点。每个顶点由顶点位置、纹理坐标和法向量表示。这类行的一个示例:f 8/4/2 6/5/2 5/6/2。仔细看看8/4/2顶点表示。这表明该顶点的位置等于中的第八个指定顶点位置。obj文件(-1.000000 1.000000 -1.000000),纹理坐标等于文件(0.7234 0.4867)中第四个指定纹理坐标和第二个法向量(0.0000 1.0000 0.0000)

processVertex方法在找到以f开头的线时,每次调用三次,以处理描述该多边形面元素的每个顶点(一次用于8/4/2,一次用于6/5/2,一次用于5/6/2)。每次,一组数据作为字符串数组(在正斜杠位置拆分)传递给该方法,然后是List纹理、List法线、float[]texturarray和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;
}

请注意,顶点法线数据可以忽略,因为它是不相关的

当前顶点索引首先通过从传递的字符串数组中的第一个数字中减去一来确定(例如,如果8/4/2作为参数传递,则将7分配给CurrentVertexInter)。减去一个的原因是波前中的图像。obj文件从1开始,而Java中的索引从0开始。然后,将该数字添加到索引列表中

然后,从纹理列表中获得相应的纹理坐标(表示为矢量2f),通过读取作为参数传递的字符串数组中的第二个数字并减去一(例如,如果8/4/2作为参数传递,将获得纹理列表索引3处的向量3f):Vector2f currentTex = textures.get(Integer.parseInt(vertexData[1]) - 1);

现在已获得当前正在处理的顶点的对应纹理坐标(由CurrentVertexPoint表示),数据现在必须相应地存储在textureArray中,然后将其传递给GameEntity以构建可渲染对象(关于这一点的详细信息将不再讨论……底线:此数组中的排序很重要,因为它会影响纹理映射到模型的方式)

为了相应地存储纹理坐标,第一个纹理坐标(us,有些人称之为)存储在textureArray索引处,该索引是CurrentVertexInter的两倍,因为每个顶点有两个纹理坐标:textureArray[currentVertexPointer*2] = currentTex.x;。第二个纹理坐标(vt如某些人所喜欢)存储在比第一个纹理坐标的索引大一的textureArray索引处:textureArray[currentVertexPointer*2 + 1] = 1.0f - currentTex.y;

请注意,由于波前文件的纹理坐标空间与OpenGL之间的差异,第二个纹理坐标从1.0f中减去

我的观察

我跟踪了每次将新纹理指定给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;
}

他报告说,这个问题是由一个顶点与几个不同的顶点坐标(属于不同的面)关联的事实造成的。应该复制每个这样的顶点,并为其指定相应的纹理坐标。我在processVertex方法中添加了一个if子句,用于检查特定纹理坐标集的索引在TextureArray中是否为空。如果浮点数组中的索引为空,则其值为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;
    }

上面的hwoever比以前更糟糕。现在,立方体甚至没有渲染为立方体,而是渲染为一个单独的三角形和面,中间为空。请帮忙:(


共 (2) 个答案

  1. # 1 楼答案

    该问题是由于为一个顶点复制纹理坐标引起的。这是由于创建模型的Blender中纹理接缝的性质造成的。一个快速解决方案是选择标记纹理接缝的模型的所有边,然后分割(复制)边(网格>;边>;边分割)

  2. # 2 楼答案

    看来你很快就会发现这个问题了。是的,一些数据被覆盖,因为一个obj顶点可以在不同的面上有多个法线或纹理向量。问题在于,openGL中的顶点属性并非如此

    你需要做的是检查你的顶点是否已经有了纹理坐标,如果已经有了,你需要创建一个新的顶点并将第二个纹理关联到它。小心点,可能有点乱。 假设你有v1,一个顶点。在不同的面上,它有几个纹理坐标

    V1->;t1;v1->;t2;v1->;t2。 所以你说V1->;简单。但是你会在同一个顶点上看到另一个坐标。所以你克隆v1,得到v2->;t2(v1=v2)。 现在v1->;t2,你不应该创建一个新的顶点v3,因为v2已经存在并且非常适合

    因此,要正确地执行此操作,您需要跟踪克隆,并查看其中是否有符合组合索引/坐标的克隆

    当你同时拥有法线和纹理坐标时,它会变得更加混乱,因为两个面可以共享纹理坐标,但不能共享法线,反之,所有的联合都存在。所以有时候你会有一个适合特定组合的克隆,但不是所有的

    我做了一个obj解析器,它可以正常工作(ish),但有点混乱,所以给你代码是一种伤害。我希望我至少让你走上了正确的道路

    编辑如果我再次制作解析器,我会这样做。 每次我在脸上添加顶点时,我都会调用以下函数:

    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);
            }
        }
    }