# help with refraction problem in my simple ray tracer (images/code inside)

This topic is 5459 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

first of all, yes i did try the search, that is how i have gotten this far. i have a basic ray tracer, and just added reflection and refraction. the reflection works great, but the refraction seems to leave little "ripples" on the surface if i use any color other than clear: http://hometown.aol.com/donate52/images/1.jpg http://hometown.aol.com/donate52/images/2.jpg in the first image, the sphere being reflected is behind the camera. in the second image, the blue sphere is transparent, and the red sphere is behind the blue sphere. you can see teh little "ripples" i'm talking about on there, but i cannot figure out what is causing that (it only happens with refraction). here is some of my code:
//Returns reflect vector
//P = point if intersection, V = ray direction, N = surface normal
Point calcReflect(Point P, Point N, Point V) {
return P - N*2*(Dot(V, N));
}

//returns refract vector
//V = ray direction, N = surface normal, k = refraction value
Point calcTrans(Point V, Point N, double k) {
V = Normalize(V);
double NdV = Dot(N, V);
Point S = V - N*NdV;
if(NdV>0){ return S*k + N*sqrt(1 - k*k*Dot(S, S)); }
else{ return S*(1/k) - N*sqrt(1 - (1/(k*k))*Dot(S, S)); }
}


color trace(Ray R, int step) {
if(step > MAX_STEPS) { return BACKGROUND; }

color& local = color(0, 0, 0);
color& reflect = color(0, 0, 0);
color& trans = color(0, 0, 0);

double t = 100000;
double dist = 0;
int obj = 0;
for(int n=0; n<NUM_OBJ; n++) {
dist = world[n]->intersection(R);
if(dist>0 && dist<t) { t = dist; obj = n; }
}

t = world[obj]->intersection(R);
if(t>0) {
//Pi = Point of intersection
//Pnorm = Surface normal at Pi
Point Pi, Pnorm;
Pi.x = Round(R.getOrigin().x + R.getDirection().x*t, 3);
Pi.y = Round(R.getOrigin().y + R.getDirection().y*t, 3);
Pi.z = Round(R.getOrigin().z + R.getDirection().z*t, 3);
Pnorm = world[obj]->getSurfaceNormal(Pi);

Point Pref = calcReflect(Pi, Pnorm, R.getDirection());
Point Ptra = calcTrans(Pi, Pnorm, 1.666);
Ray Rref = Ray(Pi, Pref-Pi);
Ray Rtra = Ray(Pi, Ptra);

if(world[obj]->getShade() == LAMBERT) { local = lambert(Pi, obj); }
if(world[obj]->getShade() == PHONG) { local = phong(obj); }
if(world[obj]->isReflective()) { reflect = trace(Rref, step+1); }
if(world[obj]->isTransparent()) { trans = trace(Rtra, step+1); }

return (local+reflect+trans);
}
return BACKGROUND;
}


any input/help/ideas at all are welcome, i've been at it for a while now and i have no idea where the problem is. thanks! [Edited by - Turo on October 12, 2004 1:27:34 AM]

##### Share on other sites
Isn't "1/k*k" the same as 1? Perhaps you wanted 1/(k*k). There may be more, that just popped out at a glance... I'm no math guru, so if there are others who can point out other problems, go for it.

##### Share on other sites
Quote:
 Original post by NamethatnobodyelsetookIsn't "1/k*k" the same as 1? Perhaps you wanted 1/(k*k). There may be more, that just popped out at a glance... I'm no math guru, so if there are others who can point out other problems, go for it.

oops, sorry, yes it is supposed to be 1/(k*k), unfortunately that isnt the problem though [depressed]

##### Share on other sites
nice to see that refract formule used :)
Looks like your ray intersects the same surface.

i think you probably need to

Point P1, P2, Pnorm;
P1.x = Round(R.getOrigin().x + R.getDirection().x*(t-smallvalue), 3);
P1.y = Round(R.getOrigin().y + R.getDirection().y*(t-smallvalue), 3);
P1.z = Round(R.getOrigin().z + R.getDirection().z*(t-smallvalue), 3);

P2.x = Round(R.getOrigin().x + R.getDirection().x*(t+smallvalue), 3);
P2.y = Round(R.getOrigin().y + R.getDirection().y*(t+smallvalue), 3);
P2.z = Round(R.getOrigin().z + R.getDirection().z*(t+smallvalue), 3);

and use P1 for casting shadow and reflection rays, and use P2 for casting refracted rays.
edit: readed your code. Instead of that suggestion with smallvalue and P1,P2 , you need to do that:

if(dist>smallvalue && dist<t) { t = dist; obj = n; }
and it will work better, too. Smallvalue it's just value that is very much smaller than any object. If your spheres have radius ~1.0 , it may be 1E-5 or so. Depends if you use doubles(recommended) or floats.

Also you need to check if value under square root( 1 - k*k*Dot(S, S) or 1 - (1/(k*k))*Dot(S, S) ) is >0
, otherwise, you need to do full internal reflection. Maybe that's the problem.(almost sure it is.)

edit3: and what does "Round" do?

[Edited by - Dmytry on October 12, 2004 1:25:51 AM]

##### Share on other sites
dmytry,
you are the man, thank you very much! your idea of using P1 and P2 fixed the problem, looks a LOT better now, thanks! i checked the values of the square roots like you mentioned, but everyhitng was ok. then i changed the dist comparison instead of zero, but the problem was still there. finally, i tried the p1 and p2 idea and it did not work at first (i was using 0.0001 as a small number because i was not sure how small to go), and it did not fix it, but it made it better. the bigger i made that 'small number', the better the image looked. i actually had to go all the way up to 0.1, but its working prefectly now! man, finally works after so many hours of looking for the damn problem, im so happy i could hug you [grin]. by the way, the Round() all that does is round off doubles/floats to a certain number of decimals (i was trying to see if maybe it was a roundoff error), but i removed it because it was not helping.

just one final question (i want to understand why it worked), basically what we are doing with this is moving the intersection point on the sphere a little bit out for the reflection, and a little bt in for the refraction, correct? thanks again!!! [grin]

##### Share on other sites
Quote:
 Original post by Turodmytry,you are the man, thank you very much! your idea of using P1 and P2 fixed the problem, looks a LOT better now, thanks! i checked the values of the square roots like you mentioned, but everyhitng was ok. then i changed the dist comparison instead of zero, but the problem was still there. finally, i tried the p1 and p2 idea and it did not work at first (i was using 0.0001 as a small number because i was not sure how small to go), and it did not fix it, but it made it better. the bigger i made that 'small number', the better the image looked. i actually had to go all the way up to 0.1, but its working prefectly now! man, finally works after so many hours of looking for the damn problem, im so happy i could hug you [grin]. by the way, the Round() all that does is round off doubles/floats to a certain number of decimals (i was trying to see if maybe it was a roundoff error), but i removed it because it was not helping.just one final question (i want to understand why it worked), basically what we are doing with this is moving the intersection point on the sphere a little bit out for the reflection, and a little bt in for the refraction, correct? thanks again!!! [grin]

nice to hear [grin]

i now recommend to do
if(dist>smallvalue && dist<t) { t = dist; obj = n; }
instead of that P1 and P2 thing. It should work better. Yes, it's all is only needed to avoid intersection of ray with surface it just intersected.

and of course round needs to be removed - it will cause probems.

And you need to add checks for signs of values for square root - in case of full internal reflection 'em will be negative.
(and of course if your k>1 , you need to check only k*k branch, and if k<1 you need to check 1/(k*k) branch )

##### Share on other sites
Quote:
 Original post by Dmytrynice to hear [grin]i now recommend to doif(dist>smallvalue && dist1 , you need to check only k*k branch, and if k<1 you need to check 1/(k*k) branch )

ok, i did everything you said, now everthing is working good. thanks again! [cool] [grin]

##### Share on other sites
an offset of .1 seems like an awfull lot (depends on the scale ofcource, but still).

also, its not very failproof: imagine a sharp edge on an object, and the errors that arise there.

the way i dealt with this in my raytracer was to keep track of the medium of the ray at all times, aswell as the origin of the ray, which would be null for primary rays, or a pointer to the primitive it originated from for secondary rays. if a ray hits a transparent surface, you cast one reflected ray which still is in the same medium, and one refracted ray, which will have its medium-bit flipped. then you can take this information into account when youre checking your ray against a primitive. you find the points of intersection with the primitive, and you interpret them based on the knowledge of the medium the ray should be in.

heres an example from my code. not fully commented and it may look bloated because it does more than just find the first point of intersection for CSG purposes, but maybe it helps:

(ray.state indicates if the ray is inside a solid or not)

class Sphere:Primitive{			public void Trace(Ray ray){		//relative distance		ray.pos.x = Void.World.Position.x - ray.pos.x;		ray.pos.y = Void.World.Position.y - ray.pos.y;		ray.pos.z = Void.World.Position.z - ray.pos.z;				//sphere check		float A, B, C, D;		A = (ray.dir.x*ray.dir.x + ray.dir.y*ray.dir.y + ray.dir.z*ray.dir.z);		B = (ray.pos.x*ray.dir.x + ray.pos.y*ray.dir.y + ray.pos.z*ray.dir.z) * 2;		C = (ray.pos.x*ray.pos.x + ray.pos.y*ray.pos.y + ray.pos.z*ray.pos.z) - (Radius*Radius);		D = (B*B) - (4*A*C);						if (D >= 0){			D = sqrt(D);			float min, max;			max = (B + D) / (2 * A);			if (ray.origin === this){				if (ray.state){					if (max < 0) max = 0;					if (ray.infinite){						iv.Set(off, max, true);						}else{						if (max < 1){							iv.Set(off, max, true);						}else{							iv.Set(true);						}					}					}else{					iv.Set(false);				}							}else{				if (ray.infinite){					if (max > 0){								//in front of ray						min = (B - D) / (2 * A);						if (min < 0){							//inside							iv.Set(off, max, true);						}else{									//outside							iv.Set(on, min, off, max, false);						}					}else{						iv.Set(false);					}								}else{					if (max > 0){								//in front of ray						min = (B - D) / (2 * A);						if (min < 0){							//inside							if (max > 1){						//ray completely inside								iv.Set(true);							}else{								//exits								iv.Set(off, max, true);							}						}else{									//outside							if (max > 1){						//doesnt exit								if (min > 1){					//doesnt enter									iv.Set(false);								}else{							//ends inside									iv.Set(on, min, false);								}							}else{								//goes trough								iv.Set(on, min, off, max, false);							}						}					}else{						iv.Set(false);					}				}			}		}else{			iv.Set(false);		}	}

• ### Game Developer Survey

We are looking for qualified game developers to participate in a 10-minute online survey. Qualified participants will be offered a \$15 incentive for your time and insights. Click here to start!

• 12
• 10
• 9
• 15
• 22