65.9K
CodeProject 正在变化。 阅读更多。
Home

关于流体模拟

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2020年11月12日

CPOL

4分钟阅读

viewsIcon

6824

一种新的流体模拟方法

引言

本文探讨了 Jos Stam 的稳定流体,这是当前游戏及其他领域流体模拟的标准,并提出了一种不那么复杂但科学性也较低的方法。

背景

我最初想寻找的是一个搅拌颜料的模拟。我遇到了 Jos Stam 的稳定流体。它的实现看起来确实像是高粘度的搅拌流体。这是一种非常流行的流体动画方法,并且据称在科学上也是站得住脚的,它解决了 Navier-Stokes 方程。唉,我不明白它。最初,它被提出作为游戏快速流体模拟的一种方法,但现在许多科学家都提到了它。第一个代码是关于将烟雾注入液体罐中,该液体是不可压缩的流体。在这里我有三个问题。

  1. 如果我将烟雾注入液体中,它会冒泡上升,而不是像我搅拌两种颜色的颜料那样扩散。
  2. 如果罐子装满了不可压缩的液体,向其中添加体积会使罐子溢出或爆炸。
  3. 对流的实现。对流通常意味着质量、热量、动量的输送。这里的特定形式是后向追踪。

接下来是我对流体流动模拟和后向追踪对流的简单、基本的实验。我对流体力学知之甚少。我的实现没有任何科学价值。它仅仅是为了模拟流体流动。

这是我对 Kelvin-Helmholtz 不稳定性的模拟

Private Sub InitiateHelm()

pi16 = Pi / 48
For tX = 0 To 511
  qy1 = Sin(tX * pi16) * tX * 0.01
  For tY = 0 To 255
   
    Vel(tX, tY, 1) = 1 ''velocity x
    Vel(tX, tY, 2) = qy1 ''velocity y
    For tX2 = 0 To 2
      ImageData(tX2, tX, tY) = 255 ''white top half of the picture
    Next tX2
    Intfield(tX, tY, 1) = 255
  Next tY
  For tY = 256 To 511
   
    Vel(tX, tY, 1) = -1
    Vel(tX, tY, 2) = qy1
    For tX2 = 0 To 2
      ImageData(tX2, tX, tY) = 0 ''black bottom half of the picture
    Next tX2
    Intfield(tX, tY, 1) = 0
  Next tY

Next tX

Private Sub Draw()

Dim a1 As Double, b1 As Double, qx1 As Double, qy1 As Double

For tX = 0 To 510
  For tY = 0 To 510
     qx1 = tX + Vel(tX, tY, 1)
     qy1 = tY + Vel(tX, tY, 2)
     tX1 = Int(qx1)
     tY1 = Int(qy1)

     qx1 = qx1 - tX1
     qy1 = qy1 - tY1
     If tX1 < 0 Then tX1 = 0
     If tX1 > 510 Then tX1 = 510
     If tY1 < 0 Then tY1 = 0
     If tY1 > 510 Then tY1 = 510

     ''a1 = lerp(CDbl(Intfield(tX1, tY1, 1)), 
     ''CDbl(Intfield(tX1 + 1, tY1, 1)), qx1) ''interpolation for smoother picture
     ''b1 = lerp(CDbl(Intfield(tX1, tY1 + 1, 1)), CDbl(Intfield(tX1 + 1, tY1 + 1, 1)), qx1)
     ''Intfield(tX, tY, 2) = lerp(a1, b1, qy1)
 
     Intfield(tX, tY, 2) = Intfield(tX1, tY1, 1) ''backward tracking advection
     ''Intfield(tX1, tY1, 2) = Intfield(tX, tY, 1) ''plain forward
  Next tY
Next tX

For tX = 0 To 511
  For tY = 0 To 511
      Intfield(tX, tY, 1) = (Intfield(tX , tY, 2) 
      For tX2 = 0 to 2
	      ImageData(tX2, tX, tY) = Intfield(tX, tY, 1) 
      Next tx2
  Next tY
Next tX

End Sub

Using the Code

它从一个白色的场上方开始,我分配了速度。顶部的速度与底部的速度相反,这并不奇怪。但我也假设一个周期性的、正弦的速度,该速度垂直(y 方向)于这些 x 方向的速度。这在科学上是不成立的。这只是我在许多真实流体运动的图片和视频中注意到的现象:Rayleigh-Taylor 不稳定性(看起来像是 Kelvin-HelmHoltz 的一种变体),墨水在水中(看起来像是 Rayleigh-Taylor 的一种形式),以及香烟烟柱。其中一些看起来不像稳定流体模拟。它看起来好像也有一个周期性的速度。有点像低频声波,在水平方向上有一个直线前进的速度,伴随着垂直方向上的周期性振幅。这似乎是我可以在我的流体模拟中使用的东西,而无需解释它是如何造成的,只是假设它。

我最初使用此行来为每个像素赋值

Intfield(tX, tY, 2) = Intfield(tX1, tY1, 1) ''backward tracking advection

结果是一个卷曲的波浪。如果我使用直接(字面意义上)的变体

Intfield(tX1, tY1, 2) = Intfield(tX, tY, 1)

我没有得到任何卷曲!图片中的卷曲是由后向对流引起的。

发生的事情是,一个黑色的像素偶尔会利用白色场的速度,反之亦然。

它实际上不是来自那里,但后向计算让它看起来是这样的。后向对流导致了所有那些漂亮的卷曲。(我见过许多实现,其中添加了额外的卷曲或涡度。这确实能做出更好的图片。)

Backward advection makes curls

Forward advection, no curls

正如我所说,我最初想寻找的是搅拌两种颜色的颜料的模拟。这可以通过变形来模拟。这就是 Photoshop 中的液化工具。这是我的实现

Private Sub InitWarp

ReDim cSeg1(-rS To rS, -rS To rS) As Single ''rS is the radius of the warp brush

For tX = 0 To rS
  dax = tX * tX
  For tY = 0 To rS
    dxy = Sqr(dax + tY * tY)
    If dxy <= rS Then
         dr = LineArr(Int(dxy)) ''LineArr stores the warp distances in the brush, 
                                ''larger distance of warp = closer to the middle of the brush
         cSeg1(tX, tY) = dr
         cSeg1(-tX, tY) = dr
         cSeg1(tX, -tY) = dr
         cSeg1(-tX, -tY) = dr
    End If
  Next tY
Next tX

End Sub

Private Sub DoWarp

dx = xM - x ''xM = middle(old mouse position x), x = new mouse position x
dy = yM - y
qX5 = pBox1.Width ''Actual picturebox
qY5 = pBox1.Height

For qY = -rS To rS
    qY1 = qY + yM
    If qY1 > -1 And qY1 < qY5 Then
    For qX = -rS To rS
      qX1 = qX + xM
      If qX1 > -1 And qX1 < qX5 Then
      
      ShiftArr(qX1, qY1, 1) = ShiftArr(qX1, qY1, 1) + cSeg1(qX, qY) * dx ''Shiftarr is a  
            ''buffer that stores the warp distances of the brush, applied to the picture
      ShiftArr(qX1, qY1, 2) = ShiftArr(qX1, qY1, 2) + cSeg1(qX, qY) * dy
      End If
      
   Next qX
   End If
Next qY

For qY = -rS To rS
    qY1 = qY + yM
    If qY1 > -1 And qY1 < qY5 Then

    For qX = -rS To rS
      qX1 = qX + xM
            If qX1 > -1 And qX1 < qX5 Then

      qX3 = qX1 + ShiftArr(qX1, qY1, 1)
      qY3 = qY1 + ShiftArr(qX1, qY1, 2)
      
      If qX3 > -1 And qX3 < qX5 And qY3 > -1 And qY3 < qY5 Then
      For t = 0 To 2
        ImageData(t, qX1, qY1) = ImageData2(t, qX3, qY3) ''Imagedata2 is the picture, 
        ''Imagedata is a copy that will replace Imagedata2 once all changes are drawn
      Next t
      End If
        End If
   Next qX
   End If
Next qY

End Sub

然后,当我观察肥皂泡时,彩色肥皂膜的运动,它看起来像一个整体的变形。我像这样实现了它

Private Sub InitRand(b1 As Integer, b2 As Integer)

Randomize

Dim Randi(0 To 511) As Integer ''random warp distance across the picture

tY1 = 255
  tX1 = CInt(Rnd * 2)
  If tX1 < 1 Then tX1 = -1
  Randi(0) = tX1
 
  For tX = 1 To 511      ''y direction, random decide to add or distract 1 
                         ''from the previous warp distance
     qy1 = Rnd
     If qy1 < 0.167 Then tX1 = Abs(tX1) - 1
     If qy1 > 0.833 Then tX1 = -Abs(tX1) + 1
     If Randi(tX - 1) < -b1 Then tX1 = 1
     If Randi(tX - 1) > b1 Then tX1 = -1
     Randi(tX) = Randi(tX - 1) + tX1
  Next tX
  For tX = 3 To 508 ''smoothen the random values a bit
   Vel(tX, 256, 2) = (Randi(tX) + Randi(tX - 1) + Randi(tX + 1) + _
                      Randi(tX - 2) + Randi(tX + 2) + Randi(tX - 3) + Randi(tX + 3)) / 7
  Next tX
  For tX = 0 To 511
     
     qx1 = Vel(tX, 256, 2) / 256
     For tY = 0 To 255
        Vel(tX, tY, 2) = tY * qx1 ''warp distance at the edges = 0, 
               '' calculate the warp (velocity) distance for all pixels, y direction
     Next tY
        
     qx1 = -qx1
     For tY = 1 To 255
        Vel(tX, tY + 256, 2) = Vel(tX, 256, 2) + tY * qx1
        
     Next tY
     qy1 = Vel(tX, 511, 2)
  Next tX
  
  tY1 = 255
  tX1 = CInt(Rnd * 2)
  If tX1 < 1 Then tX1 = -1
  Randi(0) = tX1
 
  For tX = 1 To 511 '' same for x direction
     qy1 = Rnd
     If qy1 < 0.167 Then tX1 = Abs(tX1) - 1
     If qy1 > 0.833 Then tX1 = -Abs(tX1) + 1
     If Randi(tX - 1) < -b2 Then tX1 = 1
     If Randi(tX - 1) > b2 Then tX1 = -1
     Randi(tX) = Randi(tX - 1) + tX1
  Next tX
  For tX = 3 To 508
   Vel(256, tX, 1) = (Randi(tX) + Randi(tX - 1) + Randi(tX + 1) + _
                      Randi(tX - 2) + Randi(tX + 2) + Randi(tX - 3) + Randi(tX + 3)) / 7
  Next tX
  For tX = 0 To 511
     
     qx1 = Vel(256, tX, 1) / 256
     For tY = 0 To 255
        Vel(tY, tX, 1) = tY * qx1
     Next tY

     qx1 = -qx1
     For tY = 1 To 255
        Vel(tY + 256, tX, 1) = Vel(256, tX, 1) + tY * qx1
        
     Next tY
     
  Next tX
End Sub

绘图程序与上面相同。同样,后向对流产生卷曲和手指。

Initial picture

10 iterations

20 iterations

40 iterations

延伸阅读

结论和关注点

自己决定这种非常简单的算法是否能创建逼真的流体模拟!它使用 VB6 编码,但通过使用 C++ 和 GPU 实现,它可以更快。我希望激励您进行更多实验。至于我自己,接下来我想尝试更好地模拟烟柱和墨水在水中的情况,以及肥皂泡膜。欢迎提出建议!

历史

  • 2020 年 11 月 10 日:初始版本
© . All rights reserved.