Files
OpticsSimulation.Unity/Assets/Scenes/Raytracer2D.cs
T

226 lines
5.8 KiB
C#
Raw Normal View History

2025-05-13 01:04:37 +03:00
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Windows;
public class Raytracer2D : MonoBehaviour
{
public PointLight Light;
public OpticalSurface2D[] Surfaces;
// Start is called before the first frame update
void Start()
{
}
bool NearestIntersection(Vector2 rayOrigin, Vector2 rayDir, OpticalSurface2D.Line[] lines, int excludeLineIndex, ref int lineIndex, ref Vector2 intersectionPoint) {
Dictionary<int, Vector2> intersections = new Dictionary<int, Vector2>();
Vector2 hitPoint = new Vector2();
for (int i = 0; i < lines.Length; i++) {
if(i != excludeLineIndex) {
var l = lines[i];
if (RaycastMath.RayLineSegmentIntersection(rayOrigin, rayDir, l.P1, l.P2, ref hitPoint)) {
intersections.Add(i, hitPoint);
}
}
}
if(intersections.Count == 0) {
return false;
}
var result = intersections.OrderBy(x => Vector2.Distance(rayOrigin, x.Value)).First();
lineIndex = result.Key;
intersectionPoint = result.Value;
return true;
}
Vector2 Perpendicular(Vector2 vector) {
return new Vector2(vector.y, -vector.x);
}
Vector2 Reflect(Vector2 vector, Vector2 reflectionVector) {
var l = Vector2.Dot(reflectionVector, vector );
var p = reflectionVector * l;
var p2 = p - vector;
return p + p2;
}
bool Refract(Vector2 incident, Vector2 normal, float n1, float n2, out Vector2 result) {
//Snell's law: n1*sin(theta1) = n2*sin(theta2)
//n1/n2 = sin(theta2) / sin(theta1)
//ior = sin(theta2) / sin(theta1)
//sin(theta2) = ior * sin(theta1)
float cosTheta1 = Vector2.Dot(incident, normal);
if(cosTheta1 < 0) {
cosTheta1 = -cosTheta1;
} else {
normal = -normal;
var tmp = n1;
n1 = n2; n2 = tmp;
}
float ior = n1 / n2;
float sinTheta1Squared = 1.0f - (cosTheta1 * cosTheta1);
float sinTheta2Squared = (ior * ior) * sinTheta1Squared;
float cosTheta2Squared = 1.0f - sinTheta2Squared;
if(cosTheta2Squared < 0.0f) {
result = Vector2.zero;
return false; //total internal reflection
}
float cosTheta2 = Mathf.Sqrt(cosTheta2Squared);
float s = cosTheta1 * ior - cosTheta2;
result.x = ior * incident.x + s * normal.x;
result.y = ior * incident.y + s * normal.y;
return true;
}
public int MaxTraceDepth = 2;
private bool NearestRaycast(Ray2D ray, ref RaycastMath.RaycastResult2D result) {
int id = 0;
foreach (var surface in Surfaces) {
RaycastMath.RaycastResult2D raycast = new RaycastMath.RaycastResult2D();
if (surface.Raycast(ray, ref raycast)) {
if(id == 0) {
result = raycast;
} else {
if(Vector2.Distance(ray.origin, raycast.Point) < Vector2.Distance(ray.origin, result.Point)) {
result = raycast;
}
}
id++;
}
}
return id > 0;
}
private void TraceRay(Ray2D ray, int depth, float n1, float n2, List<Ray2D> result) {
if(depth >= MaxTraceDepth) {
return;
}
RaycastMath.RaycastResult2D cast = new RaycastMath.RaycastResult2D();
if (NearestRaycast(ray, ref cast)) {
// Gizmos.color = Color.blue;
//Gizmos.DrawLine(cast.Point, cast.Point + cast.Normal * 0.5f);
bool fromOutside = Vector2.Dot(cast.Normal, ray.direction) < 0.0f;
Vector2 offset = cast.Normal * 0.0001f;
if (Refract(ray.direction.normalized, cast.Normal, n1, n2, out var refractedRay)) {
if (fromOutside) {
offset = -offset;
}
result.Add(new Ray2D(cast.Point, refractedRay.normalized));
TraceRay(new Ray2D(cast.Point + offset, refractedRay.normalized), depth + 1, n1, n2, result);
} else {
var reflectionVector = Reflect(ray.direction.normalized, Perpendicular(cast.Normal));
//Total internal reflection
result.Add(new Ray2D(cast.Point, reflectionVector));
TraceRay(new Ray2D(cast.Point - offset, reflectionVector), depth + 1, n1, n2, result);
}
}
}
private List<Vector2> Trace() {
List<Vector2> result = new List<Vector2>();
var lightRays = Light.GetRays();
// var lines = Surface.GetTransformedShape();
foreach (var ray in lightRays) {
List<Ray2D> rayIntersections = new List<Ray2D>();
rayIntersections.Add(ray);
TraceRay(ray, 0, 1.0f, 1.5f, rayIntersections);
for(int i = 0; i < rayIntersections.Count - 1; ++i) {
if(i % 2 == 0) {
Gizmos.color = Color.white;
}
else {
Gizmos.color = Color.red;
}
Gizmos.DrawLine(rayIntersections[i].origin, rayIntersections[i + 1].origin);
}
Gizmos.color = Color.yellow;
Gizmos.DrawLine(rayIntersections[rayIntersections.Count-1].origin, rayIntersections[rayIntersections.Count-1].origin + rayIntersections[rayIntersections.Count - 1].direction * 100);
}
return result;
}
private void OnDrawGizmos() {
Trace();
}
// Update is called once per frame
void Update()
{
}
}