
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
一、📐 光线追踪基础
1.1 几何光学基本原理
光线追踪(Ray Tracing)是一种基于几何光学的渲染技术,通过追踪光线在场景中的传播路径来模拟光与物体的相互作用。与光栅化渲染不同,光线追踪能够自然地处理反射、折射、阴影等光学现象。
光线追踪的核心思想:
- 从摄像机发射光线穿过每个像素
- 计算光线与场景中物体的交点
- 在交点处计算反射和折射光线
- 递归追踪这些次级光线
- 累积光照贡献直到满足终止条件
1.2 斯涅尔定律
斯涅尔定律(Snell's Law)描述了光线在两种不同介质界面上的折射行为:
n 1 sin θ 1 = n 2 sin θ 2 n_1 \sin\theta_1 = n_2 \sin\theta_2 n1sinθ1=n2sinθ2
其中:
- n 1 , n 2 n_1, n_2 n1,n2:两种介质的折射率
- θ 1 \theta_1 θ1:入射角(光线与法线的夹角)
- θ 2 \theta_2 θ2:折射角
折射率的物理意义:
折射率 n n n 定义为光在真空中的速度 c c c 与光在介质中的速度 v v v 的比值:
n = c v n = \frac{c}{v} n=vc
常见介质的折射率:
| 介质 | 折射率 |
|---|---|
| 真空 | 1.0 |
| 空气 | 1.0003 |
| 水 | 1.33 |
| 玻璃 | 1.5-1.9 |
| 钻石 | 2.42 |
1.3 反射定律
反射定律指出,入射光线、法线和反射光线位于同一平面内,且入射角等于反射角:
θ i = θ r \theta_i = \theta_r θi=θr
反射向量计算:
给定入射向量 i ⃗ \vec{i} i 和法向量 n ⃗ \vec{n} n (单位向量),反射向量 r ⃗ \vec{r} r 可通过以下公式计算:
r ⃗ = i ⃗ − 2 ( i ⃗ ⋅ n ⃗ ) n ⃗ \vec{r} = \vec{i} - 2(\vec{i} \cdot \vec{n})\vec{n} r =i −2(i ⋅n )n
1.4 全内反射
当光线从光密介质进入光疏介质时,如果入射角超过临界角,则发生全内反射(Total Internal Reflection)。
临界角计算:
θ c = arcsin ( n 2 n 1 ) \theta_c = \arcsin\left(\frac{n_2}{n_1}\right) θc=arcsin(n1n2)
其中 n 1 > n 2 n_1 > n_2 n1>n2。全内反射是光纤通信和某些光学器件的基础原理。
二、💻 二维光线追踪实现
2.1 基础数据结构
向量类 V2
向量是光线追踪中最基础的数学工具,用于表示位置、方向和法线。
dart
class V2 {
final double x, y;
const V2(this.x, this.y);
double get len => sqrt(x * x + y * y);
V2 get norm => len < 1e-10 ? const V2(0, 0) : V2(x / len, y / len);
V2 operator +(V2 o) => V2(x + o.x, y + o.y);
V2 operator -(V2 o) => V2(x - o.x, y - o.y);
V2 operator -() => V2(-x, -y);
V2 operator *(double s) => V2(x * s, y * s);
double dot(V2 o) => x * o.x + y * o.y;
V2 reflect(V2 n) => this - n * (2 * dot(n));
Offset toOffset() => Offset(x, y);
}
向量运算说明:
| 运算 | 数学表达 | 物理意义 |
|---|---|---|
| 模长 | ∣ v ⃗ ∣ = x 2 + y 2 |\vec{v}| = \sqrt{x^2 + y^2} ∣v ∣=x2+y2 | 向量长度 |
| 归一化 | v ^ = v ⃗ ∣ v ⃗ ∣ \hat{v} = \frac{\vec{v}}{|\vec{v}|} v^=∣v ∣v | 单位方向 |
| 点积 | a ⃗ ⋅ b ⃗ = a x b x + a y b y \vec{a} \cdot \vec{b} = a_x b_x + a_y b_y a ⋅b =axbx+ayby | 角度余弦、投影 |
| 反射 | r ⃗ = i ⃗ − 2 ( i ⃗ ⋅ n ⃗ ) n ⃗ \vec{r} = \vec{i} - 2(\vec{i} \cdot \vec{n})\vec{n} r =i −2(i ⋅n )n | 镜面反射 |
光线类 Ray2D
光线由起点和方向定义:
dart
class Ray2D {
final V2 origin, dir;
const Ray2D(this.origin, this.dir);
V2 at(double t) => origin + dir * t;
}
光线上的任意点可以通过参数 t t t 表示:
P ⃗ ( t ) = O ⃗ + t D ⃗ \vec{P}(t) = \vec{O} + t\vec{D} P (t)=O +tD
其中 O ⃗ \vec{O} O 是原点, D ⃗ \vec{D} D 是方向向量, t ≥ 0 t \geq 0 t≥0。
2.2 几何图元
圆形(圆形透镜/障碍物)
dart
class Circle {
final V2 center;
final double radius;
final double refractiveIndex;
const Circle({
required this.center,
required this.radius,
this.refractiveIndex = 1.5,
});
double? intersect(Ray2D ray) {
final oc = ray.origin - center;
final a = ray.dir.dot(ray.dir);
final b = 2 * oc.dot(ray.dir);
final c = oc.dot(oc) - radius * radius;
final discriminant = b * b - 4 * a * c;
if (discriminant < 0) return null;
final t1 = (-b - sqrt(discriminant)) / (2 * a);
final t2 = (-b + sqrt(discriminant)) / (2 * a);
if (t1 > 1e-6) return t1;
if (t2 > 1e-6) return t2;
return null;
}
V2 getNormal(V2 point) => (point - center).norm;
}
圆线相交推导:
圆的方程: ( x − c x ) 2 + ( y − c y ) 2 = r 2 (x - c_x)^2 + (y - c_y)^2 = r^2 (x−cx)2+(y−cy)2=r2
光线参数方程: P ⃗ ( t ) = O ⃗ + t D ⃗ \vec{P}(t) = \vec{O} + t\vec{D} P (t)=O +tD
代入得:
∥ O ⃗ + t D ⃗ − C ⃗ ∥ 2 = r 2 \|\vec{O} + t\vec{D} - \vec{C}\|^2 = r^2 ∥O +tD −C ∥2=r2
展开后得到关于 t t t 的二次方程:
a t 2 + b t + c = 0 at^2 + bt + c = 0 at2+bt+c=0
其中:
- a = D ⃗ ⋅ D ⃗ = 1 a = \vec{D} \cdot \vec{D} = 1 a=D ⋅D =1(归一化方向)
- b = 2 ( O ⃗ − C ⃗ ) ⋅ D ⃗ b = 2(\vec{O} - \vec{C}) \cdot \vec{D} b=2(O −C )⋅D
- c = ∥ O ⃗ − C ⃗ ∥ 2 − r 2 c = \|\vec{O} - \vec{C}\|^2 - r^2 c=∥O −C ∥2−r2
判别式: Δ = b 2 − 4 a c \Delta = b^2 - 4ac Δ=b2−4ac
当 Δ ≥ 0 \Delta \geq 0 Δ≥0 时有交点,取最小的正 t t t 值。
线段(平面镜)
dart
class LineSegment {
final V2 p1, p2;
const LineSegment(this.p1, this.p2);
double? intersect(Ray2D ray) {
final v1 = ray.origin - p1;
final v2 = p2 - p1;
final v3 = V2(-ray.dir.y, ray.dir.x);
final dot = v2.dot(v3);
if (dot.abs() < 1e-10) return null;
final t1 = v2.cross(v1) / dot;
final t2 = v1.dot(v3) / dot;
if (t1 > 1e-6 && t2 >= 0 && t2 <= 1) return t1;
return null;
}
}
extension on V2 {
double cross(V2 o) => x * o.y - y * o.x;
}
2.3 光线追踪引擎
dart
class RayTracer {
final List<Circle> circles;
final List<LineSegment> mirrors;
final int maxBounces;
RayTracer({
required this.circles,
required this.mirrors,
this.maxBounces = 10,
});
List<V2> trace(Ray2D ray) {
final points = [ray.origin];
var currentRay = ray;
for (int i = 0; i < maxBounces; i++) {
double? closestT = double.infinity;
V2? closestPoint;
V2? closestNormal;
bool hitMirror = false;
// 检查圆的交点
for (final circle in circles) {
final t = circle.intersect(currentRay);
if (t != null && t < closestT) {
closestT = t;
closestPoint = currentRay.at(t);
closestNormal = circle.getNormal(closestPoint!);
hitMirror = false;
}
}
// 检查镜子的交点
for (final mirror in mirrors) {
final t = mirror.intersect(currentRay);
if (t != null && t < closestT) {
closestT = t;
closestPoint = currentRay.at(t);
// 计算线段的法线
final dx = mirror.p2.x - mirror.p1.x;
final dy = mirror.p2.y - mirror.p1.y;
closestNormal = V2(-dy, dx).norm;
hitMirror = true;
}
}
if (closestPoint == null) break;
points.add(closestPoint!);
// 计算反射或折射
if (hitMirror) {
final reflected = currentRay.dir.reflect(closestNormal!);
currentRay = Ray2D(closestPoint, reflected);
} else {
// 折射处理
final refracted = refract(currentRay.dir, closestNormal!, 1.0, 1.5);
if (refracted == null) break; // 全内反射
currentRay = Ray2D(closestPoint, refracted);
}
}
return points;
}
V2? refract(V2 incident, V2 normal, double n1, double n2) {
final n = n1 / n2;
final cosI = -incident.dot(normal);
final sinT2 = n * n * (1 - cosI * cosI);
if (sinT2 > 1) return null; // 全内反射
final cosT = sqrt(1 - sinT2);
return incident * n + normal * (n * cosI - cosT);
}
}
2.4 可视化组件
dart
class RayTracingPainter extends CustomPainter {
final List<V2> path;
final List<Circle> circles;
final List<LineSegment> mirrors;
RayTracingPainter({
required this.path,
required this.circles,
required this.mirrors,
});
@override
void paint(Canvas canvas, Size size) {
// 绘制背景
canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height),
Paint()..color = const Color(0xFF0F172A));
// 绘制圆形
for (final circle in circles) {
canvas.drawCircle(
circle.center.toOffset(),
circle.radius,
Paint()
..color = Colors.blue.withOpacity(0.3)
..style = PaintingStyle.fill,
);
canvas.drawCircle(
circle.center.toOffset(),
circle.radius,
Paint()
..color = Colors.blue
..strokeWidth = 2
..style = PaintingStyle.stroke,
);
}
// 绘制镜子
for (final mirror in mirrors) {
canvas.drawLine(
mirror.p1.toOffset(),
mirror.p2.toOffset(),
Paint()
..color = Colors.cyan
..strokeWidth = 3,
);
}
// 绘制光线路径
if (path.length >= 2) {
final paint = Paint()
..color = Colors.amber
..strokeWidth = 2
..strokeCap = StrokeCap.round;
for (int i = 0; i < path.length - 1; i++) {
canvas.drawLine(
path[i].toOffset(),
path[i + 1].toOffset(),
paint,
);
}
// 绘制交点
for (int i = 1; i < path.length - 1; i++) {
canvas.drawCircle(
path[i].toOffset(),
4,
Paint()
..color = Colors.amber
..style = PaintingStyle.fill,
);
}
}
}
@override
bool shouldRepaint(covariant RayTracingPainter old) => true;
}
三、🎯 完整示例应用
3.1 主界面结构
dart
void main() {
runApp(const RayTracingApp());
}
class RayTracingApp extends StatelessWidget {
const RayTracingApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '光线追踪',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.amber,
brightness: Brightness.dark,
),
useMaterial3: true,
),
home: const RayTracingHomePage(),
debugShowCheckedModeBanner: false,
);
}
}
class RayTracingHomePage extends StatelessWidget {
const RayTracingHomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('💡 光线追踪'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
_buildCard(
context,
title: '2D光线追踪',
description: '基础光线追踪演示',
icon: Icons.lightbulb,
color: Colors.amber,
onTap: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => const RayTracing2DDemo()),
),
),
_buildCard(
context,
title: '镜面反射',
description: '多次反射效果',
icon: Icons.flip,
color: Colors.cyan,
onTap: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => const MirrorDemo()),
),
),
_buildCard(
context,
title: '棱镜折射',
description: '色散与光谱',
icon: Icons.change_history,
color: Colors.purple,
onTap: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => const PrismDemo()),
),
),
_buildCard(
context,
title: '球面透镜',
description: '凸透镜成像',
icon: Icons.circle,
color: Colors.teal,
onTap: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => const LensDemo()),
),
),
],
),
);
}
Widget _buildCard(
BuildContext context, {
required String title,
required String description,
required IconData icon,
required Color color,
required VoidCallback onTap,
}) {
return Card(
margin: const EdgeInsets.only(bottom: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(16),
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Container(
width: 56,
height: 56,
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Icon(icon, color: color, size: 28),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
description,
style: TextStyle(
color: Colors.grey[600],
fontSize: 14,
),
),
],
),
),
Icon(Icons.chevron_right, color: Colors.grey[400]),
],
),
),
),
);
}
}
3.2 2D光线追踪演示
dart
class RayTracing2DDemo extends StatefulWidget {
const RayTracing2DDemo({super.key});
@override
State<RayTracing2DDemo> createState() => _RayTracing2DDemoState();
}
class _RayTracing2DDemoState extends State<RayTracing2DDemo> {
double _angle = 0;
List<V2> _path = [];
late RayTracer _tracer;
@override
void initState() {
super.initState();
_setupScene();
}
void _setupScene() {
_tracer = RayTracer(
circles: [
Circle(center: const V2(200, 200), radius: 80, refractiveIndex: 1.5),
],
mirrors: [
LineSegment(const V2(50, 100), const V2(150, 50)),
LineSegment(const V2(350, 50), const V2(450, 100)),
],
maxBounces: 5,
);
_updatePath();
}
void _updatePath() {
final cx = 200.0;
final cy = 400.0;
final dir = V2(0, -1);
final rotated = V2(
dir.x * cos(_angle) - dir.y * sin(_angle),
dir.x * sin(_angle) + dir.y * cos(_angle),
);
_path = _tracer.trace(Ray2D(V2(cx, cy), rotated.norm));
setState(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('2D光线追踪')),
body: Column(
children: [
Expanded(
child: CustomPaint(
painter: RayTracingPainter(
path: _path,
circles: _tracer.circles,
mirrors: _tracer.mirrors,
),
size: Size.infinite,
),
),
_buildControls(),
],
),
);
}
Widget _buildControls() {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.grey[900],
borderRadius: const BorderRadius.vertical(
top: Radius.circular(20),
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
const Text('入射角: ', style: TextStyle(color: Colors.white70)),
Expanded(
child: Slider(
value: _angle,
min: -pi / 4,
max: pi / 4,
onChanged: (v) {
_angle = v;
_updatePath();
},
),
),
Text('${(_angle * 180 / pi).toStringAsFixed(1)}°',
style: const TextStyle(color: Colors.amber)),
],
),
],
),
);
}
}
3.3 镜面反射演示
dart
class MirrorDemo extends StatefulWidget {
const MirrorDemo({super.key});
@override
State<MirrorDemo> createState() => _MirrorDemoState();
}
class _MirrorDemoState extends State<MirrorDemo> {
double _mirrorAngle = 0;
List<V2> _path = [];
late RayTracer _tracer;
@override
void initState() {
super.initState();
_setupScene();
}
void _setupScene() {
final cx = 200.0;
final cy = 300.0;
final mirrorLen = 150.0;
final mx = cx - mirrorLen / 2;
final my = cy;
_tracer = RayTracer(
circles: [],
mirrors: [
LineSegment(
V2(mx, my),
V2(mx + mirrorLen * cos(_mirrorAngle), my + mirrorLen * sin(_mirrorAngle)),
),
],
maxBounces: 3,
);
_updatePath();
}
void _updatePath() {
final cx = 200.0;
final cy = 100.0;
final dir = V2(0, 1);
_path = _tracer.trace(Ray2D(V2(cx, cy), dir.norm));
setState(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('镜面反射')),
body: Column(
children: [
Expanded(
child: CustomPaint(
painter: RayTracingPainter(
path: _path,
circles: _tracer.circles,
mirrors: _tracer.mirrors,
),
size: Size.infinite,
),
),
_buildControls(),
],
),
);
}
Widget _buildControls() {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.grey[900],
borderRadius: const BorderRadius.vertical(
top: Radius.circular(20),
),
),
child: Row(
children: [
const Text('镜子角度: ', style: TextStyle(color: Colors.white70)),
Expanded(
child: Slider(
value: _mirrorAngle,
min: -pi / 3,
max: pi / 3,
onChanged: (v) {
setState(() {
_mirrorAngle = v;
_setupScene();
});
},
),
),
Text('${(_mirrorAngle * 180 / pi).toStringAsFixed(1)}°',
style: const TextStyle(color: Colors.cyan)),
],
),
);
}
}
3.4 棱镜折射演示
dart
class PrismDemo extends StatefulWidget {
const PrismDemo({super.key});
@override
State<PrismDemo> createState() => _PrismDemoState();
}
class _PrismDemoState extends State<PrismDemo> {
double _prismAngle = 0;
List<V2> _path = [];
late RayTracer _tracer;
@override
void initState() {
super.initState();
_setupScene();
}
void _setupScene() {
final cx = 200.0;
final cy = 300.0;
final prismRadius = 80.0;
_tracer = RayTracer(
circles: [
Circle(
center: V2(cx, cy),
radius: prismRadius,
refractiveIndex: 1.5,
),
],
mirrors: [],
maxBounces: 3,
);
_updatePath();
}
void _updatePath() {
final cx = 200.0;
final cy = 100.0;
final dir = V2(0, 1);
_path = _tracer.trace(Ray2D(V2(cx, cy), dir.norm));
setState(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('棱镜折射')),
body: Column(
children: [
Expanded(
child: CustomPaint(
painter: RayTracingPainter(
path: _path,
circles: _tracer.circles,
mirrors: _tracer.mirrors,
),
size: Size.infinite,
),
),
_buildControls(),
],
),
);
}
Widget _buildControls() {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.grey[900],
borderRadius: const BorderRadius.vertical(
top: Radius.circular(20),
),
),
child: Row(
children: [
const Text('入射位置: ', style: TextStyle(color: Colors.white70)),
Expanded(
child: Slider(
value: _prismAngle,
min: -50,
max: 50,
onChanged: (v) {
_prismAngle = v;
_updatePath();
},
),
),
Text('${_prismAngle.toStringAsFixed(0)}',
style: const TextStyle(color: Colors.purple)),
],
),
);
}
}
3.5 球面透镜演示
dart
class LensDemo extends StatefulWidget {
const LensDemo({super.key});
@override
State<LensDemo> createState() => _LensDemoState();
}
class _LensDemoState extends State<LensDemo> {
double _lensPower = 1.0;
List<V2> _path = [];
late RayTracer _tracer;
@override
void initState() {
super.initState();
_setupScene();
}
void _setupScene() {
final cx = 200.0;
final cy = 300.0;
final lensRadius = 100.0 / _lensPower;
_tracer = RayTracer(
circles: [
Circle(
center: V2(cx, cy),
radius: lensRadius,
refractiveIndex: 1.5,
),
],
mirrors: [],
maxBounces: 2,
);
_updatePath();
}
void _updatePath() {
final cx = 200.0;
final cy = 100.0;
final dir = V2(0, 1);
_path = _tracer.trace(Ray2D(V2(cx, cy), dir.norm));
setState(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('球面透镜')),
body: Column(
children: [
Expanded(
child: CustomPaint(
painter: RayTracingPainter(
path: _path,
circles: _tracer.circles,
mirrors: _tracer.mirrors,
),
size: Size.infinite,
),
),
_buildControls(),
],
),
);
}
Widget _buildControls() {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.grey[900],
borderRadius: const BorderRadius.vertical(
top: Radius.circular(20),
),
),
child: Row(
children: [
const Text('透镜度数: ', style: TextStyle(color: Colors.white70)),
Expanded(
child: Slider(
value: _lensPower,
min: 0.5,
max: 2.0,
onChanged: (v) {
setState(() {
_lensPower = v;
_setupScene();
});
},
),
),
Text(_lensPower.toStringAsFixed(1),
style: const TextStyle(color: Colors.teal)),
],
),
);
}
}
四、🔬 数学原理深入
4.1 折射向量推导
根据斯涅尔定律,我们需要计算折射向量的方向。设入射向量 i ⃗ \vec{i} i 、法向量 n ⃗ \vec{n} n (单位向量),折射率比 r = n 1 / n 2 r = n_1/n_2 r=n1/n2。
推导过程:
-
计算入射角的余弦:
cos θ 1 = − i ⃗ ⋅ n ⃗ \cos\theta_1 = -\vec{i} \cdot \vec{n} cosθ1=−i ⋅n
-
应用斯涅尔定律:
sin θ 2 = n 1 n 2 sin θ 1 \sin\theta_2 = \frac{n_1}{n_2} \sin\theta_1 sinθ2=n2n1sinθ1
-
由于 sin 2 θ + cos 2 θ = 1 \sin^2\theta + \cos^2\theta = 1 sin2θ+cos2θ=1:
sin 2 θ 2 = r 2 ( 1 − cos 2 θ 1 ) \sin^2\theta_2 = r^2 (1 - \cos^2\theta_1) sin2θ2=r2(1−cos2θ1)
-
计算折射角的余弦:
cos θ 2 = 1 − r 2 ( 1 − cos 2 θ 1 ) \cos\theta_2 = \sqrt{1 - r^2 (1 - \cos^2\theta_1)} cosθ2=1−r2(1−cos2θ1)
-
折射向量公式:
t ⃗ = r i ⃗ + ( r cos θ 1 − cos θ 2 ) n ⃗ \vec{t} = r\vec{i} + (r\cos\theta_1 - \cos\theta_2)\vec{n} t =ri +(rcosθ1−cosθ2)n
全内反射条件:
当 r > 1 r > 1 r>1(从光密到光疏)时,如果:
r 2 ( 1 − cos 2 θ 1 ) > 1 r^2 (1 - \cos^2\theta_1) > 1 r2(1−cos2θ1)>1
则发生全内反射,折射光线不存在。
4.2 菲涅尔方程
菲涅尔方程描述了反射和折射光强的分配:
垂直偏振分量(s偏振):
R s = ∣ n 1 cos θ 1 − n 2 cos θ 2 n 1 cos θ 1 + n 2 cos θ 2 ∣ 2 R_s = \left|\frac{n_1\cos\theta_1 - n_2\cos\theta_2}{n_1\cos\theta_1 + n_2\cos\theta_2}\right|^2 Rs= n1cosθ1+n2cosθ2n1cosθ1−n2cosθ2 2
平行偏振分量(p偏振):
R p = ∣ n 1 cos θ 2 − n 2 cos θ 1 n 1 cos θ 2 + n 2 cos θ 1 ∣ 2 R_p = \left|\frac{n_1\cos\theta_2 - n_2\cos\theta_1}{n_1\cos\theta_2 + n_2\cos\theta_1}\right|^2 Rp= n1cosθ2+n2cosθ1n1cosθ2−n2cosθ1 2
平均反射率:
R = R s + R p 2 R = \frac{R_s + R_p}{2} R=2Rs+Rp
透射率: T = 1 − R T = 1 - R T=1−R
4.3 色散现象
折射率与波长的关系导致色散:
n ( λ ) = A + B λ 2 n(\lambda) = A + \frac{B}{\lambda^2} n(λ)=A+λ2B
其中 A A A 和 B B B 是材料常数。
柯西公式(适用于可见光):
n ( λ ) = A + B λ 2 + C λ 4 n(\lambda) = A + \frac{B}{\lambda^2} + \frac{C}{\lambda^4} n(λ)=A+λ2B+λ4C
不同波长的光折射率不同,导致白光通过棱镜后分解为光谱。
五、🚀 高级应用
5.1 光纤模拟
光纤利用全内反射原理传输光信号。临界角:
θ c = arcsin ( n c l a d d i n g n c o r e ) \theta_c = \arcsin\left(\frac{n_{cladding}}{n_{core}}\right) θc=arcsin(ncorencladding)
其中 n c o r e > n c l a d d i n g n_{core} > n_{cladding} ncore>ncladding。
5.2 透镜成像
薄透镜公式:
1 f = ( n − 1 ) ( 1 R 1 − 1 R 2 ) \frac{1}{f} = (n - 1)\left(\frac{1}{R_1} - \frac{1}{R_2}\right) f1=(n−1)(R11−R21)
其中:
- f f f:焦距
- n n n:透镜折射率
- R 1 , R 2 R_1, R_2 R1,R2:透镜表面的曲率半径
成像公式:
1 d o + 1 d i = 1 f \frac{1}{d_o} + \frac{1}{d_i} = \frac{1}{f} do1+di1=f1
5.3 鸿蒙多端适配
dart
class RayTracingConfig {
static int getMaxBounces(BuildContext context) {
final performance = DevicePerformance.getLevel();
switch (performance) {
case PerformanceLevel.high: return 10;
case PerformanceLevel.medium: return 5;
case PerformanceLevel.low: return 3;
}
}
static double getStepSize(BuildContext context) {
final performance = DevicePerformance.getLevel();
switch (performance) {
case PerformanceLevel.high: return 1.0;
case PerformanceLevel.medium: return 2.0;
case PerformanceLevel.low: return 3.0;
}
}
}
六、⚡ 性能优化
6.1 空间加速结构
使用包围体层次结构(BVH)加速相交测试:
| 方法 | 时间复杂度 | 适用场景 |
|---|---|---|
| 暴力搜索 | O(n) | 少量物体 |
| 网格划分 | O(log n) | 均匀分布 |
| BVH | O(log n) | 复杂场景 |
| kd-tree | O(log n) | 动态场景 |
6.2 光线缓存
缓存常用光线的计算结果:
dart
class RayCache {
static final Map<String, List<V2>> _cache = {};
static List<V2> get(Ray2D ray, RayTracer tracer) {
final key = '${ray.origin.x}_${ray.origin.y}_${ray.dir.x}_${ray.dir.y}';
return _cache.putIfAbsent(key, () => tracer.trace(ray));
}
static void clear() => _cache.clear();
}
七、📚 学习资源
推荐阅读
| 主题 | 资源 | 难度 |
|---|---|---|
| 计算机图形学 | 《计算机图形学》Peter Shirley | ⭐⭐⭐ |
| 光学 | 《光学原理》Hecht | ⭐⭐ |
| 光线追踪 | 《Ray Tracing in One Weekend》 | ⭐⭐ |
| 数学基础 | 《线性代数及其应用》 | ⭐ |
相关项目
- Mitsuba Renderer:物理正确渲染器
- PBRT:Physically Based Rendering
- LuxRender:开源渲染引擎
八、📝 总结
本文深入探讨了光线追踪的数学原理及其在 Flutter 中的实现。
核心知识点
| 知识点 | 说明 |
|---|---|
| 📐斯涅尔定律 | 折射现象的基础 |
| 🔄反射定律 | 镜面反射的计算 |
| 💡光线追踪 | 光传播路径模拟 |
| 🌈色散 | 折射率与波长的关系 |
| 🔮全内反射 | 光纤通信基础 |
最佳实践
- ✅ 使用归一化向量进行计算
- ✅ 合理设置最大反射次数
- ✅ 使用空间加速结构
- ✅ 考虑菲涅尔效应
进阶方向
- 🔮 三维光线追踪
- ✨ 路径追踪
- 📊 光子映射
- 🎨 实时渲染
💡 提示:本文代码基于 Flutter for Harmony 开发,可在鸿蒙设备上流畅运行。