I had to draw some debug art because the rays were shooting wonky directions. In the end it was an issue of local transformations coming into play. No surprise there. I finally got the normals pointing in the correct direction in the above image by manually removing the rotation of the parent. Unforch using to_local()
is translating the normal as well as rotating it and giving me nasty results.
At first, I thought they were going to the origin but that’s not quite right, is it?
I guess I don’t need to use to_local()
. All I want to do is to transform the rotation of the normal from global space to local space. Instead of applying the rotation of the parent, I need to apply the rotation of all ancestors. Instead of normal.rotated(-rotation)
just do normal.rotated(-global_rotation)
and we’re there.
This is looking correct to me:
But this is not:
To help me reason about it, I want to know whether the angle θ should be larger or smaller when the ray enters the block of glass.
A refresher from last time:
From Wikipedia:
n = 1 for a vacuum n = 1.52 for window glass
n₁ ✗ sin θ₁ = n₂ ✗ sin θ₂
I’ll assume a vacuum for everything that’s not a block. So when a ray enters a block I get:
θ₂ = asin( sin(θ₁) / n₂ )
Or really, actually:
θ₂ = asin( sin(θ₁) ✗ n₁ / n₂ )
For vacuum to glass n₁ / n₂ is 1.52 and for glass to vacuum it’s 1/1.52.
Here’s what Desmos has to say:
So for vacuum to glass θ₂ will be bigger than θ₁ and vice versa, vice versa.
That’s definitely not what I was seeing but I fixed my code and we’re getting the correct output now:
Next problem. What’s going on here?
The angle between the first ray and its normal is so big that it casts the internal ray at 90°. After about 55px that ray collides with the same surface and casts another ray straight downwards.
What is the correct behaviour when a ray is hitting the surface at such a shallow angle (relative to the surface)? I learned about Total Internal Reflection… is this related to that?
…
Oh yikes, I’m backwards.
For vacuum to glass n₁ / n₂ is 1.52 and for glass to vacuum it’s 1/1.52.
That’s actually the opposite. Going from vacuum to glass I should be seeing a smaller θ.
I noticed it looking at a couple images on Wikipedia.
That second image shows the issue I’m having with the critical angle.
https://www.youtube.com/watch?v=NAaHPRsveJk&ab_channel=QuantumBoffin
The video shows me a couple things.
- I gotta get myself a laser optics toy set!
- Even when the light refracts, some of it is also reflected.
- The larger the value of θ, the more light that gets reflected rather than refracted.
- At the critical angle all the light actually gets reflected, it doesn’t travel along the surface as Snell’s Law would indicate.
- I should have a semi-circle piece.
A commenter pointed out that this effect can be observed underwater. Looking straight up, you can see through the water because the light hitting your eye enters the water with a low value of θ. However, other directions you’ll see a reflection of the underwater environment.
Here’s a great image from inspiredpencil.com:
That circle where you can see through the surface is called “Snell’s Window.”
So, what does that mean for this little optics game?
I haven’t implemented reflections at all yet so maybe don’t cast a child ray if we’re at or near the critical angle.
When I implement reflections, I’ll have to decide at what point I draw a reflected ray. At the critical angle or before? Should I have some sense of strength of the ray and draw more translucent rays when they’re weaker? Don’t draw them at all when they’re below a certain threshold?
How is this going to affect gameplay?
This information was certainly new to me and reckon it’ll be new to a lot of players. It’s not counter-intuitive or anything so I don’t think I need to justify it. But I’ll definitely have a simple level that can only be solved using TIR. And then after that it just opens up more possibilities so I think it’s a good thing. Maybe it’ll make level design a little trickier.
I love this stuff.