Jump to content
  • Advertisement
Sign in to follow this  
data2

Calculation of refraction vector seems to be incorrect...

This topic is 4848 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

Hi, I try to calculate the refraction in a simple ray-tracer. But actually, the implementation seems to be incorrect. Here is what I've done so far (in C' and DirectX):
public static Vector3 RefractVector(Vector3 vector, Vector3 normal, float muSurfaceFront, float muSurfaceBack)
{
      float nDotV = Vector3.Dot(normal, vector);

      float mu;
      if(nDotV > 0.0f)
      {
        mu = muSurfaceFront / muSurfaceBack;  // from front to back
      }
      else
      {
        mu = muSurfaceBack / muSurfaceFront;  // from back to front
      }

      float cosThetaT = 1 - (mu*mu * (1 - nDotV*nDotV));

      // total internal reflection
      if(cosThetaT < 0.0f)
      {
          return Vector3.Empty;
      }


      Vector3 refractionVector = ((mu * nDotV - (float)Math.Sqrt(cosThetaT)) * normal) - (mu * vector);

      if(nDotV < 0.0f)
      {
         return -refractionVector;
      }
      else
      {
          return refractionVector;
      }
}
IMPORTANT: The vector is assumed to start AT the surface and points towards the light or whatever!! I tested the code with simple vectors and the normal (0,1,0): ( 0, 1, 0) => ( 0,-1, 0) // correct? ( 0,-1, 0) => ( 0, 1, 0) // correct? ( 1, 1, 0) => (-1,-1, 0) // correct? ( 1,-1, 0) => ( 1, 1, 0) // INCORRECT? supposed (-1, 1, 0) (-1, 1, 0) => ( 1,-1, 0) // correct? (-1,-1, 0) => (-1, 1, 0) // INCORRECT? supposed ( 1, 1, 0) ... Can anyone tell me, how this damn refraction is calculated? That would be really great and let me live a few minutes longer....

Share this post


Link to post
Share on other sites
Advertisement
your algo looks correct on first glance, but i dont have my working code handy to compare it to.

in any case there is something wrong with all of your outcomes it seems. first of all the results are too 'clean', values like 0 and 1 are not to be expected.

secondly all dotproducts between your in and output are =< 0, and a < 0 outcome should be impossible, and 0 bordering on impossible.

so on thing is for sure: there is a mayor hole in there somewhere.

try googleing for extra sources, maybe your current example is flawed. otherwise derive/doublecheck it yourself using these observations:

the refracted vector is a weighted sum of the normal and the incoming vector. the two weights should be such as to statisfy two conditions: snells law and outgoing.length == 1, which is a solvable system of equations.

Share this post


Link to post
Share on other sites
Uh, I'm sorry. I forgot to mention that the refraction indices used for testing were both 1. So the refracted vector should be the inverse source vector. Inverse since both pointing away from the surface...

Share this post


Link to post
Share on other sites
i remembered something: that piece of code is derived assuming the ingoing vector is of unit length, which isnt the case in your failed examples. thats probably it, or atleast one problem.

Share this post


Link to post
Share on other sites
Oops, good point! I've totally overlooked that. But the normalized vectors don't change anything. Same results (except 0.707... instead of 1). And as you can see, 2 of my examples are correct even though they have x and y +/- 1.

Share this post


Link to post
Share on other sites
Weird. I tried hundreds of sample codes -- all with different but wrong results. What the hell is wrong with my code?!?!?! Or with me??

Share this post


Link to post
Share on other sites
i dont get it either. the expression for the weight multiplied with the normal simplifies to 0, the one for the input vector to -1, just the way you want them to.

although i dont completely understand how you overloading works, but that could be me: why do you write weight * vector, instead of vector * weight? you cant overload a float to be multiplyable by a vector, can you?

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
I think you have a few signs wrong here:

Vector3 refractionVector = ((mu * nDotV - (float)Math.Sqrt(cosThetaT)) * normal) - (mu * vector);

should be

Vector3 refractionVector = -((mu * nDotV + (float)Math.Sqrt(cosThetaT)) * normal) + (mu * vector);

Share this post


Link to post
Share on other sites
Quote:
Original post by Eelco
although i dont completely understand how you overloading works, but that could be me: why do you write weight * vector, instead of vector * weight? you cant overload a float to be multiplyable by a vector, can you?


Yes; you just make it a non-member function with two arguments. Actually, that's the recommended method of overloading a binary operator, because it encourages using only the public interfaces of the operand types, and as a bonus you get implicit conversions to work on both operands. Some operators have to be declared as member functions, though; foremost operator = (but operator += and its kind don't need to!)


vector operator * (float f, const vector &v)
{
return /* ... */
}

Share this post


Link to post
Share on other sites
@Eelco
It is Managed DirectX code, ask Microsoft why it works. Or ask Sharlin, who already answered it.

Quote:
Vector3 refractionVector = -((mu * nDotV + (float)Math.Sqrt(cosThetaT)) * normal) + (mu * vector);

Well, that doesn't help either. The results are as follows (I extended my test-driver a bit ;-)):

incident:     0 / 1 / 0
expected: 0 / -1 / 0
refracted: 0 / -1 / 0
=> Correct?: True

incident: 0 / -1 / 0
expected: 0 / 1 / 0
refracted: 0 / 1 / 0
=> Correct?: True

incident: 0,7071068 / 0,7071068 / 0
expected: -0,7071068 / -0,7071068 / 0
refracted: 0,7071068 / -0,7071068 / 0
=> Correct?: False

incident: 0,7071068 / -0,7071068 / 0
expected: -0,7071068 / 0,7071068 / 0
refracted: -0,7071068 / 0,7071068 / 0
=> Correct?: True

incident: -0,7071068 / 0,7071068 / 0
expected: 0,7071068 / -0,7071068 / 0
refracted: -0,7071068 / -0,7071068 / 0
=> Correct?: False

incident: -0,7071068 / -0,7071068 / 0
expected: 0,7071068 / 0,7071068 / 0
refracted: 0,7071068 / 0,7071068 / 0
=> Correct?: True



Ok, I think, before I'll freak out, here is my code again. This time, the whole thing. I'm wondering if the method works well and I only feed it with wrong vectors. Or my expected results are incorrect...

    private static void TestRefraction()
{
IList vectors = new ArrayList();
IList expected = new ArrayList();
vectors.Add(Vector3.Normalize(new Vector3( 0, 1, 0))); expected.Add(Vector3.Normalize(new Vector3( 0, -1, 0)));
vectors.Add(Vector3.Normalize(new Vector3( 0, -1, 0))); expected.Add(Vector3.Normalize(new Vector3( 0, 1, 0)));
vectors.Add(Vector3.Normalize(new Vector3( 1, 1, 0))); expected.Add(Vector3.Normalize(new Vector3(-1, -1, 0)));
vectors.Add(Vector3.Normalize(new Vector3( 1, -1, 0))); expected.Add(Vector3.Normalize(new Vector3(-1, 1, 0)));
vectors.Add(Vector3.Normalize(new Vector3(-1, 1, 0))); expected.Add(Vector3.Normalize(new Vector3( 1, -1, 0)));
vectors.Add(Vector3.Normalize(new Vector3(-1, -1, 0))); expected.Add(Vector3.Normalize(new Vector3( 1, 1, 0)));

Vector3 normal = new Vector3(0, 1, 0);

for(int i = 0; i < vectors.Count; i++)
{
Vector3 incident = (Vector3)vectors;
Vector3 exp = (Vector3)expected;
Vector3 refracted = MathUtils.RefractVector(incident, normal, 1,1);
System.Diagnostics.Debug.WriteLine("");
System.Diagnostics.Debug.WriteLine("incident: " + MathUtils.ToString(incident));
System.Diagnostics.Debug.WriteLine("expected: " + MathUtils.ToString(exp));
System.Diagnostics.Debug.WriteLine("refracted: " + MathUtils.ToString(refracted));
System.Diagnostics.Debug.WriteLine(" => Correct?: " + (refracted == exp));
}
}


public static Vector3 RefractVector(Vector3 vector, Vector3 mirrorNormal, float muSurfaceFront, float muSurfaceBack)
{

float nDotV = Vector3.Dot(mirrorNormal, vector);

float mu;
if(nDotV > 0.0f)
{
mu = muSurfaceFront / muSurfaceBack; // from front to back
}
else
{
mu = muSurfaceBack / muSurfaceFront; // from back to front
}

float cosThetaT = 1 - (mu*mu * (1 - nDotV*nDotV));

// total internal reflection
if(cosThetaT < 0.0f)
{
return Vector3.Empty;
}


Vector3 refractionVector = ((mu * nDotV - (float)Math.Sqrt(cosThetaT)) * mirrorNormal) - (mu * vector);
if(nDotV < 0.0f)
{
return -refractionVector;
}
else
{
return refractionVector;
}
}

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.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!