What is the formula for REFRACTION?

WAIT WTF :joy::joy::joy:

Workspace.RaycastBase.Script:11: Should be Vector3 but is a Vector3!
1 Like

-1, -0, -0 is a tuple (because of how lua works it will probably only look at the first value when checking type, so it gets number).

You need to put the tuple in the Vector3 constructor to make it a Vector3:

Vector3.new(-1, -0, -0)
1 Like

Try it again, I copied the line of code but didn’t replace the variable name in one spot.

(unitNormalDirection isn’t a vector)

1 Like

now i get this
Workspace.RaycastBase.Script:21: attempt to call a number value

return r*unitLightDirection + (r*c - math.sqrt(1-math.pow(r,2)(1-math.pow(c,2)))) * unitNormalDirection

Edit: idk whats going on since r and c are both numbers

sorry for my stupidity in math

Edit: OH OHH WAIII HHAHAHH OOOOHHHH HOAHHHAHHA ehehehe okay so that line of code should have a * between the math.pows BUT IT WORKS

1 Like

So one thing though, I’m trying to make it refract like roblox’s glass, but it seems to do this.
Screenshot (929)


(it kinda looks inverted)

1 Like

There are a few reasons this might be.

First, I think this equation could have been typed wrong:

In our code, it’s

math.sqrt( 1 - math.pow(r,2) * (1 - math.pow(c,2)) )

It was originally

math.sqrt( 1 - math.pow(r,2) (1 - math.pow(c,2)) )

I think it possibly should be:

math.sqrt( (1 - math.pow(r,2)) * (1 - math.pow(c,2)) )

(Just something to check)

Second, when you do refraction through substances:

  • You get where the beam of light hits the object and do refraction with n1 = air and n2 = object
  • You get where the beam of light leaves the object (use backwards raycast, I will explain that below) and do refraction for n1 = object and n2 = air
  • Then raycast this final vector to where it hits

Here is a picture of what I mean:

image

(Notice how there are three lines.)

(Raycast the first one until it hits the glass, get the refraction and raycast until it leaves the glass, then finally get the refraction of that and raycast until it hits something else.)

To do a backwards raycast, do a bunch of little raycasts going the opposite direction. The length of a cast is basically proportional to the comp. cost, so its fine to do this. You need to do it backwards so you can raycast from inside the mesh. Once you get a hit remember to flip the normal value.

2 Likes

woah, those 2 parentheses changed the whole thing. I inverted the unitNormalDirection, and it seemed to work, but the new code seems to work better. However, I’m not too sure why I need to do a backwards raycast though.
Here’s it rendered:


Here’s with visual rays: (blue is original and green is refracted)

Edit: Here’s why it should/ what I want it to look like.

2 Likes

The surface you’re trying to find only has one side and it’s going the opposite direction. (If the mesh was double sided you wouldn’t need to backwards raycast.)

0dacb87ef1c6e518cd382dcd81961f3e6c2417d9

See how the surface the green arrows are trying to find is facing the opposite direction? If you did a regular raycast the raycast would go right through the surface since it’s single sided.

The first two yellow points (between red+green and between green+blue) are where the refraction happens. (At the first point n1 = air and n2 = glass, at the second point n1 = glass and n2 = air.)

The green raycast should be backwards because it’s trying to find the outer surface of an object it is inside. Basically your raycast should flip the raycasting direction when it is inside something.

I might be wrong though, maybe regular raycasting can find single sided surfaces going the opposite direction. I didn’t think so though: for example a raycast from inside a character’s head doesn’t return the surface of the head.

Does your code have this feature? Are there 3 raycasts per pixel with refraction or is it only 2?

1 Like

So yes, there are only 2 raycasts with refraction. So would I refract the second raycast? or the third? the third raycast looks to be parallel to the first, so Im confused.

Edit: is the third (blue) ray always parallel to the first (red)?

1 Like

You refract every time the sample changes materials (for example, in the picture above it goes from air > glass then glass > air, so it refracts twice).

The the second object (the grey box in the side) is meant to represent an opaque object. You only stop raycasting when you hit an opaque thing.

In that case it is parallel (it depends on the shape of the object), but it’s also translated, which is still important. In the case of the sphere all the casts are bent inwards.

No, it depends on the entrance and exit normals.

Edit: Here is a picture for a sphere:

image

Edit:

It looks like the original code was more correct, though still missing the second refraction point:

image

(properly working refraction)

image

(notice how this one works the same. If things are further away than the focus they are flipped. Maybe Roblox uses different refraction index values to make the effect more subtle?)

Edit:
Roblox’s refraction appears to be not like real refraction.

I think it’s just an optimization because calculating real refraction is hard with single sided meshes and stuff (not all meshes close and things).

I think they use a super simple formula like this:

local factor = 0.1

local function refraction(unitLightDirection, unitNormalDirection)
    -- Force the vector inputs to be unit vectors
    unitLightDirection = unitLightDirection.Unit
    unitNormalDirection = unitNormalDirection.Unit

    return (unitLightDirection + -1 * factor * unitNormalDirection).Unit
end

actually probably like this:

local factor = 0.75

local function refractionOffset(unitLightDirection, unitNormalDirection, transparency)
    -- Force the vector inputs to be unit vectors
    unitLightDirection = unitLightDirection.Unit
    unitNormalDirection = unitNormalDirection.Unit

    local offset = unitNormalDirection - unitNormalDirection:Dot(unitLightDirection) * unitNormalDirection
    -- Offset is the amount to move the raycast origin by. Once the ray has
    -- gone through a transparent obejct ignore all other transparent objects
    -- until something opaque is hit.
    -- (dont change the raycast direction)
    return offset * (1-transparency) * factor
end

This isn’t real refraction but it seems to be what Roblox uses (my math is probably wrong). I imagine this is for numerous optimization and compatibility reasons.

Screen Shot 2023-02-03 at 5.55.06 PM

1 Like

Hey! Sorry this is such an odd time, but I randomly decided to revive this project, and I just have a quick question - so I still haven’t done the second/exit raycast for the refraction, but is the second raycasts indicie(?) (the n1 or n2 thing) the same as the material (probably glass) or should it be air? To me it looks like the material indicie (idek)

1 Like

n1 is the current material refractive index, and n2 is the material being entered’s refractive index.

To answer your question, when the light exits the glass, n1 is glass and n2 is air.

More detail
1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.