加载到我的光线追踪器后翻转法线

Flipped normals after loaded in my raytracer

我正在使用 C++ 开发 path/ray 示踪剂。我现在正在加载 obj 文件。但是有些物体在加载后法线翻转了。我不知道这个行为是从哪里来的,也不知道如何解决。

查看图片以更好地理解问题。

显示当前行为的图像:

Link to full GitHub page

首先我认为这是表面后面的法线的问题。但是在根据表面法线渲染颜色之后。很明显,法线在某些情况下被翻转了。

这是我加载模型的最基本代码。

    //OBJ Loader object.
    bool OBJLoader::loadMesh (std::string filePath){
        // If the file is not an .obj file return false
        if (filePath.substr(filePath.size() - 4, 4) != ".obj"){
            std::cout << "No .obj file found at given file location: "<<filePath << std::endl;

        }
        //Open file stream
        std::ifstream file(filePath);

        //check if file is open.
        if (!file.is_open()){
            std::cout << "File was not opened!" << std::endl;
            return false;
        }

        //Do file loading.
        std::cout << "Parsing obj-file: "<<filePath << std::endl;

        //constuct mesh data.
        bool smoothShading = false;
        std::string obj_name;
        std::vector<Vertex> vertices;
        std::vector<Vect3> Positions;
        std::vector<Vect3> Normals;
        std::vector<Vect2> UVs;

        std::vector<unsigned int> V_indices;

        //the current line
        std::string currentLine;
        //loop over each line and parse the needed data.
        while(std::getline(file, currentLine)){

            //for now we just print the line
            //std::cout << currentLine << std::endl;

            if(algorithm::startsWith(currentLine, "s ")){
                std::vector<std::string> line_split = algorithm::split(currentLine,' ');
                if( line_split[1] == std::string("off")){
                    smoothShading = false;
                }else if(line_split[1] == std::string("1")){
                    //enalbe smooth shading;
                    smoothShading = true;
                }
            }

            //check if the line starts with v -> vertex.
            if(algorithm::startsWith(currentLine, "o ")){
                //construct new vertex position.
                std::vector<std::string> line_split = algorithm::split(currentLine,' ');

                obj_name = line_split[1];
            }

            //check if the line starts with v -> vertex.
            if(algorithm::startsWith(currentLine, "v ")){
                //construct new vertex position.
                std::vector<std::string> line_split = algorithm::split(currentLine,' ');

                float x = std::stof(line_split[1]);
                float y = std::stof(line_split[2]);
                float z = std::stof(line_split[3]);
                Vect3 pos = Vect3(x,y,z);
                Positions.push_back(pos);
            }

            //check if the line starts with vt -> vertex uv.
            if(algorithm::startsWith(currentLine, "vt ")){
                //construct new vertex uv.
                std::vector<std::string> line_split = algorithm::split(currentLine,' ');

                float u = std::stof(line_split[1]);
                float v = std::stof(line_split[2]);
                Vect2 uv = Vect2(u,v);
                UVs.push_back(uv);
            }

            //check if the line starts with vn -> vertex normals.
            if(algorithm::startsWith(currentLine, "vn ")){
                //construct new vertex normal.
                std::vector<std::string> line_split = algorithm::split(currentLine,' ');

                float x = std::stof(line_split[1]);
                float y = std::stof(line_split[2]);
                float z = std::stof(line_split[3]);
                Vect3 normal = Vect3(x,y,z);
                Normals.push_back(normal);
            }

            //check if the line starts with f -> constuct faces.
            if(algorithm::startsWith(currentLine, "f ")){
                //construct new vertex position.

                std::vector<std::string> line_split = algorithm::split(currentLine,' ');

                //@NOTE: this only works when mesh is already triangulated.
                //Parse all vertices.
                std::vector<std::string> vertex1 = algorithm::split(line_split[1],'/');
                std::vector<std::string> vertex2 = algorithm::split(line_split[2],'/');
                std::vector<std::string> vertex3 = algorithm::split(line_split[3],'/');

                if(vertex1.size() <= 1){
                    //VERTEX 1
                    Vect3 position = Positions[std::stoi(vertex1[0])-1];
                    Vertex v1(position);
                    vertices.push_back(v1);

                    //VERTEX 2
                    position = Positions[std::stoi(vertex2[0])-1];
                    Vertex v2(position);
                    vertices.push_back(v2);

                    //VERTEX 3
                    position = Positions[std::stoi(vertex3[0])-1];
                    Vertex v3(position);
                    vertices.push_back(v3);

                    //Add to Indices array.
                    //calculate the index number
                    //The 3 comes from 3 vertices per face.
                    unsigned int index = vertices.size() - 3;
                    V_indices.push_back(index);
                    V_indices.push_back(index+1);
                    V_indices.push_back(index+2);
                }

                //check if T exist.
                else if(vertex1[1] == ""){
                    //NO Uv
                    //V -> index in the positions array.
                    //N -> index in the normals array.

                    //VERTEX 1
                    Vect3 position = Positions[std::stoi(vertex1[0])-1];
                    Vect3 normal = Normals[std::stoi(vertex1[2])-1];

                    Vertex v1(position,normal);
                    vertices.push_back(v1);

                    //VERTEX 2
                    position = Positions[std::stoi(vertex2[0])-1];
                    normal = Normals[std::stoi(vertex2[2])-1];

                    Vertex v2(position,normal);
                    vertices.push_back(v2);

                    //VERTEX 3
                    position = Positions[std::stoi(vertex3[0])-1];
                    normal = Normals[std::stoi(vertex3[2])-1];

                    Vertex v3(position,normal);
                    vertices.push_back(v3);

                    //Add to Indices array.
                    //calculate the index number
                    //The 3 comes from 3 vertices per face.
                    unsigned int index = vertices.size() - 3;
                    V_indices.push_back(index);
                    V_indices.push_back(index+1);
                    V_indices.push_back(index+2);

                }else if (vertex1[1] != ""){
                    //We have UV
                    //V -> index in the positions array.
                    //T -> index of UV
                    //N -> index in the normals array.

                    //VERTEX 1
                    Vect3 position = Positions[std::stoi(vertex1[0])-1];
                    Vect2 uv = UVs[std::stoi(vertex1[1])-1];
                    Vect3 normal = Normals[std::stoi(vertex1[2])-1];

                    Vertex v1(position,normal,uv);
                    vertices.push_back(v1);

                    //VERTEX 2
                    position = Positions[std::stoi(vertex2[0])-1];
                    uv = UVs[std::stoi(vertex2[1])-1];
                    normal = Normals[std::stoi(vertex2[2])-1];

                    Vertex v2(position,normal,uv);
                    vertices.push_back(v2);

                    //VERTEX 3
                    position = Positions[std::stoi(vertex3[0])-1];
                    uv = UVs[std::stoi(vertex3[1])-1];
                    normal = Normals[std::stoi(vertex3[2])-1];

                    Vertex v3(position,normal,uv);
                    vertices.push_back(v3);

                    //Add to Indices array.
                    //calculate the index number
                    //The 3 comes from 3 vertices per face.
                    unsigned int index = vertices.size() - 3;
                    V_indices.push_back(index);
                    V_indices.push_back(index+1);
                    V_indices.push_back(index+2);
                }

                //We can check here in which format. V/T/N, V//N, V//, ...
                //For now we ignore this and use V//N.
            }
        }

        //close stream
        file.close();

        Positions.clear();
        Normals.clear();
        UVs.clear();

        //reorder the arrays so the coresponding index match the position,uv and normal.
        for (Vertex v: vertices) {
            Positions.push_back(v.getPosition());
            Normals.push_back(v.getNormal());
            UVs.push_back(v.getUV());
        }

        //Load mesh data.
        _mesh = Mesh(smoothShading,obj_name, Positions, Normals, UVs, V_indices);

        //return true, succes.
        return true;

在此之后,模型被插入到网格结构中以进行更快的相交测试。

    for(int i= 0;i<mesh._indices.size();i=i+3){
        Triangle* tri;
        if(mesh.smoothShading){
            tri = new SmoothTriangle(Point3(mesh._positions[mesh._indices[i]]),
                                     Point3(mesh._positions[mesh._indices[i+1]]),
                                     Point3(mesh._positions[mesh._indices[i+2]]),
                                     Normal(mesh._normals[mesh._indices[i]]),
                                     Normal(mesh._normals[mesh._indices[i+1]]),
                                     Normal(mesh._normals[mesh._indices[i+2]]),material);
        }else{
            tri = new Triangle(Point3(mesh._positions[mesh._indices[i]]),
                               Point3(mesh._positions[mesh._indices[i+1]]),Point3(mesh._positions[mesh._indices[i+2]]),Normal(mesh._normals[mesh._indices[i]]),material);
        }

        add_object(tri);
    }
    constructCells();

也许可以添加用于插值法线的代码

Normal SmoothTriangle::calculate_normal(double gamma, double beta){
    return (Normal((1 - beta - gamma) * n0 + beta * n1 + gamma * n2)).normalize();
}

固定

我解决了这个问题。它不在我的 OBJ 加载器中。该模型是从搅拌机导出的,导出时它应用了所有修改器,但在导出到 .obj 文件后,Solidify 导致一些背面与正面剪裁。删除此修饰符后,一切都回到了 "normal"(只是一个有趣的双关语来完成这个答案)

您的代码可能没有问题我假设 obj 已损坏,因为某些 obj 模型已经翻转了法线...

Wavefront obj 格式根本没有指定法线方向我什至看到模型不一致所以一些法线指向其他人。你甚至不能确定面有单一缠绕规则。所以使用双向法线更安全(你知道使用

|dot(normal,light)|

而不是

dot(normal,light)

无面剔除或加载后自行重新计算法线甚至缠绕规则。

双向 normals/lighting 有时会通过不同的 material 设置来设置面部的每一侧 FRONTBACKFRONT_AND_BACKDOUBLE_SIDED 等或其配置...只需在您的 gfx API 中查找此类内容即可。要关闭面部剔除,请查找 CULL_FACE

之类的内容