//Ray Mesh Intersection //Project: LightWhat - CPU Pathtracer //https://github.com/Illation/LightWhat //Author: Robert Lindner //Intersect a ray with a mesh, and calculate the shading data at intersection point if there is a hit detected //looping over the triangles and materials is done externaly //that is why subshape indexes are passed to the intersection function //the first one determines the material group, the second one the triangle index //Triangle data is precalculated before the raytracer begins to do it more efficiently //I wrote a lot of this code before knowing a lot about c++ coding standards or the standard library //A lot of this could be more efficient and better written //precalculate triangle data for plane intersection and point in triangle test void tri::precalculate(vec3 a, vec3 b, vec3 c) { triPlane = plane(a, b, c); //normal form of plane v0 = c - a; v1 = b - a; dot00 = v0.Dot(v0); dot01 = v0.Dot(v1); dot11 = v1.Dot(v1); invDenom = (dot00 * dot11 - dot01 * dot01); } //Plane intersection test with the normal form of planes, taking backface culling into consideration //returns a ray distance to intersection point inline intersection rayIts(line ln, bool bfc) //inlining doesnt really help a lot { intersection ret(false, point3(0, 0, 0), 0); //create an empty intersection float nd = n.Dot(ln.dir); //dot product of plane normal and ray direction if (nd<0) //if plane and ray are not parralel { //the next calculation determines how far the ray needs to travel to hit the plane ret.t = (d - n.Dot(ln.orig)) / nd; //intersection hit scalar = (plane scalar - (plane normal dot ray origin)) / nd ret.hit = true; ret.backfacing = false; } else if (nd>0 && bfc == false) { ret.t = (d - n.Dot(ln.orig)) / nd; ret.hit = true; ret.backfacing = true; } return ret; } //Ray mesh intersection, finalzies ray plane intersection and does point in triangle test //returns a shading geometry containing data for shaders if a hit is detected void Mesh::getIntersection(size_t subShapeIdx, size_t subShapeIdx2, Ray ray, DifferentialGeometry &closest, float minT, bool bfc) { intersection its; tri thisTri = m_TriLists[subShapeIdx].triangles[subShapeIdx2]; //Get The triangle to be tested point3 fVertA = m_VertexList[thisTri.vertA]; //Reference corner in triangle, used for point in triangle test its = thisTri.triPlane.rayIts(ray.ln, bfc); //test plane intersection with triangle plane if (its.hit == true) { if ((its.t < closest.i.t || closest.i.hit == false) && its.t>minT) //check if its is closest intersection { //calculate baryacentric coordinates its.p = ray.ln.orig + ray.ln.dir*its.t; //intersection point = ray origin + ray direction * intersection hit scalar //**************** //Point in triangle test using barycentric coordinates //**************** vec3 v2 = its.p - fVertA; //vector between intersection point and reference corner float dot02 = thisTri.v0.Dot(v2); float dot12 = thisTri.v1.Dot(v2); float u = (thisTri.dot11 * dot02 - thisTri.dot01 * dot12) / thisTri.invDenom; //calculate u coordinate for intersection point in relation to triangle if (u >= 0) { float v = (thisTri.dot00 * dot12 - thisTri.dot01 * dot02) / thisTri.invDenom; //calculate v coordinate for intersection point in relation to triangle if (v < 0) its.hit = false; //exclude cases in which there is no hit if (u + v > 1) its.hit = false; //exclude cases in which there is no hit if (its.hit) //intersection point is in the triangle { //*********************** //Calculate shading data for differential geometry //*********************** closest.i = its;//new closest intersection point closest.mat = m_TriLists[subShapeIdx].matIndex;//set material //smooth normals vec3 n1 = m_NormalList[thisTri.normA]; //get the vertex normals of the triangle vec3 n2 = m_NormalList[thisTri.normB]; vec3 n3 = m_NormalList[thisTri.normC]; vec3 N = n1 + (n3 - n1)*u + (n2 - n1)*v; //interpolate between normals using barycentric coordenates of hit point closest.n = N.Norm(0.00001f); //normalize that normal //tangent space for normal maps if (hasTangentSpace) //determine tangent and bitangent the same way for normal mapping { vec3 t1 = m_TangentList[thisTri.normA]; //get all vertex tangents vec3 t2 = m_TangentList[thisTri.normB]; vec3 t3 = m_TangentList[thisTri.normC]; vec3 T = t1 + (t3 - t1)*u + (t2 - t1)*v; //interpolate between tangents closest.t = T.Norm(0.00001f); //normalize and store tangent vec3 b1 = m_BiTangentList[thisTri.normA]; //get all vertex bitangents vec3 b2 = m_BiTangentList[thisTri.normB]; vec3 b3 = m_BiTangentList[thisTri.normC]; vec3 B = b1 + (b3 - b1)*u + (b2 - b1)*v; //interpolate between bitangents closest.b = B.Norm(0.00001f); //store normalized bitangent closest.hasTangentSpace = true; } else closest.hasTangentSpace = false; //uv map closest.uv = point2(u, v); //use barycentric coordinates by default for non mapped meshes if (m_TriLists[subShapeIdx].hasUV) { vec2 uv1 = m_UVs[0].coords[thisTri.uvA]; //get all uv coordinates vec2 uv2 = m_UVs[0].coords[thisTri.uvB]; vec2 uv3 = m_UVs[0].coords[thisTri.uvC]; closest.uv = uv1 + (uv3 - uv1)*u + (uv2 - uv1)*v; //interpolate between texcoords using barycentric coordinates closest.uv = closest.uv.correctUV(); //clamp uv coordiantes between 0 and 1 closest.uv.y = 1 - closest.uv.y; //invert v coordinate } } } else its.hit = false; } else its.hit = false; } }