In my previous post titled Reflecting laser light in Unity, I talked about reflecting a laser beam through simulated mirrors. I then thought to myself, if I am reflecting light, why not refract it as well and make the game a bit trickier?
The break down of this problem is pretty easy:
- First, we need to shoot the light and check whether it hits an object tagged as lens or not.
- If the laser hits a lens, we refract the light.
I would recommend before proceeding through article that you read the “Reflecting laser light in Unity” as it goes in details through some of the functions that I will go mention in this article.
To detect the type of the object we hit, I had to update the function CheckHit() by adding an additional “else if” statement to check for a collision with a lens. The updated function looks like this:
The first thing we need to do, just like the reflection, is to store the point of impact on the lens on the list of indices of the LineRenderer and from there we need to calculate the refracted direction of the new laser beam. After spending a lot of time learning about physics, I found a law called Snell’s Law that is used to describe the relationship between the angles of incidence and refraction, when referring to light or other waves passing through a boundary between two different isotropic media, such as water, glass, or air. If we assume that when the beam is not touching the lens it is traveling through the air, and when it hits the lens it travels through glass, then we have to use that law to calculate the refracted direction of the light when it passes from the air to the glass. This is Snell’s law:
where n1 is the refractive index of medium 1, n2 is the refractive index of medium 2, l is the direction of the light that is hitting the surface and n is the normal of the surface, theta1 is the angle between the light and the surface and theta2 is the angle between the refracted vector and the surface.
in figure1, you can see that n1 and n2 is set from two constants that were declared and initialized in the class with refractive indices depending on the media type, the vector norm is set to the normal of the hit.point and the incident corresponds to the direction of the laser when it hits the lens. Then we come to the most important function which is Refract() where the refracted vector is calculated through Snell’s law. the function looks like this:
As you can see, the incident is normalized and then we plug in all the inputs into the equation to get a refracted vector. Once the new vector is calculated, we can now use the CastLaser() function with the new refracted vector to draw the new laser beam. But there is a problem, if you notice in figure 1, i am calculating an offset position based on the hit.point. The reason behind this is that the hit.point is located right on the box collider that is used in the object, the problem is that if we use CastLaser() with the new refracted laser direction and the hit.point, the ray casting condition in CastLaser() will return the same object again, because the ray hits the same object first. and thus we will recalculate the same vector, call CastLaser() again and keep going through the recursion until we get a stack overflow exception. To fix that problem, I moved the point in which the laser will be drawn a tiny bit so that it is not on the collider. This will ensure that the refracted light will not collide with the same lens.
This approach totally ignores the fact that the light should refract again when it is moving from the lens to the air, which means it should refract again, but for the purpose of this game, I decided to ignore it for now. Maybe I will make a separate article about it laser. But for now, here is how the refraction looks like: