教研室接了一个物理实验模拟的项目。我的第一个实验是“测定玻璃的折射率”。由于是刚刚学习unity和C#。实现过程终于到了很多问题,想记下来。这是第一个博客。
我用LineRenerer模拟的光线。
实验过程:
- 射线照射在玻璃砖上;
- 射线与玻璃砖碰撞,发生折射。该折射是光疏介质到光密介质;(暂不考虑反射)
- 折射光线与玻璃砖内侧碰撞,发射折射。该折射是光密介质到光疏介质;(暂不考虑全反射,不考虑和侧面碰撞)
- 光线出射玻璃砖。
如下图:其中矩形为玻璃砖,AB为入射光线,BC为折射光线,CD为出射光线。
其中折射部分,unity没有现有的方法可用,自己写了一个。确定C点着实花了一阵功夫。最后用bc射线的反向延长线确定一条线与玻璃砖碰撞确定C点。
代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Vectrosity;
public class LaserPen : MonoBehaviour {
//游戏对象,这里是线段对象
private GameObject LineRenderGameObject;
//线段渲染器
private LineRenderer lineRenderer;
//设置线段的个数,标示一个曲线由几条线段组成
private int lineCount = 2;
//分别记录4个点,通过这4个三维世界中的点去连接一条线段
private Vector3 v0 = new Vector3();
private Vector3 v1 = new Vector3();
private Vector3 v2 = new Vector3();
private Vector3 v3 = new Vector3();
//射线与激光笔的端点绑定
private GameObject OriginPoint;
private GameObject DirectionPoint;
//用于碰撞检测
Ray ray;
RaycastHit HitPoint;
Ray RefractRay;
RaycastHit HitPoint_chushe;
void Start()
{
lineRenderer= GetComponent<LineRenderer>();
//获取激光笔模型的坐标
OriginPoint = GameObject.Find("origin");
DirectionPoint = GameObject.Find("direcfion");
}
void Update()
{
ray = new Ray(transform.position,-transform.right);
v0 = new Vector3(OriginPoint.transform.position.x, OriginPoint.transform.position.y, OriginPoint.transform.position.z);
v1 = new Vector3(DirectionPoint.transform.position.x, DirectionPoint.transform.position.y, DirectionPoint.transform.position.z);
if (Physics.Raycast(ray,out HitPoint, Mathf.Infinity))
{
Debug.Log("碰撞对象是"+HitPoint.collider.name);
v3 = HitPoint.point;
v2 = Refract((v1-v0),HitPoint.normal,0.6f);//返回值是折线向量
lineRenderer.positionCount = 3;
lineRenderer.SetPosition(0, v0);
lineRenderer.SetPosition(1, v3);
lineRenderer.SetPosition(2, v3+v2.normalized*100);
RefractRay = new Ray(PointQ(HitPoint.point, v2.normalized ,100),-v2*50);//折线的碰撞检测ray
碰撞检测射线RefractRay可视化
//VectorLine.SetLine3D(Color.green,RefractRay.origin, RefractRay.GetPoint(100));
if (Physics.Raycast(RefractRay, out HitPoint_chushe, Mathf.Infinity))
{
Debug.Log("二次碰撞对象是:" + HitPoint_chushe.collider.name);
Debug.Log("出射点的坐标是:" + HitPoint_chushe.ToString());
lineRenderer.positionCount = 4;
lineRenderer.SetPosition(0, v0);
lineRenderer.SetPosition(1, v3);
lineRenderer.SetPosition(2, HitPoint_chushe.point);
lineRenderer.SetPosition(3,HitPoint_chushe.point+ray.direction.normalized*100);
}
else
Debug.Log("sorry,二次碰撞失败");
}
else
{
Debug.Log("没碰上");
lineRenderer.positionCount = 2;
lineRenderer.SetPosition(0, v0 );
lineRenderer.SetPosition(1, (v1-v0)*500 );
}
}
//I为入射光线,N为折射光线,h为透射比,玻璃的h=0.8
public Vector3 Refract(Vector3 I,Vector3 N,float h)
{
I = I.normalized;
N = N.normalized;//单位化
float cosI = Vector3.Dot(I, N);
float cosT2 = 1.0f - h * h * (1.0f - cosI * cosI);
Vector3 T = h * I - (h * cosI + Mathf.Sqrt(cosT2)) * N;
if (cosT2 > 0)
return T;
else
return Vector3.zero;
}
//已知,空间点P(x,y,z)和一个方向向量n(x0,y0,z0).如何求得另一个离P点距离d的点Q(x',y',z')的坐标
public Vector3 PointQ(Vector3 p,Vector3 n,float d)
{
float x, y, z;
x = p.x + d * n.x / n.magnitude;
y = p.y + d * n.y / n.magnitude;
z = p.z + d * n.z / n.magnitude;
Vector3 Q = new Vector3(x,y,z);
return (Q);
}
}
代码写得很烂。
学下的东西真的要记下来,不然过阵子就忘了。