# What is the formula for REFRACTION?

Hello I am making a pixel system, and I have the formula for reflection, not refraction.

Reflection code:

``````local norm = rayc.Normal
local cf = CFrame.lookAt(ray.Origin,rayc.Position)
local currentNormal = cf.LookVector
local reflect = (currentNormal - (2 * currentNormal:Dot(norm) * norm))
``````

What is the formula (in code) for refraction?

2 Likes

The ratio of the angles in and out is the inverse of the ratios of the IORs.

3 Likes

uhh what? Sorry if I sound rude but I dont speak math.
Do you mind explaining the â€śratio of anglesâ€ť and â€śin and out is the revers of the ratios of the â€śâ€ťâ€śIORSâ€ťâ€ť"""??

Refraction occurs whenever the index of refraction changes along the path of some ray of light. The IOR of air is close to 1. The index has been defined so that it is the ratio of the sines of the angles for a ray going from one material with one IOR, (n1) to another material with IOR n2. Rearranging this to work with vectors is hard so give me a minute to figure that out and Iâ€™ll post it.

2 Likes

Any refraction in 3D with the same incoming angle can be rotated so that itâ€™s the same one in 2D, so you can simplify the problem to be in terms of two axes, one of which is the surface normal and the other is perpendicular to that. Lets calls the normal i and the other axis j. You start out knowing i and you can get j by taking the cross between the incoming ray and the normal, then crossing with the normal again. In this form, the direction of the refracted ray is vr = sin(Î¸2)i - cos(Î¸1)j
Since you donâ€™t know one of the Î¸, you have to calculate it using the ratios, so that: n1 / n2 = sin(Î¸2) / sin(Î¸1)

Note that this will be undefined when the incoming angle is zero, but in that case there is also no refraction. It will also be undefined for total internal refraction, which occurs whenever the result of solving the second equation is not real, which can only happen when going from higher to lower n at high angles. You can choose to handle this however you want, but in real life something Completely Ridiculous happens at this point and the idea of light as rays completely breaks down.

1 Like

Okay so, so far I am writing it out, and the first issue is what do you mean-

-?

what do I cross the normal with again?

um and also what is â€śÎ¸â€ť? does that mean angle? or what?

I gotta brb but hereâ€™s this snell

1 Like

When the light ray hits the surface of another material, the angle to the normal at which it continues changes slightly.

When you think of the example azqjanna provided, the normal is that magenta line you see. The â€śnormalâ€ť line is perpendicular to the surface of the second material and the angles on both sides are 90 degrees.

When the light ray hits the surface, you can find Î¸1 (Î¸ is just a variable, think x or y) by finding the angle between the normal and the ray.

The relation between Î¸1 and Î¸2 is `n1*sin(Î¸1) = n2*sin(Î¸2)`. n1 and n2 are the indices of refraction for both materials. These never change as long as the material is the same. What you have is n1,n2, and Î¸2. What you need to find is Î¸2.

If we change the subject of the formula it becomes: `Î¸2 = arcsin((n1*sin(Î¸1))/n2)`.

Now, I gave you the formula you need to find Î¸2. All you need to implement is a function to find Î¸1 depending on how you implement your ray and to use Î¸2 out of it as well.

1 Like

So for â†“

Can I use-

``````local i = rayc.Normal -- raycast normal
local j = base.CFrame.LookVector:Angle(i) -- the base's cframe lookvector is the direction of the raycast
``````

Edit: Iâ€™m not sure how to get angle either
-?

And for â†“

What do I set those as? (n1 and n2)

Edit: I may have done it?? (probably not)

``````local norm = rayc.Normal
local a1 = base.CFrame.LookVector:Angle(norm)
local n1 = 1
local n2 = 1

local a2 =math.asin((n1*math.sin(a1))/n2)

print(a2)
``````

but how do I make a2 a vector3/direction?

``````r = d - 2 * d:Dot(n) * n
``````

Where d is the direction, n is the normalâ€™s unit vector, and r is the result/reflected vector.

This works in 2D and 3D.

Source:

Is this for reflection or refraction?

Edit:

@KUW_Alt1

Iâ€™m not very familiar with the refraction formula. This stack overflow thread has a solution/general formula:

Let V_incedence be the normalized incoming vector. Let `n1` and `n2` be the refracting indices of the two surfaces. You want to calculate V_refraction . Let n be the normalized normal vector.

`V_refraction = r*V_incedence + (rc - sqrt(1-Math.pow(r,2)(1-Math.pow(c,2))))n`

`where r = n1/n2 and c = -n dot V_incedence.`

(You need to pick the refracting indices of the materials. n_1 is basically 1 (1.000293) assuming the first material is air.)

(This is for 3D)

1 Like

uhhhh

So I have V_incedence which is â€śthe normalized incoming vectorâ€ť
but also Let n be the normalized normal vector??

Edit: what is rc and c and r?

n is the unit vector of the surfaceâ€™s normal. Itâ€™s what raycasting returns for the normal.

This is the direction of the incoming light but normalized/unit (which just means you make the length 1. You can do that with `vector/vector.Magnitude`).

1 Like

Okay so, so far I got it without errors, but I just donâ€™t know what r and c is.

Edit: oh nvm i dont know how to read

r is n1/n2

c is (-normal):Dot(V_incedence)

n1/n2 is a property of a material, called the refracting index.

n1 is the refracting index of the first material (what material the light is coming from).

n2 is the refracting index of the second material (what material the light is going into).

Here is a chart of refracting indices:

Source: Refractive index - Wikipedia

Here is the code written in Lua:

``````local function refraction(unitLightDirection, unitNormalDirection, refractionIndex1, refractionIndex2)
local r = refractionIndex1/refractionIndex2
local c = (-unitNormalDirection):Dot(unitLightDirection)
return r*unitLightDirection + (r*c - sqrt(1-math.pow(r,2)(1-math.pow(c,2)))) * unitNormalDirection
end
``````

or

``````local function refraction(V_incedence, n, n1, n2)
local r = n1/n2
local c = (-n):Dot(V_incedence)
return r*V_incedence + (r*c - sqrt(1-math.pow(r,2)(1-math.pow(c,2)))) * n
end
``````
1 Like

Oh okay so I did it and I got this.

``````invalid argument #2 (Vector3 expected, got number)
local c = -n:Dot(V_incedence) -- the error
``````

So you said vector/vector.Magnitude, but magnitude makes the vector3 a number, but :Dot() requires a vector3, so do I just ignore the Magnitude?

Edit: uh oh, the big confusing part has an error.

``````local V_refraction = r*V_incedence + (r*c - math.sqrt(1-math.pow(r,2)(1-math.pow(c,2))))*n
``````

`Workspace.RaycastBase.Script:41: attempt to call a number value`

`vector` is a Vector3

`vector.Magnitude` is a number/scalar

A vector multiplied or divided by a number is a vector still.

``````local c = (-1 * n):Dot(V_incedence)
``````

so that you make sure the -1 gets multiplied by n before the dot product.

`c` will be a number/scalar

You can debug it by breaking the equation into smaller bits.

Try using the code I sent but with type checks:

``````local function refraction(unitLightDirection, unitNormalDirection, refractionIndex1, refractionIndex2)
-- Type checks
assert(typeof(unitLightDirection) == "Vector3", "Should be Vector3 but is a "..typeof(unitLightDirection).."!")
assert(typeof(unitNormalDirection) == "Vector3", "Should be Vector3 but is a "..typeof(unitNormalDirection).."!")
assert(typeof(refractionIndex1) == "number", "Should be number but is a "..typeof(unitLightDirection).."!")
assert(typeof(refractionIndex2) == "number", "Should be number but is a "..typeof(unitLightDirection).."!")

-- Force the vector inputs to be unit vectors
unitLightDirection = unitLightDirection.Unit
unitNormalDirection = unitNormalDirection.Unit

local r = refractionIndex1/refractionIndex2
local c = (-unitNormalDirection):Dot(unitLightDirection)
return r * unitLightDirection + (r * c - math.sqrt(1 - math.pow(r,2) * (1 - math.pow(c,2)))) * unitNormalDirection
end
``````
1 Like

I apologize for keep editing and posting right before you update lmao

Okay so I did the function (idk why `unitNormalDirection` is supposed to be a number) but roblox is acting weird, its saying that `-1, -0, -0` is a â€śâ€ťâ€śnumberâ€ť"" (bruh) (its a vector3 right?) (am I going crazy?)
`Workspace.RaycastBase.Script:11: attempt to index number with 'Dot'`