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.

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.

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.

Hopefully this was helpful. If you need any help with anything, please feel free to ask.

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.

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).

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

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

I would add parenthesis too:

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

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'