226 lines
5.8 KiB
C#
226 lines
5.8 KiB
C#
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()
|
||
|
|
{
|
||
|
|
|
||
|
|
}
|
||
|
|
}
|