I am having an issue with my algorithm to check if my ray intersect a 3D triangle. It seems to be still drawing in the circle behind it(top left hand corner). I can't seem to find out where in my code is causing this slight error.
bool Mesh::intersectTriangle(Ray const &ray,
Triangle const &tri,
Intersection &hit) const{
// Extract vertex positions from the mesh data.
Vector const &p0 = positions[tri[0].pi];
Vector const &p1 = positions[tri[1].pi];
Vector const &p2 = positions[tri[2].pi];
Vector e1 = p1 - p0;
Vector e2 = p2 - p0;
Vector e1e2 = e1.cross(e2);
Vector p = ray.direction.cross(e2);
e1e2.normalized();
float a = e1.dot(p);
if(a < 0.000001)
return false;
float f = 1 / a;
Vector s = ray.origin - p0;
float u = f*(s.dot(p));
if(u < 0.0 || u > 1.0)
return false;
Vector q = s.cross(e1);
float v = f * (ray.direction.dot(q));
if(v < 0.0 || u + v > 1.0)
return false;
float t = f * (e2.dot(q));
hit.depth = t;
hit.normal = e1e2;
hit.position = hit.position *t;
return true;
Related
I'm trying to implement sphere ray intersection in GLSL, both the geometric and analytical solution. I'm having trouble solving the geom one, it should have something to do with how I return true or false:
bool hitSphere(Ray ray, Sphere sphere, float t_min, float t_max, out float t_out) {
// Geometric solution
float R2 = sphere.radius * sphere.radius;
vec3 L = sphere.position - ray.origin;
float tca = dot(L, normalize(ray.direction));
// if(tca < 0) return false;
float D2 = dot(L, L) - tca * tca;
if(D2 > R2) return false;
float thc = sqrt(R2 - D2);
float t0 = tca - thc;
float t1 = tca + thc;
if(t0 < t_max && t0 > t_min) {
t_out = t0;
return true;
}
if(t1 < t_max && t1 > t_min) {
t_out = t1;
return true;
}
return false;
}
I think the problem is with how I deal with t0 and t1 for none, one or both intersection cases.
Edit: the analytic version that does work:
vec3 oc = ray.origin - sphere.position;
float a = dot(ray.direction, ray.direction);
float b = dot(oc, ray.direction);
float c = dot(oc, oc) - sphere.radius * sphere.radius;
float discriminant = b * b - a * c;
if (discriminant > 0.0f) {
if(b > 0)
t_out = (-b + sqrt(discriminant)) / a;
else
t_out = (-b - sqrt(discriminant)) / a;
if(t_out < t_max && t_out > t_min) {
return true;
}
}
return false;
The issue is caused by t_out. The algorithm has to compute t_out in that way, that X is the intersected point of the ray and the surface of the sphere, for:
X = ray.origin + ray.direction * t_out;
In the working algorithm t_out depends on the length of ray.direction. t_out becomes smaller, if the magnitude of the vector ray.direction is greater.
In the algorithm, which doesn't work, ray.direction is normalized.
float tca = dot(L, normalize(ray.direction));
Hence t_out is computed for a ray direction length of 1. Actually you compute a t_out' where t_out' = t_out * length(ray.direction).
Divide t0 respectively t1 by the length of ray.direction:
bool hitSphere_2(Ray ray, Sphere sphere, float t_min, float t_max, out float t_out)
{
float R2 = sphere.radius * sphere.radius;
vec3 L = sphere.position - ray.origin;
float tca = dot(L, normalize(ray.direction));
// if(tca < 0) return false;
float D2 = dot(L, L) - tca * tca;
if(D2 > R2) return false;
float thc = sqrt(R2 - D2);
float t0 = tca - thc;
float t1 = tca + thc;
if (t0 < t_max && t0 > t_min) {
t_out = t0 / length(ray.direction); // <---
return true;
}
if (t1 < t_max && t1 > t_min) {
t_out = t1 / length(ray.direction); // <---
return true;
}
return false;
}
I am trying to calculate the RGB value of a pixel using the Blinn-Phong formula. For that I use this function:
Material getPixelColor(Ray ray, double min, int index, std::vector<Object*> Objects, std::vector<Object*> lightSources) {
Vector intersectionPoint = ray.getOrigin() + ray.getDirection() * min;
Vector n = Objects.at(index)->getNormalAt(intersectionPoint);
Vector reflectiondirection = ray.getDirection() - n * Vector::dot(ray.getDirection(), n) * 2;
Ray reflectionRay(intersectionPoint, reflectiondirection);
// check if ray intersects any other object;
double minimum = INFINITY;
int count = 0, indx = -1;
for (auto const& obj : Objects) {
double distance = obj->Intersect(reflectionRay);
if (minimum > distance) {
minimum = distance;
indx = count;
}
count++;
}
Material result(0,0,0);
if (recurseDepth >= 5 || indx == -1) {
recurseDepth = 0;
// Check if object is lit for each light source
for (auto const& light : lightSources) {
// Blinn-Phong
Vector lightDirection = (light->getPosition() - intersectionPoint).normalize();
double nl = Vector::dot(n, lightDirection);
nl = nl > 0 ? nl : 0.0;
result = result + (Objects.at(index)->getMaterial() * light->getMaterial() * nl);
}
}
else{
recurseDepth++;
result = result + getPixelColor(reflectionRay, minimum, indx, Objects, lightSources);
}
return result;
}
The result that I get is this:
This is how it was without shading:
I have been trying to find a solution for hours and can't. Am I using the wrong formula?
After a lot of research, I removed the part where it is getting color from other objects:
Material getPixelColor(Ray ray, double min, int index, std::vector<Object*> Objects, std::vector<Object*> lightSources) {
Vector intersectionPoint = ray.getOrigin() + ray.getDirection() * min;
Vector n = Objects.at(index)->getNormalAt(intersectionPoint);
Material result(0,0,0);
// Check if object is lit for each light source
for (auto const& light : lightSources) {
//create a ray to the light and check if there is an object between the two
Vector lightDirection = (light->getPosition() - intersectionPoint).normalize();
Ray lightRay(intersectionPoint, lightDirection);
bool hit = false;
for (auto const& obj : Objects) {
double distance = obj->Intersect(lightRay);
if (INFINITY > distance && distance > 0.0001) {
hit = true;
break;
}
}
if (!hit) {
// Blinn-Phong
double nl = Vector::dot(n, lightDirection);
// clamp nl between 0 and 1
if (nl > 1.0) {
nl = 1.0;
}
else if (nl < 0.0) {
nl = 0.0;
}
result = result + (Objects.at(index)->getMaterial() * nl);
}
}
return result;
}
And so I got the desired result:
I am trying to implement the ray tracing algorithm and I have some trouble computing the reflected rays of spherical objects.It seems that
for some particular rays, the reflected ray just passes through and is collinear with the traced ray.
Bellow is how i record the ray - sphere intersection:
bool Sphere::intersectLocal(const ray & r, isect & i) const {
Vec3d P = r.getPosition();
Vec3d D = r.getDirection();
//D.normalize();
double a = dot(D, D);
double b = 2 * dot(P, D);
double c = dot(P, P) - 1;
double delta = b * b - 4 * a * c;
if (delta < 0)
return false;
if (delta == 0) {
double t = -b / 2 * a;
Vec3d Q = P + t * D;
Vec3d N = Q;
N.normalize();
i.setT(t);
i.setN(N);
i.setObject(this);
return true;
}
if (delta > 0) {
double t1 = (-b - sqrt(delta)) / 2 * a;
double t2 = (-b + sqrt(delta)) / 2 * a;
double t;
if (t1 > 0) t = t1;
else if (t2 > 0) t = t2;
else return false;
Vec3d N = P + t * D;
N.normalize();
i.setT(t);
i.setN(N);
i.setObject(this);
return true;
}
return false;
}
And this is how I compute the reflected ray for each intersection:
isect i;
if (scene - > intersect(r, i)) {
// An intersection occured!
const Material & m = i.getMaterial();
double t = i.t;
Vec3d N = i.N;
Vec3d I = m.shade(scene, r, i); //local illumination
if (!m.kr(i).iszero() && depth >= 0) {
// compute reflection direction
Vec3d raydir = r.getDirection();
Vec3d refldir = 2 * dot(-raydir, i.N) * i.N + raydir;
refldir.normalize();
ray reflectionRay = ray(r.at(i.t), refldir, ray::RayType::REFLECTION);
Vec3d reflection = traceRay(reflectionRay, thresh, depth - 1);
Vec3d R = reflection;
I += R;
}
return I;
} else {
// No intersection. This ray travels to infinity, so we color
// it according to the background color, which in this (simple) case
// is just black.
return Vec3d(0.0, 0.0, 0.0);
}
The code above seems to work fine for most of the points on the sphere where the rays intersect, but for others it does not reflect as i expected
If I see right, this makes the normal face same direction as the ray. So with ray==normal==reflected_ray nothing gets reflected.
Vec3d Q = P + t * D;
Vec3d N = Q;
About errors in floating-point arithmetic and how to deal with it:
What Every Computer Scientist Should Know About Floating-Point Arithmetic
Here you can find how to compare floating-point numbers. Searching for relative absolute compare floating you may find more information.
https://floating-point-gui.de/errors/comparison/
This is an excerpt from my code in C#. Almost never use absolute compare.
public static bool IsAlmostRelativeEquals(this double d1, double d2, double epsilon)
{
double absDiff = Math.Abs(d1 - d2);
if (double.IsPositiveInfinity(absDiff))
return false;
if (absDiff < epsilon)
return true;
double absMax = Math.Max(Math.Abs(d1), Math.Abs(d2));
return Math.Abs(d1 - d2) <= epsilon * absMax;
}
public static bool IsAlmostZero(this double d, double epsilon)
{
double abs = Math.Abs(d);
if (double.IsPositiveInfinity(abs))
return false;
return abs < epsilon;
}
I'm trying to write a ray tracer for any objects formed of triangular meshes. I'm using an external library to load a cube from .ply format and then trace it down. So far, I've implemented most of the tracer, and now I'm trying to test it with a single cube, but for some reason all I get on the screen is a red line. I've tried several ways to fix it but I simply can't figure it out anymore. For this primary test, I'm only creating primary rays, and if they hit my cube, then I color that pixel to the cube's diffuse color and return. For checking ray-object intersections, I am going through all the triangles that form that object and return the distance to the closest one. It would be great if you could have a look at the code and tell me what could have gone wrong and where. I would greatly appreciate it.
Ray-Triangle intersection:
bool intersectTri(const Vec3D& ray_origin, const Vec3D& ray_direction, const Vec3D& v0, const Vec3D& v1, const Vec3D& v2, double &t, double &u, double &v) const
{
Vec3D edge1 = v1 - v0;
Vec3D edge2 = v2 - v0;
Vec3D pvec = ray_direction.cross(edge2);
double det = edge1.dot(pvec);
if (det > - THRESHOLD && det < THRESHOLD)
return false;
double invDet = 1/det;
Vec3D tvec = ray_origin - v0;
u = tvec.dot(pvec)*invDet;
if (u < 0 || u > 1)
return false;
Vec3D qvec = tvec.cross(edge1);
v = ray_direction.dot(qvec)*invDet;
if (v < 0 || u + v > 1)
return false;
t = edge2.dot(qvec)*invDet;
if (t < 0)
return false;
return true;
}
//Object intersection
bool intersect(const Vec3D& ray_origin, const Vec3D& ray_direction, IntersectionData& idata, bool enforce_max) const
{
double tClosest;
if (enforce_max)
{
tClosest = idata.t;
}
else
{
tClosest = TMAX;
}
for (int i = 0 ; i < indices.size() ; i++)
{
const Vec3D v0 = vertices[indices[i][0]];
const Vec3D v1 = vertices[indices[i][1]];
const Vec3D v2 = vertices[indices[i][2]];
double t, u, v;
if (intersectTri(ray_origin, ray_direction, v0, v1, v2, t, u, v))
{
if (t < tClosest)
{
idata.t = t;
tClosest = t;
idata.u = u;
idata.v = v;
idata.index = i;
}
}
}
return (tClosest < TMAX && tClosest > 0) ? true : false;
}
Vec3D trace(World world, Vec3D &ray_origin, Vec3D &ray_direction)
{
Vec3D objColor = world.background_color;
IntersectionData idata;
double coeff = 1.0;
int depth = 0;
double tClosest = TMAX;
Object *hitObject = NULL;
for (unsigned int i = 0 ; i < world.objs.size() ; i++)
{
IntersectionData idata_curr;
if (world.objs[i].intersect(ray_origin, ray_direction, idata_curr, false))
{
if (idata_curr.t < tClosest && idata_curr.t > 0)
{
idata.t = idata_curr.t;
idata.u = idata_curr.u;
idata.v = idata_curr.v;
idata.index = idata_curr.index;
tClosest = idata_curr.t;
hitObject = &(world.objs[i]);
}
}
}
if (hitObject == NULL)
{
return world.background_color;
}
else
{
return hitObject->getDiffuse();
}
}
int main(int argc, char** argv)
{
parse("cube.ply");
Vec3D diffusion1(1, 0, 0);
Vec3D specular1(1, 1, 1);
Object cube1(coordinates, connected_vertices, diffusion1, specular1, 0, 0);
World wrld;
// Add objects to the world
wrld.objs.push_back(cube1);
Vec3D background(0, 0, 0);
wrld.background_color = background;
// Set light color
Vec3D light_clr(1, 1, 1);
wrld.light_colors.push_back(light_clr);
// Set light position
Vec3D light(0, 64, -10);
wrld.light_positions.push_back(light);
int width = 128;
int height = 128;
Vec3D *image = new Vec3D[width*height];
Vec3D *pixel = image;
// Trace rays
for (int y = -height/2 ; y < height/2 ; ++y)
{
for (int x = -width/2 ; x < width/2 ; ++x, ++pixel)
{
Vec3D ray_dir(x+0.5, y+0.5, -1.0);
ray_dir.normalize();
Vec3D ray_orig(0.5*width, 0.5*height, 0.0);
*pixel = trace(wrld, ray_orig, ray_dir);
}
}
savePPM("./test.ppm", image, width, height);
return 0;
}
I've just ran a test case and I got this:
for a unit cube centered at (0,0, -1.5) and scaled on the X and Y axis by 100. It seems that there is something wrong with the projection, but I can't really tell exactly what from the result. Also, shouldn't, in this case (cube is centered at (0,0)) the final object also appear in the middle of the picture?
FIX: I fixed the centering problem by doing ray_dir = ray_dir - ray_orig before normalizing and calling the trace function. Still, the perspective seems to be plain wrong.
I continued the work and now I started implementing the diffuse reflection according to Phong.
Vec3D trace(World world, Vec3D &ray_origin, Vec3D &ray_direction)
{
Vec3D objColor = Vec3D(0);
IntersectionData idata;
double coeff = 1.0;
int depth = 0;
do
{
double tClosest = TMAX;
Object *hitObject = NULL;
for (unsigned int i = 0 ; i < world.objs.size() ; i++)
{
IntersectionData idata_curr;
if (world.objs[i].intersect(ray_origin, ray_direction, idata_curr, false))
{
if (idata_curr.t < tClosest && idata_curr.t > 0)
{
idata.t = idata_curr.t;
idata.u = idata_curr.u;
idata.v = idata_curr.v;
idata.index = idata_curr.index;
tClosest = idata_curr.t;
hitObject = &(world.objs[i]);
}
}
}
if (hitObject == NULL)
{
return world.background_color;
}
Vec3D newStart = ray_origin + ray_direction*idata.t;
// Compute normal at intersection by interpolating vertex normals (PHONG Idea)
Vec3D v0 = hitObject->getVertices()[hitObject->getIndices()[idata.index][0]];
Vec3D v1 = hitObject->getVertices()[hitObject->getIndices()[idata.index][1]];
Vec3D v2 = hitObject->getVertices()[hitObject->getIndices()[idata.index][2]];
Vec3D n1 = hitObject->getNormals()[hitObject->getIndices()[idata.index][0]];
Vec3D n2 = hitObject->getNormals()[hitObject->getIndices()[idata.index][1]];
Vec3D n3 = hitObject->getNormals()[hitObject->getIndices()[idata.index][2]];
// Vec3D N = n1 + (n2 - n1)*idata.u + (n3 - n1)*idata.v;
Vec3D N = v0.computeFaceNrm(v1, v2);
if (ray_direction.dot(N) > 0)
{
N = N*(-1);
}
N.normalize();
Vec3D lightray_origin = newStart;
for (unsigned int itr = 0 ; itr < world.light_positions.size() ; itr++)
{
Vec3D lightray_dir = world.light_positions[0] - newStart;
lightray_dir.normalize();
double cos_theta = max(N.dot(lightray_dir), 0.0);
objColor.setX(objColor.getX() + hitObject->getDiffuse().getX()*hitObject->getDiffuseReflection()*cos_theta);
objColor.setY(objColor.getY() + hitObject->getDiffuse().getY()*hitObject->getDiffuseReflection()*cos_theta);
objColor.setZ(objColor.getZ() + hitObject->getDiffuse().getZ()*hitObject->getDiffuseReflection()*cos_theta);
return objColor;
}
depth++;
} while(coeff > 0 && depth < MAX_RAY_DEPTH);
return objColor;
}
When I reach an object with the primary ray, I send another ray to the light source positioned at (0,0,0) and return the color according to the Phong illumination model for diffuse reflection, but the result is really not the expected one: http://s15.postimage.org/vc6uyyssr/test.png. The cube is a unit cube centered at (0,0,0) and then translated by (1.5, -1.5, -1.5). From my point of view, the left side of the cube should get more light and it actually does. What do you think of it?
The code below is my attempt at triangulation. It outputs the wrong angles (it read a square's angles as 90, 90. 90, 176) and draws the wrong shapes. What am I doing wrong?
//use earclipping to generate a list of triangles to draw
std::vector<vec> calcTriDraw(std::vector<vec> poly)
{
std::vector<double> polyAngles;
//get angles
for(unsigned int i = 0;i < poly.size();i++)
{
int p1 = i - 1;
int p2 = i;
int p3 = i + 1;
if(p3 > int(poly.size()))
p3 -= poly.size();
if(p1 < 0)
p1 += poly.size();
//get the angle of from 3 points
double dx, dy;
dx = poly[p2].x - poly[p1].x;
dy = poly[p2].y - poly[p1].y;
double a = atan2(dy,dx);
dx = poly[p3].x - poly[p2].x;
dy = poly[p3].y - poly[p2].y;
double b = atan2(dy,dx);
polyAngles.push_back((a-b)*180/PI);
}
std::vector<vec> triList;
for(unsigned int i = 0;i < poly.size() && poly.size() > 2;i++)
{
int p1 = i - 1;
int p2 = i;
int p3 = i + 1;
if(p3 > int(poly.size()))
p3 -= poly.size();
if(p1 < 0)
p1 += poly.size();
if(polyAngles[p2] >= 180)
{
continue;
}
else
{
triList.push_back(poly[p1]);
triList.push_back(poly[p2]);
triList.push_back(poly[p3]);
poly.erase(poly.begin()+p2);
std::vector<vec> add = calcTriDraw(poly);
triList.insert(triList.end(), add.begin(), add.end());
break;
}
}
return triList;
}
Sorry, I don't know why the first few lines aren't getting counted as code.
You don't evaluate angles correctly.
Look at the snippet from your code, and to this picture.
In the picture there are two different situations.
The first one, when the polygon is above the points P1, P2, P3 (then angle = 135 degrees), and the second situation when it is under these points (then angle = 225 degrees), but your code will evaluate the same angle in both situations.
//get the angle of from 3 points
double dx, dy;
dx = poly[p2].x - poly[p1].x;
dy = poly[p2].y - poly[p1].y;
double a = atan2(dy,dx);
dx = poly[p3].x - poly[p2].x;
dy = poly[p3].y - poly[p2].y;
double b = atan2(dy,dx);
polyAngles.push_back((a-b)*180/PI);
alt text http://www.freeimagehosting.net/uploads/28a3a66573.png
You need to reduce p3 if it is >= poly.size(), not just >.
Edit: python code to test
#!/usr/bin/python
import math
p = ((0,0),(0,1),(1,1),(1,0))
for i in xrange(4):
p1 = (i + 3) % 4
p2 = i
p3 = (i + 1) % 4
a = math.atan2(p[p2][1] - p[p1][1], p[p2][0] - p[p1][0])
b = math.atan2(p[p3][1] - p[p2][1], p[p3][0] - p[p2][0])
print (a-b)*180/math.pi
And to run it:
$ ./tmp.py
90.0
90.0
90.0
-270.0