Find intersection of a ray with a 3Dprimitive

I’m trying to use this method https://glm.g-truc.net/0.9.0/api/a00162.html in order to find the intersection between a ray (with origin X and direction wi) and a mesh. The problem that I’m having now is to convert the position of the vertices of the 3d primitive, in this case a box, to global space.

This is my code

ofColor RayCaster::L_i(const glm::vec3& X, const glm::vec3& wi) const {
     // for all the triangles in a mesh
     // Find the first intersection (and the closest!) with the scene
    glm::mat4 globalTransfMatrix = this->box.getGlobalTransformMatrix();
    vector<ofMeshFace> faces = this->mesh.getUniqueFaces();
    bool found = false;
    for(ofMeshFace face : faces){
        glm::vec3 baricenter;
        found = glm::intersectRayTriangle(X, wi,
                                          face.getVertex(0) * globalTransfMatrix,
                                          face.getVertex(1) * globalTransfMatrix,
                                          face.getVertex(2) * globalTransfMatrix,
                                          baricenter);
        if (found) {
            break;
        }
    }

   // black and white debug
    if (found) {
       return ofColor(255,0,255);
    } else {
       return ofColor(0,0,0);
    }
}

The error that I’m having is
"
Use of overloaded operator ‘*’ is ambiguous (with operand types ‘const glm::tvec3<float, glm::precision::highp>’ and ‘const glm::mat4’ (aka ‘const tmat4x4<float, highp>’))
"

I’ve already tried to multiply a glm::vec4 for glm::mat4, but in this case I was having another error, saying that the signature of the method is not correct.

Any Ideas?

the multiplication order in glm is reversed compared to ofVec so you need to do:

globalTrasfMatrix * face.getVertex(...)

but also you can’t really multiply a vec3 by a mat4, ofVec defaulted to a conversion to vec4 by adding a 1 as w which is what you want most of the time working with graphics. in any case with glm you need to do:

globalTrasfMatrix * glm::vec4(face.getVertex(...), 1.f)

Ok, Thanks. This part does not produce errors now, but it looks like the rest of the parameters are not what the method is expecting. This is my code, (blu and bla are just two vector to test what type is this method expecting):

        glm::vec3 blu = glm::vec3(1.f,1.f,1.f);
        glm::vec3 bla = glm::vec3(1.f,1.f,1.f);
        found = glm::intersectRayTriangle(blu,
                                          bla,
                                          globalTransfMatrix * glm::vec4(face.getVertex(0), 1.f),
                                          globalTransfMatrix * glm::vec4(face.getVertex(1), 1.f),
                                          globalTransfMatrix * glm::vec4(face.getVertex(2), 1.f),
                                          bari);

The error is:

I’ve tried to change the two vectors from glm::vec3 to glm::vec4 to (although the second parameter is a direction, and it should be a vec3):

glm::vec4 blu = glm::vec4(1.f,1.f,1.f,1.f);
glm::vec4 bla = glm::vec4(1.f,1.f,1.f,1.f);

And this time it is giving me this error:

As the error is highlighted at this line

found = glm::intersectRayTriangle(blu,

I assume is the vector blu the wrong one, but I’m not sure how xcode highlights the errors.

I’ve checked a bit around and the first two parameters has to be glm::vec3
https://pastebin.com/60G7be6K,

Xcode error’s highlight does not match the offending parameter with the line number, but with the line where the method is called. The offending parameters where the triangle vertices, that needs to be converted back to glm::vec3

found = glm::intersectRayTriangle(X, wi,
                                      glm::vec3(globalTransfMatrix * glm::vec4(face.getVertex(0), 1.f)),
                                      glm::vec3(globalTransfMatrix * glm::vec4(face.getVertex(1), 1.f)),
                                      glm::vec3(globalTransfMatrix * glm::vec4(face.getVertex(2), 1.f)),
                                          bari);

I’ve actually another question. I’ debugging a scene where rays are casted from a camera to a cube, and when there is an intersection the pixel color is white. So far, so good.

My question is, as a ray sometimes interesect with a cube 2 times, for example in the front face and in the backface, how should i proceed? I’m interested just in the first intersection, that one that faces the camera.

For now, i’m just exiting the loop as soon as one of the ray meets a triangle in the mesh. Is this the right approach?

ofColor RayCaster::L_i(const glm::vec3& X, const glm::vec3& wi) const{
    // for all the triangles in a mesh
    // Find the first intersection (and the closest!) with the scene
    //const shared_ptr<Surfel>& s = findFirstIntersection(X, wi); TODO

    vector<ofMeshFace> faces = this->mesh.getUniqueFaces();
    bool found = false;
    for (ofMeshFace face : faces) {
        glm::vec3 baricenter;
        found = glm::intersectRayTriangle(X, wi,
                                          glm::vec3(globalTransfMatrix * glm::vec4(face.getVertex(0), 1.f)),
                                          glm::vec3(globalTransfMatrix * glm::vec4(face.getVertex(1), 1.f)),
                                          glm::vec3(globalTransfMatrix * glm::vec4(face.getVertex(2), 1.f)),
                                          baricenter);

        if(found) {
            break;
        }
    }

    //if (notNull(s)) TODO, if a ray is found, create a Surfel
    if (found) {
       return ofColor(255,255,255);
    } else {
       return ofColor(0,0,0);
    }
}

yeah i imagine you can go out as soon as you find the first collision. also you can get the xyz froma vec4 by calling: (globalTransfMatrix * glm::vec4(face.getVertex(0), 1.f)).xyz()

Actually, it is not enough to find the first collision, one ray can have more collision with the triangles in the mesh and I’ve to pick up the closest collision point (intersection) for that ray.

This is the previous code:

shared_ptr<Surfel> RayCaster::findFirstIntersection(const Ray& ray, const ofMesh& mesh, const glm::mat4& globalTransfMatrix) const{
    vector<ofMeshFace> faces = mesh.getUniqueFaces();
    bool found = false;
    for (ofMeshFace face : faces) {
        glm::vec3 baricenter;
        found = glm::intersectRayTriangle(
                                          ray.origin, ray.direction,
                                          glm::vec3(globalTransfMatrix * glm::vec4(face.getVertex(0), 1.f)),
                                          glm::vec3(globalTransfMatrix * glm::vec4(face.getVertex(1), 1.f)),
                                          glm::vec3(globalTransfMatrix * glm::vec4(face.getVertex(2), 1.f)),
                                          baricenter);
        if (found) {
            glm::vec3 faceNormal = face.getFaceNormal();
            glm::vec3 position = getPointOnTriangle(ray, baricenter);
            return shared_ptr<Surfel>(new Surfel(faceNormal, ray.direction, position));
            break;
        }
    }
    return nullptr;
};

This code was generating this image (320x200 resolution)

As you see the intersection are not sorted. That’s why I’ve changed the code as follow:

shared_ptr<Surfel> RayCaster::findFirstIntersection(const Ray& ray, const ofMesh& mesh, const glm::mat4& globalTransfMatrix) const{
    vector<ofMeshFace> faces = mesh.getUniqueFaces();
    // at the beginning, no intersection is found and the distance to the closest surface
    // is set to an high value;
    bool found = false;
    float distanceToTheClosestSurface = numeric_limits<float>::max();
    glm::vec3 faceNormal;
    glm::vec3 position;
    glm::vec3 rayDirection;

    for (ofMeshFace face : faces) {
        glm::vec3 baricenter;
        bool intersection = glm::intersectRayTriangle(
                                          ray.origin, ray.direction,
                                          glm::vec3(globalTransfMatrix * glm::vec4(face.getVertex(0), 1.f)),
                                          glm::vec3(globalTransfMatrix * glm::vec4(face.getVertex(1), 1.f)),
                                          glm::vec3(globalTransfMatrix * glm::vec4(face.getVertex(2), 1.f)),
                                          baricenter);
        // when an intersection is found, it updates the distanceToTheClosestSurface value
        // this value is used to order the new intersections, if a new intersection with a smaller baricenter.z
        // value is found, this one will become the new intersection
        if (intersection) {
            if (baricenter.z < distanceToTheClosestSurface) {
                found = true;
                distanceToTheClosestSurface = baricenter.z;
                faceNormal = face.getFaceNormal();
                position = getPointOnTriangle(ray, baricenter);
                rayDirection = ray.direction;
            }
        }
    }

    if (found) {
        return shared_ptr<Surfel>(new Surfel(faceNormal, rayDirection, position));
    } else {
        return nullptr;
    }
};

And this goes better but as you see the right part of the image looks still wrong.

Any idea about what I’m doing wrong?

This is the method that I’m using for calculating the light, simple lambertian light for now:

ofColor RayCaster::L_scatteredDirect(const shared_ptr<Surfel>& surfelX,const glm::vec3 wo) const{
    glm::vec3 tmpCol;
    for(int i = 0; i<lights.size(); i++){
        glm::vec3 lightPos = lights[i].getGlobalPosition();
        //lambertian light
        glm::vec3 lightDirection = glm::normalize(lightPos - surfelX->getPosition());
        glm::vec3 color = surfelX->getColor();
        // surfelX->getGeometricNormal() returns the normal that was
        //  founded using face.getFaceNormal()
        float dProd = glm::dot(surfelX->getGeometricNormal(), lightDirection);
        tmpCol += glm::vec3( dProd ) * color;
    }
    return ofColor(tmpCol.x*255, tmpCol.y*255, tmpCol.z*255);
};

My bad, the error was in the method that calculated the light. Changing this line

float dProd = glm::dot(surfelX->getGeometricNormal(), lightDirection);

to this:

float dProd = abs(glm::dot(surfelX->getGeometricNormal(), lightDirection));

Fixed the problem