Calculation of refraction vector seems to be incorrect...

Started by
12 comments, last by data2 18 years, 8 months ago
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....
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.
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...
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.
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.
Weird. I tried hundreds of sample codes -- all with different but wrong results. What the hell is wrong with my code?!?!?! Or with me??
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?
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);

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 /* ... */}
@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 / 0expected:     0 / -1 / 0refracted:    0 / -1 / 0 => Correct?: Trueincident:     0 / -1 / 0expected:     0 / 1 / 0refracted:    0 / 1 / 0 => Correct?: Trueincident:     0,7071068 / 0,7071068 / 0expected:     -0,7071068 / -0,7071068 / 0refracted:    0,7071068 / -0,7071068 / 0 => Correct?: Falseincident:     0,7071068 / -0,7071068 / 0expected:     -0,7071068 / 0,7071068 / 0refracted:    -0,7071068 / 0,7071068 / 0 => Correct?: Trueincident:     -0,7071068 / 0,7071068 / 0expected:     0,7071068 / -0,7071068 / 0refracted:    -0,7071068 / -0,7071068 / 0 => Correct?: Falseincident:     -0,7071068 / -0,7071068 / 0expected:     0,7071068 / 0,7071068 / 0refracted:    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;        }    }

This topic is closed to new replies.

Advertisement