Jump to content
  • Advertisement
Sign in to follow this  
Turo

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.

If you intended to correct an error in the post then please contact us.

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 this post


Link to post
Share on other sites
Advertisement
Quote:
Original post by Namethatnobodyelsetook
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.


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

Share this post


Link to post
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 this post


Link to post
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 this post


Link to post
Share on other sites
Quote:
Original post by Turo
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]

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 this post


Link to post
Share on other sites
Quote:
Original post by Dmytry
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 )


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

Share this post


Link to post
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);
}
}

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!