要求
简要模拟闪电的形成过程,要求插值渲染,用C# WinForm实现(网格)。
思路
逻辑部分
- 网格电场模型是一种图结构,基于C#的特性,设计Node类(结点)和Wire类(连线),且为泛型。
- 设计超类Unit(继承自Node,管理Node的事件)。
- 网格模型的计算需要状态的更替,故设计Status类(状态)和Mutable类(原状态和新状态)。
- 每个电路对象(结点,单元,连线)拥有一个唯一编号,故设计Markable类(基于UUID)。
- 结点和单元可以处理事件(单击、绘图等),增加接口IDraw。
- 单元的绘制过程使用插值接口IInterpolating,边界为双线性插值,中间为更平滑的双立方插值。
- 为介质设计介质计算接口IMedia,用于计算传导电流以及判定介质是否击穿。
界面部分
- 采用DirectUI思想绘制,API采用Gdi+的Graphics,构造图元方法参考自@vczh的C++ GUI Library/GacLib。
- 构造渲染器工厂GraphicsRendererFactory和图元工厂GraphicsElementFactory,渲染器存放绘制句柄(对Gdi+而言不需要存放句柄,且Gdi+对象有缓存机制),图元存放图形的属性。
- 每个元器件拥有独立的IGraphicsElement图元绘制接口集合以绘制图像。
- 插值函数采用双线性和双立方插值,双线性只需4个网格,而双立方需要16个网格,分层渲染(Estimate Test),渲染网格越小,颜色梯度越大,则耗时越多。
- 由于插值算法为CPU密集型,故本身效率不高,有优化空间,参考渐变着色为相对着色(按最值设置区间)。
核心算法
采用状态更新机制,类似状态机,并行优化。
public virtual void Update() { _nodes.Values.AsParallel().ForAll(a => a.Advance(AdvanceType.NodeToWire)); _wires.Values.AsParallel().ForAll(a => a.Advance(AdvanceType.NodeToWire)); _wires.Values.AsParallel().ForAll(a => a.Update()); _wires.Values.AsParallel().ForAll(a => a.Advance(AdvanceType.WireToNode)); _nodes.Values.AsParallel().ForAll(a => a.Advance(AdvanceType.WireToNode)); _nodes.Values.AsParallel().ForAll(a => a.Update()); }
测试
PS:由于仿真算法本身的不足,击穿及传导等各种物理参数很难调整完全,故仿真结果差强人意,帧率太低。由于绘图采用异步的async/await机制,故要求.NET 4.5。
云内电荷迁移:
可以看出,电荷慢慢迁移到云的边界处,导致边界处场强增大,颜色梯度增大。
云的左上角出现一对电荷量相反且电荷非常集中的正反电荷,导致其他区域的颜色梯度接近为中等,呈现绿色。这对集中的正反电荷导致云与空气的接触面被击穿。
云的四周布满电荷,导致云内部被击穿,同时,由于巨大的场强,地上产生感应电流。
电场逐渐增大,地上附近的空气被击穿,产生电流,向上延伸;云附近的空气被击穿,向四周延伸。地下是零电位导体。
至于如何使得感应电流成为一条有鲜明轮廓的折线(闪电轮廓),还有待研究。
源码
https://github.com/bajdcc/SimuCircult
界面:WinSE