0
votes

The repository (GitHub)

I'm having issues with my diffuse shading for my models (It does not arise when rendering primitives.

What's interesting here to note is I think that when you look at the left reflective sphere, the shading appears normal (I might be wrong on this, basing on observation).

Low poly bunny and a triangle

Low poly bunny and 2 reflective spheres

Cube and 2 reflective spheres

I'm not sure what I'm doing wrong, as the normals are calculated each time I create a triangle in the constructor. I am using tinyobjloader to load my models, here is the TriangleMesh intersection algorithm.

FPType TriangleMesh::GetIntersection(const Ray &ray)
{
    for(auto &shape : shapes)
    {
        size_t index_offset = 0;
        for(size_t f = 0; f < shape.mesh.num_face_vertices.size(); ++f) // faces (triangles)
        {
            int fv = shape.mesh.num_face_vertices[f];
            tinyobj::index_t &idx0 = shape.mesh.indices[index_offset + 0]; // v0
            tinyobj::index_t &idx1 = shape.mesh.indices[index_offset + 1]; // v1
            tinyobj::index_t &idx2 = shape.mesh.indices[index_offset + 2]; // v2

            Vec3d &v0 = Vec3d(attrib.vertices[3 * idx0.vertex_index + 0], attrib.vertices[3 * idx0.vertex_index + 1], attrib.vertices[3 * idx0.vertex_index + 2]);
            Vec3d &v1 = Vec3d(attrib.vertices[3 * idx1.vertex_index + 0], attrib.vertices[3 * idx1.vertex_index + 1], attrib.vertices[3 * idx1.vertex_index + 2]);
            Vec3d &v2 = Vec3d(attrib.vertices[3 * idx2.vertex_index + 0], attrib.vertices[3 * idx2.vertex_index + 1], attrib.vertices[3 * idx2.vertex_index + 2]);
            Triangle tri(v0, v1, v2);
            if(tri.GetIntersection(ray))
                return tri.GetIntersection(ray);
            index_offset += fv;
        }
    }
}

The Triangle Intersection algorithm.

FPType Triangle::GetIntersection(const Ray &ray)
{
    Vector3d v0v1 = v1 - v0;
    Vector3d v0v2 = v2 - v0;
    Vector3d pvec = ray.GetDirection().Cross(v0v2);
    FPType det = v0v1.Dot(pvec);

    // ray and triangle are parallel if det is close to 0
    if(abs(det) < BIAS)
        return false;

    FPType invDet = 1 / det;
    FPType u, v;
    Vector3d tvec = ray.GetOrigin() - v0;
    u = tvec.Dot(pvec) * invDet;
    if(u < 0 || u > 1)
        return false;

    Vector3d qvec = tvec.Cross(v0v1);
    v = ray.GetDirection().Dot(qvec) * invDet;
    if(v < 0 || u + v > 1)
        return false;

    FPType t = v0v2.Dot(qvec) * invDet;

    if(t < BIAS)
        return false;

    return t;
}

I think this is because when I'm handling all my object intersections, the triangle mesh is regarded as 1 object only, so it only returns 1 normal, when I'm trying to get the object normals: code

Color Trace(const Vector3d &origin, const Vector3d &direction, const std::vector<std::shared_ptr<Object>> &sceneObjects, const int indexOfClosestObject,
                 const std::vector<std::shared_ptr<Light>> &lightSources, const int &depth = 0)
{
    if(indexOfClosestObject != -1 && depth <= DEPTH) // not checking depth for infinite mirror effect (not a lot of overhead)
    {
        std::shared_ptr<Object> sceneObject = sceneObjects[indexOfClosestObject];
        Vector3d normal = sceneObject->GetNormalAt(origin);

screenshot of debug

EDIT: I have solved the issue and now shading works properly: https://github.com/MrCappuccino/Tracey/blob/testing/src/TriangleMesh.cpp#L35-L48

1
Sneaky edit! Just don’t return immediately from TriangleMesh::GetIntersection but calculate the distance of your hit, only remember you current hit if the distance is shorter than your currently saved hit and return at the end. - Darklighter
@Darklighter do you mean I should cycle through all triangles and only return the closest one inside TriangleMesh::GetIntersection? - The Dude
Is there a way in c++ of seeing if an Object is a TriangleMesh or a Triangle for instance? - The Dude
Yes, because otherwise you don’t get the intersection you really want, no? Why do you want to know if an object is a TriangleMesh or a Triangle? (and usually not, no) - Darklighter

1 Answers

1
votes

If you iterate all your faces and return on the first face you hit, it may happen that you hit faces which are behind other faces and therefore not really the face you want to hit, so you would have to measure the length of your ray and return the intersection for the shortest ray.