*
This commit is contained in:
@@ -0,0 +1,225 @@
|
||||
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()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user